January 20, 2006

Replacing methods

Let's say we want to replace a method instead of just throwing it away. But we want the new method to be able to call the old implementation.

The standard way of doing this is by aliasing the old method and making the new one call the aliased version:


class Foo
  def method
    ...
  end

...

class Foo
  alias :method :old_method

  def method
    ...
    old_method
  end
end



This solution works perfectly for most situations, but what if we want to remove all traces of the old method?

Here's a simple trick to do exactly that:


class Foo
   old_method = instance_method(:method)

   define_method(:method) { |*args|
       # do something here ...
       old_method.bind(self).call(*args)
   }
end


It shouldn't take much to understand how it works, but let's analyze it in more detail. The first line gets a hold of the old implementation of method. instance_method returns an UnboundMethod object that wraps it.

Next, we redefine method and pass it a block with the new implementation. One thing worth mentioning is that the old_method variable inside the block is bound to the variable assigned to outside of the block. The binding is maintained even after the execution reaches the end of the class definition. As a result, the new method has a direct reference to the old one... and nobody else does.

After doing whatever it has to do, the new method calls the old one after binding the UnboundMethod to the current object (i.e., the object on which method was invoked).

If you have a keen eye, you'll notice that there's something special about self. At first glance, it looks like a standard variable that just happens to hold a reference to the current object. If that were true, it would follow that the reference to self inside the block would be bound to the self representing class Foo. That is not the case, though. The binding of self is established when the block gets called.

Labels:

3 Comments:

Blogger J Chris A said...

thanks for the insight... I stopped by to learn about conditional assignment, and left with unbound methods!

March 26, 2006 11:06 PM  
Anonymous Anonymous said...

How do you put old_method back as if you'd never touched it?

June 01, 2007 1:37 AM  
Blogger Daniel Cadenas said...

Tim I tried to do exactly that some months ago, check this: http://dcadenas.blogspot.com/2007/11/bit-of-metaprogramming.html

August 14, 2008 7:47 AM  

Post a Comment

<< Home