Charly's Tech Blog

home

Use case: How to Decorate your Plugins

18 Sep 2008

Rails being ruby's killer app, I believe it's vital to illustrate whatever theoretical consideration with real life rails cases (and not just Coffee, spoon_required gibberish)

So take my image bank: class Asset holds the pictures and i gave it a tagging system. I quikly realized i needed to downcase all tags for consistency so I would not end up with both "Winston Churchill" and "winston churchill" as seperate tags.

acts_as_taggable_on, gives you a whole set of methods... some of which are dynamic given its argument (e.g acts_as_taggable_on(:tags, :people) will give you tag_list, and person_list)

Well 2 years ago i would of done something extremely ugly in my controller like:

params[:asset][:tag_list].downcase.... 

And just the thought of it makes me sick and consider zen boudhism as an alternative to eating cats.

So now that my controllers are all skinny, i was looking at a way to downcase the argument of tag_list=(arg) in my model. Lets make a short story shorter: tag_list= and person_list= being defined dynamically, they're just stubs for a more general method: set_tag_list_on(context, new_list, tagger=nil).

Below i've summed up my 4 or 5 attempts in two:

To tell you the truth my second attempt was actually

module TagHack
  def tag_list=(new_tags)
    super(new_tags.downcase)
  end
end

but that didn't work because tag_list is actually dynamically defined directly in the body of the Asset class wheras set_tag_list_on lies in ActiveRecord::Acts::TaggableOn::InstanceMethods so it is higher up in the inheritance chain. And that is true for 95% of all instance methods of any active record plugin.

So as you see, nothing is more easy than "enhancing", "decorating", "altering", "bending" any method

The only constraint is that both the original code and your hack must be in modules 'bracket'. The good news is that a lot of code (like ActiveSupport and so on) is already concieved that way.

VoilĂ .

(Next I'll look at some rails internals and a suggested way to dump alias_method_chain)