Friday, May 28, 2010

Tweaking on Rails 3.0: #2 ActiveSupport::Concern

Unless you've been writing your Ruby code in a cave, deep within uninhabited mountains, you have probably seen and written a Ruby module. And it probably went something like this:

Including this into a User model gives us User.awesome, User.make_everyone_awesome, and User#awesome?.

Before getting into ActiveSupport::Concern, you shouldn't be defining the InstanceMethods module and explicitly including it. Remove it now, based on reasons explained by Yehuda Katz. I only provide it in this example for criticism.

In Rails 3, extend your module with ActiveSupport::Concern. This gives you two things:
  • The including class is automatically extended with the ClassMethods module. No more klass.extend(ClassMethods).
  • You get a new method called "included", which provides a terser way to define the idiomatic "self.include(base)" method.
. This is best described by a refactoring of the above example:

As an aside, the Awesomeness module should definitely go into Rails core, but they keep ignoring my requests.

1 comment:

bibwild said...

I believe, if I understand this right, what's REALLY awesome about ActiveSupport::Concern is not that it gives you some sugar for saying the same thing somewhat more clearly.

It's that it's actually more robust. I believe if you have a module that includes another module (that includes another module, etc), and some class include's the top-level module... the class will get the class methods from ALL those modules in the tree. As long as they all include Concern. Without Concern... you would basically have had to write Concern yourself to do this. Not that Concern is THAT complicated, but most people didn't.

Additionally, I believe the logic you put in "included do" will get added to the relevant object whether you add your module on to a class with #include, or onto an individual object with #extend. Without Concern, you would have had to carefully write a buncha code to do that right.

Here's the code, it's short but a bit confusing: