Originally blogged by Martin @
split-s. I'm reposting because I don't see this method used very often.
Using Ruby's alias it is possible to override a method and still call the original.
class Radio
def on!
@on = true
end
def on?
@on
end
end
class ClockRadio < Radio
alias :old_on! :on!
def on!
old_on!
@display_time = true
end
def display_time?
@display_time
end
end
While this works, it can cause unexpected results and leave around artifacts. In isolation this doesn't look risky; however, in a large codebase someone could easily define
old_on!
or use it as their alias name also. The other, much smaller, issue is that
old_on!
will be left as a method on ClockRadio when you actually have no desire to expose this method outside of calling it from
on!
.
An alternative is to capture the
on!
method as an unbound method, bind it to the current instance, and call it explicitly.
class ClockRadio < Radio
on = self.instance_method(:on!)
define_method(:on!) do
on.bind(self).call
@display_time = true
end
def display_time?
@display_time
end
end
The above version ensures that the correct version of
on!
will be called from the
on!
implementation defined in ClockRadio. This version also lets the reference to the
old_on!
fall out of scope after the class is defined; therefore, there are no additional methods left around as side effects.
Below are the tests that prove the concept.
class AliasMethodAlternativeTest < Test::Unit::TestCase
def test_original_method_returns_true
radio = Radio.new
radio.on!
assert_equal true, radio.on?
end
def test_aliased_method_returns_true_for_on
radio = ClockRadio.new
radio.on!
assert_equal true, radio.on?
end
def test_aliased_method_returns_true_for_display_time
radio = ClockRadio.new
radio.on!
assert_equal true, radio.display_time?
end
end