Charly's Tech Blog

home

From Decorator Pattern to Before Filter 1

11 Sep 2008

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...

require 'mixology'

module Callback
  def before_filter filter, filtered_method
    Callback.method_factory(filter, filtered_method)
  end

  # Needed defin_method here so it's called in module's context
  def self.method_factory(filter, filtered_method)
    define_method(filtered_method) do
      send filter
      super
    end
  end
end

class Coffee
  def spoon_required
    puts "get a spoon to steer sugar || creme"
  end

  def cost
    puts "3"
  end

  def taste
    puts "African Arabica"
  end
end

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

- 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:

require 'mixology'

module Callback
 def cost
   spoon_required
   super
 end
end

class Coffee
 def spoon_required
   puts "get a spoon to steer sugar || creme"
 end

 def cost
   puts 3
 end
end

coffee = Coffee.new
coffee.mixin Callback

coffee.cost

- 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 :

require 'mixology'

module Callback
 def before_filter filter, filtered_method
   Callback.method_factory(filter, filtered_method)
 end

 # Needed defin_method here so it's called in module's context
 def self.method_factory(filter, filtered_method)
   define_method(filtered_method) do
     send filter
     super
   end
 end
end

class Coffee
 def spoon_required
   puts "get a spoon to steer sugar || creme"
 end

 def cost
   puts "3"
 end

 def taste
   puts "African Arabica"
 end
end

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

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