Misc #10178
closedrefinements unactivated within refine block scope?
Description
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.
So:
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.
Updated by nobu (Nobuyoshi Nakada) over 10 years ago
- Description updated (diff)
- Status changed from Open to Feedback
I can't get your point.
Module#refine
requires a block, so your code doesn't work, simply.
Updated by why-capslock-though (Alexander Moore-Niemi) over 10 years ago
Nobuyoshi Nakada wrote:
I can't get your point.
Module#refine
requires a block, so your code doesn't work, simply.
Yes, I mistakenly left out the "do" after refine ActiveSupport::Time
(which should be ActiveSupport::TimeWithZone
) and refine Time
, with it the code does indeed work, and my question still stands.
Updated by why-capslock-though (Alexander Moore-Niemi) over 10 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.
Updated by nobu (Nobuyoshi Nakada) over 10 years ago
- Status changed from Feedback to Closed
In general, the scope inside a method definition is different from outside.
Consider method arguments and class/module level local variables.
Updated by why-capslock-though (Alexander Moore-Niemi) over 10 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.
Updated by why-capslock-though (Alexander Moore-Niemi) over 10 years ago
I had posted some more code but remembered "send" doesn't apply yet! Sorry for my confusion. Any plans on indirect method access?