refinements unactivated within refine block scope?
I doubt I am seeing a bug, but I was hoping someone could clarify for me the reason why I am seeing what I see. I tried pouring over the spec and wasn't quite able to pin it down.
My use case of refinements is not the normal one, so this is not high priority by any means.
But I am curious why, if I have defined a refinement in, say, module A, and then module B is using A, if B itself as a refine block, A's refinements will not be active within it.
module A refine Time def weekday self.strftime("%A") end end module B using A puts Time.now.weekday # 1 refine ActiveSupport::Time def method_missing(method, *args) puts Time.now.weekday # 2 self.to_time.send(method.to_sym, args.first) end puts Time.now.weekday # 3 end
1 and 3 will be defined, but 2 will not. Is it because according to:
"The scope of a refinement is lexical in the sense that, when control is transferred outside the scope (e.g., by an invocation of a method defined outside the scope, by load/require, etc...), the refinement is deactivated."
refine transfers control outside the scope of the module, so no matter where I put using, it will not have the refinements of A active?
I apologize for my ignorance and greatly appreciate your answers on this matter.
#2 [ruby-core:64599] Updated by Alexander Moore-Niemi about 2 years ago
Nobuyoshi Nakada wrote:
I can't get your point.
Module#refinerequires a block, so your code doesn't work, simply.
Yes, I mistakenly left out the "do" after
refine ActiveSupport::Time (which should be
refine Time, with it the code does indeed work, and my question still stands.
#3 [ruby-core:64601] Updated by Alexander Moore-Niemi about 2 years ago
Here is an executable version of what I was roughing out above, I apologize for not vetting it beforehand to prevent confusion:
require 'active_support/core_ext' module A refine Time do def weekday self.strftime("%A") end end end module B using A puts Time.now.weekday # 1 refine ActiveSupport::TimeWithZone do def method_missing(method, *args) # undefined puts Time.now.weekday # 2 self.to_time.send(method.to_sym, args.first) end end puts Time.now.weekday # 3 end
With #2 in, I will error out for undefined method.
#5 [ruby-core:64611] Updated by Alexander Moore-Niemi about 2 years ago
Nobuyoshi Nakada wrote:
In general, the scope inside a method definition is different from outside.
Consider method arguments and class/module level local variables.
So I was correct, in that
refine invokes a different scope where the refinements aren't activated? Ok, cool.
That's kind of too bad though, because as you see in my example, it means it is harder to reuse a refinement across different object types. (In my production code I actually have to just duplicate code, which is unfortunate.) I imagine there's no plans to change that in the future, right? That plus the indirect method access (when is that going to happen?) could let me do this:
def method_missing(method, *args) if Time.respond_to?(method.to_sym) self.to_time.send(method.to_sym, args.first) end end
Thanks again for your responses.