【问题标题】:How can I validate a function passed to a method in Ruby?如何验证传递给 Ruby 中方法的函数?
【发布时间】:2010-12-05 19:53:12
【问题描述】:

我正在编写一个库来人性化 Ruby 中的字节(例如,将字节数 1025 转换为字符串 1.1K),但我被困在设计的一个元素上。

计划是使用humanize 方法扩展Numeric,该方法在对数字调用时返回对人类友好的字符串。在查看了Number::Bytes::Human(一个我非常喜欢的Perl模块)的源代码之后,我决定在该方法中添加两个选项:一个使用1000字节块,一个使用floor而不是ceil对于默认舍入函数。

为了最大程度地灵活,该方法的定义对参数使用哈希,以便用户可以更改其中一个或两个选项。如果未传递任何参数,则使用默认散列。这给了我这样的东西:

def humanize(params = {})
  params = {:block => 1024, :r_func => lambda }.merge params
  # yada yada
end

理想情况下,我想让用户将函数作为params[:r_func] 的值传递,但我不知道如何验证它是ceil 还是floor。因为我无法处理这个问题,所以我最终做了以下事情,感觉很笨拙:

  def humanize(params = {})
    params = {:block => 1024, :r_func => 'ceil' }.merge params
    if params[:r_func].eql? 'ceil'
      params[:r_func] = lambda { |x| x.ceil }
    elsif params[:r_func].eql? 'floor'
      params[:r_func] = lambda { |x| x.floor }
    else 
      raise BadRound, "Rounding method must be 'ceil' or 'floor'."
    end
    # blah blah blah
  end

如果有人知道偷看 Ruby lambda 包含的方法的技巧,我很想听听。 (我也很高兴听到任何其他设计建议。)谢谢。

【问题讨论】:

    标签: ruby parameters methods


    【解决方案1】:

    如果您对允许用户通过的内容过于严厉,就没有理由让用户通过方法(您知道除了天花板和地板之外还有other rounding schemes,对吧?)

    如果您想将用户限制在天花板和地板上,只需允许他们传递符号 :ceiling:floor 即可。更灵活的设计是允许该方法采用接收单个参数的块,要四舍五入的数字。然后用户可以使用他们喜欢的任何舍入算法,包括自定义算法。

    顺便说一句,Numeric#humanize 属于猴子补丁类别,名称如此流行,以至于您可能会在任何小型个人项目中遇到命名空间冲突(以及由此产生的细微错误)。

    【讨论】:

    • 这很有趣:你看到的(可能是正确的)我是“严厉的”实际上是我在努力小心。我想防止用户(比如说)传递一些对方法的其余部分没有意义或有害的东西。我喜欢只接受一个块的想法。我将使用它和符号,看看什么对我有意义。至于名字,如果你有更好的主意,我愿意接受建议。我想humanize 的好处(它是如此明显)在这里也是一个缺点。
    • 一般来说,我不提倡向 Ruby 内置类添加方法。有许多替代方案,例如将其作为可包含模块中的实用方法 (humanize_number(42.3)),或创建使用委托的包装类 (n = HumaneNumber.new(42.3); n.to_s)。
    • 我是 Ruby 的初学者,但我认为它的内置类对这类事情开放是该语言的优势之一。 (介绍性书籍当然会不遗余力地宣传它。)我并不是真的不同意,实用方法很好 - 实际上这是我的第一个想法。我决定扩展 Numeric 对 Ruby 来说更惯用。再次感谢您的反馈。
    • @Telemachus:Avdi 的意见是他自己的。扩展内置类的问题是,如果多个库都试图同时修补同一个东西,可能会发生冲突,但这只是你需要记住的一个缺点,而不是绝对意味着你不能去做吧。
    • 这确实只是我的意见。然而,在这种情况下,我在野外看到(并写过)不止一个 #humanize 方法也是事实。这是一个流行的扩展。
    【解决方案2】:

    如果你不打算真正调用这个东西,我认为让调用者传递一个 lambda 没有任何意义。将其改为符号,您可以执行以下操作:

    raise BadRound, "Rounding method must be :ceil or :floor." unless [:ceil, :floor].include? params[:r_func]
    op = lambda {|x| x.send params[:r_func]}
    # blah blah blah
    

    【讨论】:

    • 我一直在尝试使用符号并失败了。 send 方法是我所缺少的。谢谢。 (作为记录,我确实调用了 lambda - 稍后,在私有工作方法中验证之后。)
    【解决方案3】:

    为什么让他们传递函数指针而不是布尔值?这样您就避免了必须验证函数的问题。

    【讨论】:

    • 我没有想到,但如果我把它留给两个选择是个好主意。
    猜你喜欢
    • 2023-03-07
    • 2019-09-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-19
    • 1970-01-01
    • 2014-01-05
    • 1970-01-01
    相关资源
    最近更新 更多