New in Rails: Module#alias_method_chain

Posted by marcel April 26, 2006 @ 05:50 PM

Though not an outward facing feature, changeset 4276 introduces a nice method to DRY up and encapsulate the common pattern of aliasing a method so that you can build behavior on top of it.

All over the internals of Rails you’ll find code like this in a module:

  module Layout #:nodoc:
    def self.included(base)
      base.extend(ClassMethods)
      base.class_eval do
        alias_method :render_with_no_layout, :render
        alias_method :render, :render_with_a_layout
        # ... etc
This makes it so that when the module is included into the base class, it adds behavior onto some method in that class without the method having to be aware of the fact that it’s being enhanced. In this case, the render method of ActionController::Base is enhanced to wrap its output in a layout. The new Module#alias_method_chain wraps up this pattern into a single method call. The above example, once refactored to use Module#alias_method_chain, would simply be:

alias_method_chain :render, :layout
This will be used to refactor quite a bit of Rails internals which may not be of immediate relevance to what you do, but it serves as a nice example of the mechanisms Ruby provides for software organization. Small victories.

Posted in Tools, Tricks | 5 comments

Comments

  1. Jon Tirsen on 27 Apr 01:04:

    This is also called “around advice” in aspect oriented terminology. Based on the Common Lisp “around” method combinator which does almost exactly what you are doing above.

    Very nice!

  2. Jerome on 27 Apr 04:18:

    The example shows the original as being: alias_method :render_with_no_layout, :render alias_method :render, :render_with_a_layout

    and this is refactored to: alias_method_chain :render, :layout

    I see how this works partially, but I’m not sure how Ruby knows to add with_no or with_a

  3. Jim Driscoll on 27 Apr 06:15:

    It’ll just be casting to/from symbols:

    irb(main):001:0> :foo.to_s => “foo” irb(main):002:0> “foo_bar”.to_sym => :foo_bar

    Take a look at http://dev.rubyonrails.org/browser/trunk/activesupport/lib/active_support/core_ext/module/aliasing.rb?rev=4276 and you can see that the alias_method_chain method knows the “with”/”without” part.

  4. Amr, on 29 Apr 14:37:

    Looking at the module.rb in the changeset above kinda explained it for me.

    def alias_method_chain(target, feature)
    alias_method "#{target}without#{feature}", target
    alias_method target, "#{target}with#{feature}"
    end
  5. Amr on 29 Apr 14:39:

    oops! the comment systam ate the underscores around “without” and “with” in the alias_method calls above somehow.