有什么方法可以在 Ruby 中创建一个新的空绑定吗?

问题描述 投票:0回答:2

有没有办法创建一个完全空的

Binding
对象以与
eval
一起使用?

根据文档,只有

Kernel#binding
方法可以创建新的绑定。我尝试过这样的事情:

empty = binding

但是,该绑定在其局部变量中包含

empty
本身,以及稍后在代码中分配的相同范围内的任何其他局部变量。

我发现常量

TOPLEVEL_BINDING
是一个空绑定,足以满足我的直接目的。但可能并不总是如此。

有什么办法可以创建一个全新的、完全空的

Binding

ruby
2个回答
4
投票

一个简单的方法是编写一个只调用

binding
的方法:

def empty_binding
  binding
end

然后:

b = empty_binding
b.local_variables
# [ ]

该绑定仍将具有

self
并可以访问该
self
可用的任何实例变量。你可以用一些诡计来限制它:

module Empty
  def self.binding
    super
  end
end

b = Empty.binding
b.eval('puts local_variables.inspect')
# [ ]
b.eval('puts instance_variables.inspect')
# [ ]
b.eval('puts self.inspect')
# Empty

什么有效取决于目标是什么。没有局部变量的绑定非常简单,如果不破解 Ruby 本身,什么都没有的绑定可能是不可能的(尽管

BasicObject
可能比模块更接近于空)。

如果这就是你所追求的,这些东西都不会给你一个安全

eval
的监狱。


0
投票

之前接受的答案的问题在于仍然存在共享上下文:

module Empty
  def self.binding
    super
  end
end

binding = Empty.binding
binding.eval("@foo = 'bar'")
binding.eval("puts instance_variables.inspect")
# => [:@foo]

binding_too = Empty.binding
binding_too.eval("puts instance_variables.inspect")
# => [:@foo]

Ruby 3.0 引入了 Ractor。它类似于 Javascript 的 Web Workers,并提供了一种并行执行 Ruby 代码的方法(每个 ractor 都有自己的 GVL/GIL)。此功能的一个有趣的副作用是,除了传递给它的状态之外,它无法共享任何状态,因此 Ruby 3.0 有效地引入了一个“上下文无关”块,该块仅在您创建 Ractor 时可用。该块不捕获任何外部上下文(与 Ruby 中的所有其他块不同)。

您可以(ab)使用此功能生成绑定并立即返回:

binding = Ractor.new { self.binding }.take
binding.eval("@foo = 'bar'")
binding.eval("puts instance_variables.inspect")
# => [:@foo]

binding_too = Ractor.new { self.binding }.take
binding_too.eval("puts instance_variables.inspect")
# => []

尽管如此,它仍然不是完全隔离,因为您可能需要使用不同的进程。修改环境变量之类的事情仍然会影响绑定之外的事情:

binding = Ractor.new { self.binding }.take
binding.eval("ENV['LOL'] = 'yolo'")
binding.eval("puts ENV['LOL']")
# => "yolo"

binding_too = Ractor.new { self.binding }.take
binding_too.eval("puts ENV['LOL']")
# => "yolo"

puts ENV['LOL']
# => "yolo"
© www.soinside.com 2019 - 2024. All rights reserved.