18 September 2008

Use case: How to Decorate your Plugins

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
  • without touching the original code or endangering other classes using it...
  • without resolving to black magic...
  • and without polluting your code with methods saying without !

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)

16 September 2008

From Decorator Pattern to Before Filter 2

In my previous post From Decorator Pattern to Before Filter 1 we got to the point where we could write this:


coffee = Coffee.new
coffee.mixin Callback
coffee.before_filter :spoon_required, :cost
coffee.cost


Now the thing missing is that we're only filtering one method ('cost' in this case) wheras the before_filter in rails filters all methods by default. So how do we do this?

Quit easily, we just have to list all the instance methods inside the class Coffee (ruby provides this possiblity with "instance_methods(false)").... and... iterate!:



After that adding options like :except=>"smell" is easy. Dealing with inheritance is probably less obvious. The one main differance we have with the rails version of before_filter is the fact that it is set inside the classes body. Not at object creation, and for a good reason since controllers instances are created under the hood.

Of course the easy path is adding: extend(Creme) and before_filter(:spoon_required) in the Coffee's initialize method, but my question was: "is it possible to make it look like this" :

class Coffee
include Creme
before_filter :spoon_required

def spoon_required
puts "get spoon"
end

def cost
puts "3"
end
end


The answer is yes although it's a bit convoluted (it looks like a rail plugin with module InstanceMethod and so on) AND we still have to overide initialize somewhere which makes it dangerous.

Now one word of explanation: the hole elegance of the Decorator Pattern in ruby relies on the fact we're calling super instead of aliasing twice the method we're decorating (or unbinding and binding). Calling super() is possible because when we extend an object with a module (like in coffee.extend Creme) we're actually including the module in the ghostclass (or metaclass or singleton class) which sits right underneath the 'real' class in the inheritance chain.

(TODO add figure)

So wouldn't be nice if we could mixin a module 'underneath' a class by saying so in the body of the class so we could easily do all that aspect oriented stuff without having to worry about it at instanciation time ?

The great Library Facets, gives it a try with prepend which can be used this way :



Comments welcome !

11 September 2008

From Decorator Pattern to Before Filter 1

Looking at my previous post on the Decorator Pattern in ruby, I thought: 'here's an elegant solution to inject behaviour in an object method while respecting encapsulation... but what could be the use of it, a part from unmixing creme out of my coffee'.

- Hummm well in this rails plugin i started writing it could fit in....

- yes but as a more general solution...

- mmmm...Filtering of course !!! Just like the 'before_filter' in rails controller.

- you mean like that :




- as a rough start it gives you the idea. But we would need something more dsl-ish, like our reference in rails controller: 'before_filter :login_required'

- so i guess the next step is to give it a method to call on like spoon_required:



- ok, that was easy but the module is still tied to the class it's decorating. We need to abstract away the method we're filtering ('cost') in the module so it could be any method in the class. So this requires a little bit of ruby's magic :



- I think we've done the hardest part. The rest in episode 2.