763d99e68bc22bb6bd457feaadd68df0
yasaichi
2015/11/27 21:43:39 投稿
2

Scalaの型パラメータのように書けるモジュールを作って似たような処理をまとめる

互いに継承関係にない複数のクラスに設定用のインターフェイス(configconfigure)を毎回書くのが面倒になったので、Configurableというモジュールを作って処理を共通化した。

Configurableincludeする際に、Scalaの型パラメータのような書き方で設定用のモジュール(Config::Fooなど)を指定できるようにしたのがポイント。

Before

module Config::Foo
  extend Mixlib::Config
end

module Config::Bar
  extend Mixlib::Config
end

class Foo
  class << self
    def config
      Config::Foo
    end

    def configure
      yield config if block_given?
    end
  end
end

class Bar
  class << self
    def config
      Config::Bar
    end

    def configure
      yield config if block_given?
    end
  end
end

Foo.configure { |config| config.foo = 'foo' }
Foo.config.foo #=> "foo"

Bar.configure { |config| config.bar = 'bar' }
Bar.config.bar #=> "bar"

After

module Configurable
  class << self
    def [](config)
      module_cache[config] ||= module_for(config)
    end

    private

    def module_cache
      @module_cache ||= {}
    end

    def module_for(config)
      method_body = lambda do |klass|
        klass.define_singleton_method(:config) { config }
        klass.define_singleton_method(:configure) { |&block| block.call(config) if block }
      end

      Module.new { define_singleton_method(:included, method_body) }
    end
  end
end

module Config::Foo
  extend Mixlib::Config
end

module Config::Bar
  extend Mixlib::Config
end

class Foo
  include Configurable[Config::Foo]
end

class Bar
  include Configurable[Config::Bar]
end

Foo.configure { |config| config.foo = 'foo' }
Foo.config.foo #=> "foo"

Bar.configure { |config| config.bar = 'bar' }
Bar.config.bar #=> "bar"

みんなのコメント