class Foo
def self.run(n,code)
foo = self.new(n)
@env = foo.instance_eval{ binding }
@env.eval(code)
end
def initialize(n)
@n = n
end
end
Foo.run( 42, "p @n, defined? foo" )
#=> 42
#=> "local-variable"
The sample program above is intended to evaluate arbitrary code within the scope of a Foo
instance. It does that, but the binding is "polluted" with the local variables from the code
method. I don't want foo
, n
, or code
to be visible to the eval'd code. The desired output is:
#=> 42
#=> nil
How can I create a binding that is (a) in the scope of the object instance, but (b) devoid of any local variables?
The reason that I am creating a binding instead of just using instance_eval(code)
is that in the real usage I need to keep the binding around for later usage, to preserve the local variables created in it.
so like this? or did i miss something important here?
class Foo
attr_reader :b
def initialize(n)
@n = n
@b = binding
end
def self.run(n, code)
foo = self.new(n)
foo.b.eval(code)
end
end
Foo.run(42, "p @n, defined?(foo)")
# 42
# nil
or move it further down to have even less context
class Foo
def initialize(n)
@n = n
end
def b
@b ||= binding
end
def self.run(n, code)
foo = self.new(n)
foo.b.eval(code)
end
end
Foo.run(42, "p @n, defined?(foo), defined?(n)")
# 42
# nil
# nil
Answer:
module BlankBinding
def self.for(object)
@object = object
create
end
def self.create
@object.instance_eval{ binding }
end
end
Description:
In order to get a binding with no local variables, you must call binding
in a scope without any of them. Calling a method resets the local variables, so we need to do that. However, if we do something like this:
def blank_binding_for(obj)
obj.instance_eval{ binding }
end
…the resulting binding will have an obj
local variable. You can hide this fact like so:
def blank_binding_for(_)
_.instance_eval{ binding }.tap{ |b| b.eval("_=nil") }
end
…but this only removes the value of the local variable. (There is no remove_local_variable
method in Ruby currently.) This is sufficient if you are going to use the binding in a place like IRB or ripl where the _
variable is set after every evaluation, and thus will run over your shadow.
However, as shown in the answer at top, there's another way to pass a value to a method, and that's through an instance variable (or class variable, or global variable). Since we are using instance_eval
to shift the self
to our object, any instance variables we create in order to invoke the method will not be available in the binding.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With