This post originated from an RSS feed registered with Ruby Buzz
by Christopher Williams.
Original Post: Patterns in Ruby: Decorator revisited
Feed Title: Late to the Party
Feed URL: http://cwilliams.textdriven.com/xml/rss20/feed.xml
Feed Description: Ruby. Rails. Stuff.
A while back I wrote an article describing some possible ways to implement a
Decorator pattern in Ruby.
I've stumbled across several mentions of
yet another idiom used so often in the Rails codebase that they've extracted
it into the latest ActiveSupport. That idiom is
alias_method_chain,
and it's a good example of a decorator implementation.
This idiom is a codified example of using the alias approach I briefly mentioned in the earlier article. In that
article we aliased the original implementation with anew name, and set up a new implementation of our method
(often delegating to the original) with the original's name. A quick example makes this clear:
class Windowdef draw# do some drawing here...end# some code...# creates a 'copy' of draw method, but gives it# the name/selector 'original_draw'alias:original_draw:drawdef drawdraw_vertical_scrollbaroriginal_drawendend
alias_method_chain
In Rails 1.2 (ActiveSupport specifically), the Rails core team found many instances of this pattern
and codified a new method on the class Module, alias_method_chain. This class-level method encapsulates this
pattern of wrapping existing methods with additional behavior.
Here's a specific example, showing how they would wrap rendering with layouts:
In this small code snippet, they are creating a small chain of methods to wrap the existing render behavior.
Now calls to render will be routed to render_with_a_layout and then on to the original render implementation
(which is now aliased to render_with_no_layout). So they coded up alias_method_chain which simply does
the wrapping for them (using naming conventions):
class Module# Encapsulates the common pattern of:## alias_method :foo_without_feature, :foo# alias_method :foo, :foo_with_feature## With this, you simply do:## alias_method_chain :foo, :feature## And both aliases are set up for you.def alias_method_chain(target,feature)alias_method"#{target}_without_#{feature}",targetalias_methodtarget,"#{target}_with_#{feature}"endend
Please note that if they were to replace their existing two calls to alias_method above, they would need to tweak the naming a little
(the methods would become render_without_layout and render_with_layout as opposed to
render_with_no_layout and render_with_a_layout respectively).