Yehuda Katz is a member of the Ruby on Rails core team, and lead developer of the Merb project. He is a member of the jQuery Core Team, and a core contributor to DataMapper. He contributes to many open source projects, like Rubinius and Johnson, and works on some he created himself, like Thor.
@gmcintire Passenger may be setting GEM_HOME differently -- can you print out ENV["GEM_HOME"] in passenger?
Other ways to wrap a method
January 18th, 2009
This is something of a follow-up to Bruce Williams’ wrap-up of “wrapping a method” approaches. Bruce covered two approaches:
alias_method_chain: rename the original method, then copy the new method over it and call the old one.
class Foo
def bar
1
end
alias_method :original_bar, :bar
def bar
original_bar + 1
end
end
super and extend: override #new and extend the new object’s metaclass with the method.
class Foo
def bar
1
end
module Incrementor
def bar
super + 1
end
end
def self.new
super.extend Incrementor
end
end
I wanted to cover two other options, which come with more serious caveats than Bruce’s approaches, but which have their benefits.
explicitly declaring a method as chainable: if you are in control of the code you want to extend, make it chainable from the beginning
class Foo
chainable = Module.new do
def bar
1
end
end
include chainable
def chainable
super + 1
end
end
What’s going on above is that instead of declaring the method on the class itself, we’re sticking it into an (anonymous) module, then including the module into Foo. As a result, we can now define a method on Foo itself, and super to the module we included. We can even wrap up the idiom pretty simply:
class Module
def overridable(&blk)
mod = Module.new(&blk)
include mod
end
end
class Foo
overridable do
def bar
1
end
end
def bar
super + 1
end
end
I like this approach a lot, but it does require that you have control over the original code. In some cases (for instance, Rails chaining its own code via modules) that is the case. This approach, in my opinion, is superior to alias_method_chain because it does not mutate method names, so it eliminates the confusion of looking for a method from a stack trace on a certain line and finding a method with a different name. Also, having to explicitly declare a method chainable provides a cue to the reader of the code that the method is not complete, and that there might be other components elsewhere.
the best of all worlds (with a caveat): the best option would allow you the benefits of the approach immediately above without requiring that you declare the method chainable (so you could add advice to other peoples’ code). The solution, unfortunately, requires a (rather crude) C extension I whipped up.
There are a few things going on above. The interesting part is the little bit of C code. This code lives on any Module. It grabs a method from the module’s method table, and inserts it (with the same name) on any other Module. The TrackAddedMethods module can be extended onto any module, and it will remember all the methods that were added (using a method added hook).
The override method uses the same technique as the chainable method above to create a new module with the methods inside of it. Only this time, as the methods get added, we track them. Each method that is added to the module is then removed from the class itself and moved up (via the C extension) to a module, which is then included into the base class. At this point, the base class doesn’t have the method on it anymore, but the original method is included into the base class via a module.
At this point, we’re at the same place as we would have been using the first technique I described above, so we simply include the module that was created from the override() method and voila: you can tweak someone else’s methods without needing to rename them.
I hope the forgoing didn’t blow anyone’s minds. It blew mine the first time I started playing with it ;)

Pat Nakajima, Posted January 18, 2009, 3:28 am
The third method you describe is new to me, and was mindblowing in an elegant, Rubyish fashion. One more technique for wrapping methods is to unbind the pristine method, store it someplace, then redefine a method with the same name that runs callbacks, then binds the stored UnboundMethod to the given instance and calls it. Not nearly as elegant, but I believe it leads to some friendlier stacktraces than alias_monster_chain.
teamon, Posted January 18, 2009, 5:16 am
What about jruby?
iain, Posted January 18, 2009, 6:22 am
I do like alias_method_chain. If you know how it works, it is easy to find the methods it uses. If you see a method name =~ /\w_with_\w/ or /\w_without_\w/, you can recognize what has been done. It is very transparent imho. I don’t like specifying your old method to be overridable, for the reason you mentioned. It sounds more natural for me to say to the overriding method that it’s overriding something. But it might be because I’m used to it. What about:
class Foo
def bar
1
end
override :bar do |old_bar|
old_bar 1
end
end
I have of course no idea about it’s implementation. Unfortunately ruby 1.8 doesn’t support blocks this way…
Yehuda Katz, Posted January 18, 2009, 12:11 pm
@teamon this technique can easily work on jruby
@iain the issue I have with amc is that it’s a very leaky abstraction. The goal here is to make an abstraction that is much less leaky so that the implementation doesn’t *matter*. If you go look at the implementation of alias_method you’ll see that it’s quite complex, but you don’t *have* to look at it because it works perfectly 100% of the time.
Ted Han, Posted January 18, 2009, 12:33 pm
Crazy stuff Yehuda!
So, i don’t know the MRI internals well enough to judge whether this is a good idea from an implementational standpoint. I’ve got a comment and a question.
I’ve always wanted more powerful hooks in Ruby. I think it’d be cool to have the ability (before or after) a class or a method is defined or added to an object, to be able to manipulate that method, object or the host/parent in which the object is defined.
This sort of looks like a step in that direction (although not general purpose yeh?).
Further to that, how do you access prior versions of a method?
(Yehuda answered this question for me elsewhere. You’ve still got access to the ancestor chain of methods via ‘super’, so if you wanted any of the intermediate definitions of a method, you can get at ‘em, if you know the order in which they were added).
Antares Trader, Posted January 18, 2009, 8:13 pm
@Ted Han: Have a look at Extlib::Hook for general purpose before and after hooks. As Yahuda pointed out in another post the library lacks conditionals filters, but so does AMC. because hooks work on a per method basis there is really nothing to be done about filters, but I know conditional can be added. The only question in my mind is how expensive they will be.
Alex P, Posted January 20, 2009, 10:26 am
maybe I am missing something, but why do you need to include module inside override
def override(&blk)
mod = Module.new do
extend TrackAddedMethods
end
mod.class_eval(&blk)
__move_to_module(*mod.__added_methods)
include mod
end
it looks like it is already included by __move_to_module… Am I missing something?
Gabriel Horner, Posted January 20, 2009, 2:41 pm
Nice writeup. I think there’s a typo when you first introduced the chainable method. ‘def chainable’ should be ‘def bar’. On a related note, I just released a gem to easily alias methods/classes when in irb: http://github.com/cldwalker/alias/tree/master
Greg Spurrier, Posted January 27, 2009, 2:11 pm
I like the idea of explicitly declaring a method as chainable.
My question, though, is how to you test/spec the wrapped method? With AMC, I usually stub or mock the aliased original method to make sure it is getting invoked as expected from within the wrapper. How do you do that when you are invoking it via super?
Konstantin Haase, Posted March 22, 2009, 8:47 am
I just wanted to let you now, that I wrote a script (in ruby!) that does something like “the best of all worlds ( without a caveat)”:
http://github.com/rkh/chainable