Ruby嵌套发送

Lyn*_*ynn 14 ruby metaprogramming

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

def foo
   @foo
end
Run Code Online (Sandbox Code Playgroud)

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

obj.send("foo")  # Returns @foo
Run Code Online (Sandbox Code Playgroud)

是否有一种直接的方法来执行递归发送以获取@foo对象的参数,例如:

obj.send("foo.bar")  # Returns @foo.bar
Run Code Online (Sandbox Code Playgroud)

Mic*_*ant 19

你可以使用instance_eval:

obj.instance_eval("foo.bar")
Run Code Online (Sandbox Code Playgroud)

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

obj.instance_eval("@foo.bar")
Run Code Online (Sandbox Code Playgroud)

  • 字符串的`instance_eval`是危险的,昂贵的,很少被要求. (2认同)

dbe*_*hur 15

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

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

obj.foo.bar
Run Code Online (Sandbox Code Playgroud)

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

obj.send(foo_method).send(bar_method)
Run Code Online (Sandbox Code Playgroud)

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

如果方法以点线形式出现,可以使用split和inject来链接方法:

'foo.bar'.split('.').inject(obj, :send)
Run Code Online (Sandbox Code Playgroud)

澄清以回应评论:从安全角度来看,字符串评估是最危险的事情之一.如果有任何方式字符串是由用户提供的输入构造的,而没有令人难以置信的仔细检查和验证该输入,您应该只考虑您的系统所有.

发送(方法)从用户输入获得方法也有风险,但有一个更有限的攻击向量.您的用户输入可能会导致您执行通过接收器可分派的任何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
Run Code Online (Sandbox Code Playgroud)