It seems that to_proc
doesn't work on methods defined in refinements:
module ArrayExtensions
refine Array do
def sum
reduce(0, :+)
end
end
end
using ArrayExtensions
puts [[1, 2, 3]].map { |array| array.sum } # => 6
puts [[1, 2, 3]].map(&:sum) # => array.rb:13:in `map': undefined method `sum' for [1, 2, 3]:Array (NoMethodError)
puts [1, 2, 3].method(:sum).to_proc.call # => array.rb:14:in `method': undefined method `sum' for class `Array' (NameError)
Is this the intended behavior? Is there a workaround?
NB The answer below is correct for legacy rubies. In Ruby 2.5+ map(&:sum)
works as expected.
Scoping of refinements is restricted to current context. Since refinements are intended to be not global, as an opposite to monkey patches, any try to call the refined method from outside is prevented. In the code below:
puts [[1, 2, 3]].map { |array| array.sum } # => 6
the scope is fine, we are inside the same scope where this refinement was defined. But here:
puts [[1, 2, 3]].map(&:sum)
the scope is transferred to the context of Symbol
class (!). As stated in the documentation:
When control is transferred outside the scope the refinement is deactivated.
The analogy here would be a private method. Though, while it is exactly as stated in docs, I am unsure whether this behaviour is intended. I believe, an interpreter should take care about such cases. But this question is better to address Matz :)
P.S. Good question!