假设我有一个访问对象的方法的对象:
def foo
@foo
end
我知道我可以使用send来访问该方法:
obj.send("foo") # Returns @foo
是否有一种直接的方法来执行递归发送以获取@foo对象的参数,例如:
obj.send("foo.bar") # Returns @foo.bar
答案 0 :(得分:19)
答案 1 :(得分:15)
虽然OP已经使用instance_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)
澄清以回应评论:从安全角度来看,字符串评估是最危险的事情之一。如果有任何方式字符串是由用户提供的输入构造的,而没有令人难以置信的仔细检查和验证该输入,那么您应该只考虑您的系统。
发送(方法)从用户输入获取方法也有风险,但是攻击向量更有限。您的用户输入可能会导致您执行通过接收器可分派的任何0-arghument方法。这里的好习惯是在调度之前始终将方法列入白名单: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
答案 2 :(得分: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)
如果您想使用无关紧要的访问权限,您也可以将其添加为参数。 E.g。
value = obj.send_nested('data.foo[:bar][0].id', with_indifferent_access: true)
由于它涉及更多,here is the link to the gist可用于将该方法添加到基础Ruby对象。 (它还包括测试,以便您可以看到它是如何工作的)