带参数的动态方法调用

格雷

例如,我有两个方法的类:

class Example < ActiveRecord::Base
  def method_one(value)

  end
  def method_two

  end
end

和控制器中的方法,我称它们为:

  def example
    ex = Example.find(params[:id])
    ex.send(params[:method], params[:value]) if ex.respond_to?(params[:method])
  end

但是问题出在我尝试打电话时 method_two

ArgumentError (wrong number of arguments (1 for 0))

这是因为params[:value]回报nil最简单的解决方案是:

  def example
    ex = Example.find(params[:id])
    if ex.respond_to?(params[:method])
      if params[:value].present?
        ex.send(params[:method], params[:value])
      else
        ex.send(params[:method])
      end
    end
  end

我想知道是否有更好的解决方法,如果它为null则不传递参数。

杰弗罗伊

您尝试做的事情可能真的很危险,因此建议您先过滤一下params[:method]

allowed_methods = {
  method_one: ->(ex){ex.method_one(params[:value])}
  method_two: ->(ex){ex.method_two}
}
allowed_methods[params[:method]]&.call(ex)

我定义了一个将方法名称映射到调用该方法的lambda的哈希值,该哈希值可处理参数和所需的任何特殊情况。

如果params[:method]allowed_methods哈希中作为密钥,我只会得到一个lambda

&.语法是红宝石2.3新的安全导航操作,以及-短-执行如果接收者不是零以下方法(即结果allowed_methods[params[:method]]),如果你不使用Ruby> = 2.3,则可以使用try替代,这在这种情况下有类似的行为:

allowed_methods[params[:method]].try(:call, ex)

如果您不过滤的值params[:method],那么用户可以:destroy例如通过以删除您的条目,这当然不是您想要的。

另外,通过调用ex.send ...,您可以绕开对象的封装,而通常这是不应该的。要仅使用公共接口,请使用public_send


关于您的代码的重大安全漏洞的另一点:

eval是在Object(实际上继承自Kernel上定义的私有方法,因此您可以在任何对象上以这种方式使用它:

object = Object.new
object.send(:eval, '1+1') #=> 2

现在,使用您的代码,想象用户将eval的值params[:method]和任意的ruby代码放入中params[:value],他实际上可以在您的应用程序中做任何他想做的事情。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章