开发者

How can I validate a function passed to a method in Ruby?

I'm writing a library to humanize bytes in Ruby (e.g. to turn the byte count 1025 into the string 1.1K), and I'm stuck on one element of the design.

开发者_JS百科

The plan is to extend Numeric with ahumanize method that returns a human-friendly string when called on a number. After looking at the source of Number::Bytes::Human (a Perl module that I like a lot for this), I decided to add two options to the method: one to use 1000 byte blocks and one to use floor rather than ceil for the default rounding function.

In order to be maximally flexible, the method's definition uses a hash for the parameters, so that users can change one or both of the options. If no parameters are passed, a default hash is used. That gives me something like this:

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

Ideally, I would like to let the user pass a function as the value of params[:r_func], but I can't figure out how to validate that it's either ceil or floor. Because I can't get a handle on this, I've ended up doing the following, which feels pretty clumsy:

  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

If anyone knows a trick for peeking at the method that a Ruby lambda contains, I would love to hear it. (I'm also happy to hear any other design advice.) Thanks.


There's no reason to let the user pass a method in if you're going to be that draconian about what they are allowed to pass (you know there are other rounding schemes besides ceiling and floor, right?)

If you want to restrict the user to ceiling and floor, just allow them to pass the symbol :ceiling or :floor in. A more flexible design would be to allow the method to take a block which receives a single parameter, the number to be rounded. Then the user could use whatever rounding algorithm they prefer, including custom ones.

By the way, Numeric#humanize falls into that category of monkeypatches with such a popular name that you are likely to run into namespace collisions (and resulting subtle bugs) in anything but a small, personal project.


I don't see any point in having the caller pass a lambda if you're not going to actually call the thing. Make it a symbol instead and you can do something like:

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


Why have them pass a function pointer instead of a boolean? That way you avoid the problem of having to validate the function.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜