17

假设我有一个带有访问对象的方法的对象:

def foo
   @foo
end

我知道我可以使用 send 来访问该方法:

obj.send("foo")  # Returns @foo

是否有一种简单的方法可以进行递归发送以获取 @foo 对象上的参数,例如:

obj.send("foo.bar")  # Returns @foo.bar
4

3 回答 3

23

您可以使用instance_eval

obj.instance_eval("foo.bar")

您甚至可以直接访问实例变量:

obj.instance_eval("@foo.bar")
于 2013-04-07T13:36:41.757 回答
16

虽然 OP 已经接受了使用 的答案instance_eval(string),但我强烈敦促 OP 避免使用字符串形式,eval除非绝对必要。Eval 调用 ruby​​ 编译器——计算成本高,使用起来很危险,因为它打开了代码注入攻击的向量。

如前所述,根本不需要发送:

obj.foo.bar

如果确实 foo 和 bar 的名称来自一些非静态计算,那么

obj.send(foo_method).send(bar_method)

很简单,所有人都需要这个。

如果方法以点分字符串的形式出现,则可以使用 split 和 injection 链接方法:

'foo.bar'.split('.').inject(obj, :send)

澄清回应评论:从安全角度来看,字符串评估是最危险的事情之一。如果有任何方式从用户提供的输入构造字符串,而无需对该输入进行非常勤奋的检查和验证,那么您应该只考虑您的系统拥有。

从用户输入获取方法的 send(method) 也有风险,但攻击向量更有限。您的用户输入可能会导致您执行任何可通过接收器调度的 0 参数方法。这里的良好做法是在调度之前始终将方法列入白名单:

VALID_USER_METHODS = %w{foo bar baz}
def safe_send(method)
  raise ArgumentError, "#{method} not allowed" unless VALID_USER_METHODS.include?(method.to_s)
  send(method)
end
于 2013-04-07T17:26:55.510 回答
1

聚会有点晚了,但我不得不做一些类似的事情,必须在一次调用中结合“发送”和访问来自哈希/数组的数据。基本上,这允许您执行以下操作

value = obj.send_nested("data.foo['bar'].id")

在引擎盖下,这将做类似的事情

obj.send(data).send(foo)['bar'].send(id)

这也适用于属性字符串中的符号

value = obj.send_nested('data.foo[:bar][0].id')

这将做类似的事情

obj.send(data).send(foo)[:bar][0].send(id)

如果您想使用无关访问,您也可以将其添加为参数。例如

value = obj.send_nested('data.foo[:bar][0].id', with_indifferent_access: true)

由于涉及更多,这里是指向 gist 的链接,您可以使用该链接将该方法添加到基本 Ruby 对象。(它还包括测试,以便您了解它是如何工作的)

于 2017-01-26T15:01:10.253 回答