Passing options to included modules

Posted in engineering

One of the tasks I’ve faced recently was to allow configuring a module, included into multiple classes, by passing options (simple key => value hash) to it. The traditional approach would be using class or instance variables/accessors or so, but this time I decided to try something different.

Here is the final result:

class ClassA
  include PModule[option1: :value1, option2: :value2]
  ...
end

Pretty clear, isn’t it? No additional lines of code, all the options are passed in module include line.

The implementation

module PModule
  def self.[](options)
    Module.new do
      define_singleton_method(:included) do |base|
        base.class_eval do
          # we have access to options hash here
        end
      end
    end
  end
end

Let’s examine the code.

In PModule we define only self.[] method which takes one argument options and returns another anonymous module. That’s enough, no any other code should be added there. The anonymous module will be included into the target class and there we dynamically define included method which will extend the target with the code in block. In the block passed to base.class_eval we can define anything we need and we have access to options argument there.

Using this approach we can configure what will be included into the target class but we should keep in mind that options won’t be accessible in methods defined in the anonymous module. So any methods that require access to options argument should be defined dynamically by using define_method.