I'd like to provide some refinements to a DSL. I'm able to get refinements working with this example:
module ArrayExtras
refine Array do
def speak
puts 'array!'
end
end
end
module MyUniverse
using ArrayExtras
class Thing
def initialize
[1].speak
end
end
end
MyUniverse::Thing.new
This prints out "array!" just fine. But once I introduce instance_eval
, the method can't be found:
module MyUniverse
using ArrayExtras
class DSL
def initialize(&block)
instance_eval(&block)
end
end
end
MyUniverse::DSL.new do
[1].speak
end
I get a undefined method
speak' for [1]:Array (NoMethodError)`
Is there a way to get refinements working within an instance_eval?
Refinements are lexically scoped. You are activating the Refinement in the wrong lexical context. You need to activate it where you are calling the refined method:
module ArrayExtras
refine Array do
def speak
puts 'array!'
end
end
end
module MyUniverse
class DSL
def initialize(&block)
instance_eval(&block)
end
end
end
using ArrayExtras
MyUniverse::DSL.new do
[1].speak
end
# array!
In some cases you can achieve it by using ArrayExtras
on the binding.
module MyUniverse
using ArrayExtras
class DSL
def initialize(&block)
block.binding.eval("using ArrayExtras")
instance_eval(&block)
end
end
end
MyUniverse::DSL.new do
[1].speak
end
This will however only work if you are not using your class in an instance, it only works if the binding
is a module or class context, otherwise the eval
will fail because main.using is permitted only at toplevel
.
If you want to refine the ruby core BaseObject
, you need to modify it as below.
module ArrayExtras
refine ::Array do
def speak
puts 'array!'
end
end
end
It will be found in top level class.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.