In Metaprogramming Ruby 2 in Chapter 2 at the "Refinements" section I found the following piece of Ruby code:
class MyClass
def my_method
"original my_method()"
end
def another_method
my_method
end
end
module MyClassRefinement
refine MyClass do
def my_method
"refined my_method()"
end
end
end
using MyClassRefinement
MyClass.new.my_method # => "refined my_method()"
MyClass.new.another_method # => "original my_method()" - How is this possible?
According to the author:
However, the call to
another_method
could catch you off guard: even if you callanother_method
afterusing
, the call tomy_method
itself happens beforeusing
— so it calls the original, unrefined version of the method.
This totally trips me up.
Why MyClass.new.another_method
prints "original my_method()" since its used after using MyClassRefinement
and what is the author trying to say here?
Could anyone provide a more intuitive/better explanation?
Thanks.
The best explanation I can find is from the docs:
That means your refinement method must be invoked somewhere after the call to
using
. The actual location of the method's invocation is what matters, not how the method was invoked or from where the method was invoked.Here's what happens.
using
i.e.using MyClassRefinement
activates themy_method
refinement.MyClass.new.my_method
is executed.my_method
returns the code from the refinement"refined my_method()"
MyClass.new.another_method
is executed.another_method
is not a refinement, so Ruby looks foranother_method
in the classMyClass
and finds it.another_method
, the methodmy_method
is found and invoked.using
above the line (i.e. physically prior to) wheremy_method
is invoked. Ruby goes on to look formy_method
in the classMyClass
and finds it.my_method
returns the code from the class method"original my_method()"
.We can make a simple comparison. Let's say I have one isolated
file.rb
with the following code:puppy
can't be used before it is defined. The variable is lexically scoped, and its use depends on the location of its definition in the isolatedfile.rb
.Similarly, a refinement cannot be invoked until it has been activated via
using
on a previous line (or somewhere physically previous within the source code file). The refinement is lexically scoped.From Wikipedia
Your specific issue with refinements is discussed in the last section of this article. The author explains as well how the
using
statement's physical location in the file determines whether or not a refinement is active.