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_methodcould catch you off guard: even if you callanother_methodafterusing, the call tomy_methoditself 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 theusingstatement 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 CRuby 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
CC- 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