Misc #10178

refinements unactivated within refine block scope?

Added by Alexander Moore-Niemi 8 months ago. Updated 8 months ago.

[ruby-core:64595]
Status:Closed
Priority:Low
Assignee:-

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.

History

#1 Updated by Nobuyoshi Nakada 8 months 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.

#2 Updated by Alexander Moore-Niemi 8 months 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.

#3 Updated by Alexander Moore-Niemi 8 months 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.

#4 Updated by Nobuyoshi Nakada 8 months 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.

#5 Updated by Alexander Moore-Niemi 8 months 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.

#6 Updated by Alexander Moore-Niemi 8 months ago

I had posted some more code but remembered "send" doesn't apply yet! Sorry for my confusion. Any plans on indirect method access?

Also available in: Atom PDF