在对象范围内创建空白绑定

时间:2014-11-08 02:33:28

标签: ruby eval

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"

上面的示例程序旨在评估Foo实例范围内的任意代码。它这样做,但绑定被code方法中的局部变量“污染”。我不希望fooncode对eval'd代码可见。所需的输出是:

#=> 42
#=> nil

如何创建(a)在对象实例范围内的绑定,但(b)没有任何局部变量?


我创建绑定而不是仅仅使用instance_eval(code)的原因是real usage我需要keep the binding around for later usage,以保留在其中创建的局部变量。

2 个答案:

答案 0 :(得分:4)

这样吗?还是我错过了一些重要的事情?

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

或进一步向下移动以获得更少的上下文

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

答案 1 :(得分:2)

<强>答案

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

<强>描述

为了获得没有局部变量的绑定,您必须在没有任何局部变量的范围内调用binding。调用方法会重置局部变量,因此我们需要这样做。但是,如果我们这样做:

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

...生成的绑定将具有obj局部变量。你可以这样隐藏这个事实:

def blank_binding_for(_)
  _.instance_eval{ binding }.tap{ |b| b.eval("_=nil") }
end

...但这只会删除局部变量的值。 (目前Ruby中没有remove_local_variable方法。)如果要在IRB或ripl之类的地方使用绑定,在每次评估后设置_变量,这就足够了。 ,因此会掠过你的影子。

但是,如顶部的答案所示,还有另一种方法可以将值传递给方法,而这是通过实例变量(或类变量或全局变量)传递的。由于我们使用instance_evalself转换为我们的对象,因此我们为调用该方法而创建的任何实例变量都不会在绑定中可用。