The Rails 4.2 announcement had some interesting news about the upcoming Rails 5: It’s probably going to require Ruby 2.2. Which will make it the first Rails version to take advantage of all the good stuff from Ruby 2.

The post mentioned garbage collected symbols and keyword arguments. But to me, one of the most interesting Ruby 2 features is Module#prepend.

The good (and bad) of alias_method_chain

When I first learned Rails, alias_method_chain totally blew my mind. It really showed off how flexible Ruby could be.

With just a single line of code, you could completely change the way a method worked. You no longer needed to hack around libraries to add the code you wanted, you could just add it on the fly. alias_method_chain led to my first patches to gems, which led to my first pull requests, which led to my first open source contributions.

But just like monkey patching, alias_method_chain got overused, and its problems started to become obvious:

The method names it generates are confusing, which makes errors hard to find and debug. For example:

class Person def greeting "Hello" end end module GreetingWithExcitement def self . included ( base ) base . class_eval do alias_method_chain :greeting , :excitement end end def greeting_with_excitement " #{ greeting_without_excitement } !!!!!" end end Person . send ( :include , GreetingWithExcitement )

If you had an error in Person#greeting , the backtrace would tell you that an error actually happened in Person#greeting_without_excitement . But where is that method even defined? I don’t see it anywhere. How do you know which greeting method is the one with the bug in it? And the method names get even more confusing the more you chain.

If you call alias_method_chain twice with the same parameters on the same class, you could cause a stack overflow. (Can you see why?) This doesn’t normally happen, as long as your require statements are consistent about which paths they use. But it’s super annoying if you frequently paste code into a Rails console.

And the rest of the things described by Yehuda Katz’s blog post. This post convinced a lot of Rails devs to start abandoning alias_method_chain in favor of module inheritance.

So, why is it still used?

You can replace most alias_method_chain s by overriding those methods in a module, and including those modules into your child classes. But that only works if you want to override your superclass, not your class itself. That is:

class ParentClass def log puts "In parent" end end class ChildClass < ParentClass def log puts "In child" super end def log_with_extra_message puts "In child, with extra message" log_without_extra_message end alias_method_chain :log , :extra_message end

If you ran ChildClass.new.log , you’d see:

In child, with extra message In child In parent

if you tried to use modules instead of alias_method_chain , you could get the output to be:

In child In child, with extra message In parent

But you cannot match the original output without changing the log method in ChildClass . Ruby inheritance doesn’t work that way. Well, it didn’t.

What changed in Ruby 2.0?

Until Ruby 2.0, there was no way to add code below a class, only above it. But with prepend , you can override a method in a class with a method from a module, and still access the class’s implementation with super . So, using our last example, we could get the original output with:

class ParentClass def log puts "In parent" end end module ExtraMessageLogging def log puts "In child, with extra message" super end end class ChildClass < ParentClass prepend ExtraMessageLogging def log puts "In child" super end end

In child, with extra message In child In parent

Perfect.

If prepend is still hard to wrap your head around, think of it as doing something like this:

class NewChildClass < ChildClass include ExtraMessageLogging end ChildClass = NewChildClass

Except it won’t mess with your class names, and it affects objects that already exist.

(Yes, you can reassign class names in Ruby. No, it’s probably not a great idea.)

What does this mean for Rails?

So, the last excuse for using alias_method_chain is gone in Ruby 2.0. We could take one of the few remaining examples of alias_method_chain in Rails:

rails/activesupport/lib/active_support/core_ext/range/each.rb require 'active_support/core_ext/module/aliasing' class Range #:nodoc: def each_with_time_with_zone ( & block ) ensure_iteration_allowed each_without_time_with_zone ( & block ) end alias_method_chain :each , :time_with_zone def step_with_time_with_zone ( n = 1 , & block ) ensure_iteration_allowed step_without_time_with_zone ( n , & block ) end alias_method_chain :step , :time_with_zone private def ensure_iteration_allowed if first . is_a? ( Time ) raise TypeError , "can't iterate from #{ first . class } " end end end

and replace it with a module, instead:

require 'active_support/core_ext/module/aliasing' module RangeWithTimeWithZoneSupport #:nodoc: def each ( & block ) ensure_iteration_allowed super ( & block ) end def step ( n = 1 , & block ) ensure_iteration_allowed super ( n , & block ) end private def ensure_iteration_allowed if first . is_a? ( Time ) raise TypeError , "can't iterate from #{ first . class } " end end end Range . send ( :prepend , RangeSupportingTimeWithZone )

It’s cleaner, Range#each doesn’t get renamed, and ensure_iteration_allowed isn’t monkey-patched in.

Use inheritance, not patches

Ruby gives you a ton of flexibility, and that’s one of the reasons I love it. But it also has a powerful object model. So when you want to inject your own code, try leaning on modules and inheritance before you hack it in. Your code will be much easier to understand and debug, and you’ll avoid some of the harder-to-detect side effects of something like alias_method_chain .