Extending your Rails classes from plugins

The latest plugin this week is appable_plugins. It’s cool that they are breaking up most of the Rails Engine functionality so that you can use bits and pieces that you need. I’ve started dabbling in Mephisto extensions, so I’ve been thinking about this a bit. However, appable_plugins does some dirty things to some of the Rails internals. Sure, it works with Rails 1.2, but will it work with 2.0?

I got to thinking about a simpler, slightly more explicit way of handling this. Sometimes having too much transparency and magic is a bad thing. So, forget all the load_path mingling, and check this out:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

Module.class_eval do
  def include_into(*klasses)
    klasses.flatten!
    klasses.each do |klass|
      (@@class_mixins[klass] ||= []) << self
      @@class_mixins[klass].uniq!
    end
  end
end

Class.class_eval do
  def inherited_with_mixins(klass)
    returning inherited_without_mixins(klass) do |value|
      mixins = @@class_mixins[klass.name]
      klass.send(:include, *mixins) if mixins
    end
  end

  alias_method_chain :inherited, :mixins
end

This adds a method to modules that let’s you define which classes it should be mixed into. Just define a module like this:

1
2
3
4
5

module FooExtension
  include_into "Foo", "Bar"
  ...
end

When Foo or Bar are created, they will then look for any modules in @@class_mixins and automatically include FooExtension. It’s only 15 lines of evil, and won’t mess with Edge Rails. I’m thinking about making it a little more explicit and leaving out inherited (one of those internal ruby callbacks), and adding a method like include_mixins!.

One thing: This stores the actual module in @@class_mixins. This will probably cause reloading issues in development mode if your plugin modules are being unloaded on each request. I could try storing the module’s name instead.

Comments

Comments are closed.