How To Replace alias_method_chain
alias_method_chain was deprecated in Rails 5.0 and removed in 5.1, in favor of
Module#prepend. When upgrading a Rails application or gem that uses this
method, a bit of restructuring is required.
Say you have:
class Foo
def bar
"foo#bar"
end
endLater, you need to change this behavior and have decided that
alias_method_chain is the way to do it, perhaps because you read this helpful
blog post by Ernie
Miller, and you
did, indeed, need to override a method on a class which you have no control over.
Or perhaps you’ve inherited a project where you find the following code already
exists, and it still meets a use case where it cannot be refactored to use simple
inheritance.
class Foo
def bar_with_extra_stuff
"#{bar_without_extra_stuff} with extra stuff"
end
alias_method_chain :bar, :extra_stuff
endIn Rails 5.1, we can override Foo#bar with super by using Module#prepend, which
makes the prepended module’s methods act as if they inherit from the class,
instead of stopping the lookup chain at methods defined in the class.
Example:
module BarWithExtraStuff
def bar
"#{super} with extra stuff"
end
end
class Foo
prepend BarWithExtraStuff
def bar
"Foo#bar"
end
endNow we can call Foo#bar and get the results of #bar as called in the
BarWithExtraStuff module.
alias_method_chain would have originally created the method
bar_without_extra_stuff, and the method bar_with_extra_stuff would have been
defined in the code. Module#prepend does not give us access to the old method
- so this is something to keep in mind.
Sometimes code is messy, though, and there is not an immediate solution using
Module#prepend, as was the case in a very large ten-year-old project that my team
and I upgraded from Rails 3.x to 5.1 over the course of the last year or so.
alias_method_chain was used and buried in metaprogramming, and replacing it
with the suggested solution would have required the creation of more
metaprogrammed modules that did not make the code easier to read or maintain.
In (hopefully temporary) cases such as this, it’s useful to remember the origins
of alias_method_chain. It was created to abstract a common pattern used in the
Rails code base itself:
class Foo
def bar
"Foo#bar"
end
def bar_with_extra_stuff
"#{bar_without_extra_stuff} with extra stuff"
end
alias_method :foo_without_extra_stuff, :foo
alias_method :foo, :foo_with_extra_stuff
endThis is still perfectly legal code that can be used in a pinch, and will maintain the original behavior exactly.