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:
Refinements are lexical in scope. Refinements are only active within a scope after the call to
using
. Any code before theusing
statement will not have the refinement activated.
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 the my_method
refinement. MyClass.new.my_method
is executed.When looking up a method for an instance of
class C
Ruby checks:
- If refinements are active for
C
, in the reverse order they were activated
- The prepended modules from the refinement for
C
- The refinement for
C
- The included modules from the refinement for
C
- The prepended modules of
C
C
- The included modules of
C
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 for another_method
in the class MyClass
and finds it.another_method
, the method my_method
is found and invoked.using
above the line (i.e. physically prior to) where my_method
is invoked. Ruby goes on to look for my_method
in the class MyClass
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:
puts puppy
puppy = 'waggle'
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 isolated file.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
In languages with lexical scope (also called static scope), name resolution depends on the location in the source code and the lexical context, which is defined by where the named variable or function is defined...
Lexical resolution can be determined at compile time, and is also known as early binding, while dynamic resolution can in general only be determined at run time, and thus is known as late binding.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With