Ruby嵌套发送

Ruby嵌套发送,ruby,metaprogramming,Ruby,Metaprogramming,假设我有一个对象,该对象具有访问对象的方法: def foo @foo end 我知道我可以使用send访问该方法: obj.send("foo") # Returns @foo 是否有一种简单的方法可以执行递归发送以获取@foo对象上的参数,如: obj.send("foo.bar") # Returns @foo.bar 您可以使用: 您甚至可以直接访问实例变量: obj.instance_eval("@foo.bar") 虽然OP已经接受了使用instance\u eva

假设我有一个对象,该对象具有访问对象的方法:

def foo
   @foo
end
我知道我可以使用send访问该方法:

obj.send("foo")  # Returns @foo
是否有一种简单的方法可以执行递归发送以获取@foo对象上的参数,如:

obj.send("foo.bar")  # Returns @foo.bar
您可以使用:

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

obj.instance_eval("@foo.bar")

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

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

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

obj.send(foo_method).send(bar_method)
这是一个简单的和所有人需要的

如果方法以虚线字符串的形式出现,可以使用split和inject来链接方法:

'foo.bar'.split('.').inject(obj, :send)
针对评论进行澄清:从安全角度来看,字符串评估是最危险的事情之一。如果从用户提供的输入中构建字符串的方式不需要对输入进行非常仔细的检查和验证,就应该只考虑系统所拥有的。< /P> < P>发送(方法),从用户输入中获得方法也有风险,但有一个更有限的攻击向量。用户输入可能会导致您执行任何可通过接收器分派的0-argument方法。这里的良好做法是在分派之前始终将方法列为白名单:

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

参加聚会有点晚了,但我不得不做一些类似的事情,在一次调用中同时“发送”和访问散列/数组中的数据。基本上,这允许您执行以下操作

value=obj.send\u嵌套(“data.foo['bar'].id”)

在引擎盖下,这将做一些类似于

obj.send(数据).send(foo)['bar'].send(id)

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

value=obj.send\u嵌套('data.foo[:bar][0.id')

它会做一些类似于

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

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

value=obj.send\u嵌套('data.foo[:bar][0].id',访问权限为true)


因为它有点复杂,所以您可以使用它将该方法添加到基本Ruby对象中。(它还包括测试,以便您可以看到它是如何工作的)

obj.send('foo').send('bar')
@SergioTulentsev-对不起,我应该澄清一下-是否有一种方法可以一步完成这一工作。您所描述的内容需要解析字符串“foo.bar”,以了解您需要递归调用send()。我只是想知道是否有一些现有的机制可以自动或更干净地完成这项工作。@Lynn:我不知道有这样的内置机制。是的,我认为
“foo.bar”.split('..).inject(obj){| obj,property | obj.send(property)}
是最接近的了。@PascalJungblut可以简化为
“foo.bar.split(.”.inject(obj,:send)
。在
“@foo.bar
什么是
bar
?它是在哪里定义的?它是对存储在
@foo
中的对象调用的
bar
方法。这与您在
@foo
的实例方法中键入此内容完全相同。查看文档。
instance\u eval
字符串是危险的、昂贵的,而且很少被调用。基于字符串的
send
调用链并不比使用
eval
更安全。我同意这两种观点。IMHO,出于明确的安全原因和开发人员的维护,我将把可以被称为的东西列入白名单。您正在创建一个下沉孔,所以在它周围要小心。@dbenhur-迈克尔的评论-在安全风险方面发送和注入不像eval那么糟糕吗?还是一个比另一个更可取?@MichaëlWitrant Inzhdeed,基于输入的发送应该通过白名单得到适当的祝福。然而,与管柱评估相比,它的表面积要小得多。攻击向量限于当前接收器及其祖先的无参数方法。
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