Create an empty snap in the object area

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 described above is designed to evaluate arbitrary code within an instance of Foo . It does this, but the binding is "polluted" by local variables from the code method. I do not want Foo , n or code display in eval'd code. Desired Result:

 #=> 42 #=> nil 

How to create a binding that is (a) in the region of an instance of an object, but (b) is devoid of any local variables?


The reason I create the binding instead of using instance_eval(code) is that in real use I need to save the binding for later use in order to save the local variables created in it.

+5
source share
2 answers

so like that? 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 
+4
source

Answer

 module BlankBinding def self.for(object) @object = object create end def self.create @object.instance_eval{ binding } end end 

Description

To get binding without local variables, you must call binding in a scope without any of them. A method call resets local variables, so we must do this. However, if we do something like this:

 def blank_binding_for(obj) obj.instance_eval{ binding } end 

... the resulting binding will have a local variable obj . You can hide this fact like this:

 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 right now.) This is enough if you intend to use the binding in a place like IRB or ripl , where the variable _ is set after each evaluation and thus works according to your shadow.

However, as shown in the answer at the top, there is another way to pass the value to the method and through an instance variable (either a class variable or a global variable). Since we use instance_eval to offset self to our object, any instance variables that we create to invoke the method will not be available in the binding.

+1
source

Source: https://habr.com/ru/post/1206464/


All Articles