首页 > 解决方案 > Ruby 绑定 instance_eval 访问本地变量?

问题描述

处理一些我需要能够通过字符串定义方法和本地变量的功能binding

我认为代码最适合自己,所以这就是我想要完成的事情:

mybind = binding

mybind.eval("a = 5")
mybind.eval("a") #=> 5

a #=> NameError: undefined local variable or method `a'...
# Calling it in global/main scope doesn't find anything, but it's found in the `mybind` scope

mybind.eval("def thing; 3; end")
mybind.eval("thing") #=> 3

thing #=> NameError: undefined local variable or method `thing'...
# Calling it in global/main scope doesn't find anything, but it's found in the `mybind` scope

本质上,我不希望定义的任何方法/变量进入全局范围,但我希望它们在对同一个绑定/eval 的第二次调用中是可调用的。这些将作为字符串出现,因此可调用块也是不可能的。

我试过instance_eval, class_eval, eval, 和instance_exec

instance_evalinstance_eval几乎是完美的,除非由于某种原因,在下一次调用中无法访问定义的任何本地变量。

我对使用有点陌生binding,所以如果有明显的我遗漏的东西,请告诉我。否则,如果有人对如何实现这一点有任何想法,即使使用不同的流程,我也会非常感谢任何帮助。

谢谢!

我在 Ruby 2.7.2 上,但如果能提供解决方案,我可以升级。

标签: rubyruby-on-rails-5eval

解决方案


binding,根据它的设计,无论何时调用它都会获取当前绑定。如果您在全局范围内调用它,您将获得全局绑定。听起来您想要的是用户无法访问的绑定。我们可以轻松地创建其中之一,只需binding在一个空函数内部调用即可。

def new_binding
  binding
end

现在,每次我调用 时new_binding,我都会得到一个新的范围,它从 Ruby 的标准全局范围的词法继承而来,但没有该对象,其他任何人都无法访问。

irb(main):012:0> new_binding == new_binding
=> false
irb(main):013:0> my_binding = new_binding
=> #<Binding:0x00005562fd103a00>
irb(main):014:0> my_binding.eval("a = 1")
=> 1
irb(main):015:0> my_binding.eval("a")
=> 1

--

发表评论后编辑

new_binding通过包装一个简单的特征类,我们可以更接近您正在寻找的内容。

def new_binding
  class <<Object.new
    return binding
  end
end

现在您可以定义方法,但您必须使用显式的self.

new_binding.eval("def self.thing; 3; end")
new_binding.eval("thing")

很可能有一些技巧可以method_added绕过显式接收器,但无论出于何种原因,它似乎method_added都不想在特征类上工作。老实说,我可能最终会提出一个关于我自己的新问题,因为这种行为很奇怪。


推荐阅读