在范围内包装Ruby全局变量

时间:2012-12-21 06:33:44

标签: ruby scope closures

我正在使用Ruby评估脚本,我希望每个脚本都有自己的沙箱,其中包含一个名为$window的全局变量。 $window变量应该根据脚本运行的沙箱指向不同的东西。线程局部变量可以工作,但我不使用线程。我正在使用Ruby C API,因此可以开启一些可能性。

现在,我在Binding内运行每个脚本,因此它们在那里有点沙盒。绑定可以具有闭合的局部变量,但不能具有全局变量。这是个主意:

sandbox1 = window1.get_binding
sandbox2 = window2.get_binding
sandbox3 = window3.get_binding

sandbox1.eval('$window.foo') # calls 'foo' on window 1
sandbox2.eval('$window.foo') # calls 'foo' on window 2
sandbox3.eval('$window.foo') # calls 'foo' on window 3

有没有办法在Binding内关闭全局变量?我找到了一个可能的解决方案并将其发布在下面。

2 个答案:

答案 0 :(得分:1)

不同的窗户会绑在什么位置?

如果他们被绑定到一个线程,那么解决问题的最简单方法就是拥有一个Thread local variable,如果绑定到其他东西(让我们说当前时间),那么你可能想要使用一个全局哈希。

我个人会去一个专门的类,这将使以后更容易重构(你可能会重构这个,因为全局变量是代码注入差的依赖注释的代码味道):

class Windows < Hash
end
WINDOWS = Windows.new
...
window_scope = WINDOWS[Time.now].binding

然后你有一个可变常量,它实际上是一个全局集合。

答案 1 :(得分:0)

我找到了使用Ruby C API的可能解决方案。在绑定中评估的任何内容都将位于该绑定的调用框架下。使用Ruby 1.8.7,我可以在C:

中完成
public VALUE getCurrentWindow()
{
    // Get the current call frame from Ruby.
    FRAME* frame = ruby_frame;

    // Traverse up the call frame stack until we find a Window class.
    while (frame) {
        if (RTEST(rb_obj_is_kind_of(frame->self, rb_cWindow))
            return frame->self;

        frame = frame->prev;
    }

    // Couldn't find the window.
    return Qnil;
}

它似乎有用,但我们会看到是否遇到问题。