Mr eel

Injecting Modules into Classes

Inheritance is not always the answer. More often than not, code that needs to be shared between classes can be put into a module. Then you just include it inside you class. Easy as pie. Still in the case where there are multiple classes that you want to mix a module into it can be a bit annoying to have to call include inside each class. So a better way to do this is to inject the module into the class. I have no idea if this classifies as Dependency Injection, but well… that’s what what this example does.

Module.class_eval do
  def inject(method, *klasses)
    klasses.each {|klass| klass.send(method, self)}
  end

  def inject_all(method, options)
    if options[:modules].is_a? Array
      options[:modules].each {|mod| mod.inject(method, *options[:classes])}
    else
      options[:modules].inject(method, options[:modules])
    end
  end
end

We extend the module class using the class_eval method — meta-programming FTW. The first method #inject lets us specify either #include or #extend as the means of mixing the module and a list of classes you want to operate on. So lets imagine I have a module called SuperFunTime.

SuperFunTime.inject(:include, ClassOne, ClassTwo)

SuperFunTime injects itself into the classes using #include. Just a quick note about #include and #extend. These are both private methods for a class, so you can’t call then externally. To get around this we use #send, which lets us call private methods. Naughty! But Nice. The #include method is for mixing in instance methods and #extend is for class methods. There are ways to add both using just #include, but that’s for another time.

Now our second method #inject_all is a little more tricky. It lets us specify a list of modules and a list of classes. So if you have a heap of classes you want to extend with multiple modules, this is a nice shortcut. It’s indented that you call this on the Module class not a module you have specified.

Module.inject_all(:include, :modules => [SuperFunTime, GetBently], :classes => [ClassOne, ClassTwo])

This bit of code will then mix SuperFunTIme and GetBently into the list of classes we pass in. For simple cases, this is overkill, but I’ve definitely found it useful, so please consider my humble bit of code.

Posted on June 28th, 2007 | There are 0 comments

Comments

Add a comment

All contents © 2005—2007 Luke Matthew Sutton