tags:

views:

73

answers:

2

Is there a more concise and idiomatic way to write the following code, which is used to specify default values for optional parameters (in the params/options hash) to a method?

def initialize(params={})
  if params.has_key? :verbose
    @verbose = params[:verbose]
  else
    @verbose = true # this is the  default value
  end
end

I would love to simplify it to something like this:

def initialize(params={})
  @verbose = params[:verbose] or true
end

which almost works, except that you really need to use has_key? :verbose as the condition, instead of just evaluating params[:verbose], in order to cover cases when you want to specify a value of 'false' (i.e. if you want to pass :verbose => false as the argument in this example).

I realize that in this simple example I could easily do:

def initialize(verbose=false)
  @verbose = verbose
end

but, in my real code I actually have a bunch of optional parameters (in addition to a few required ones) and I'd like to put the optional ones in the params hash so that I can easily only specify (by name) the few that I want, rather than having to list them all in order (and potentially having to list ones I don't actually want).

+9  A: 

A common pattern is to use

def foo(options = {})
  options = { :default => :value }.merge(options)
end

You’ll end up with options being a hash containing the passed in values, with the options from your defaults hash as any that weren’t provided.

Ciarán Walsh
Thanks! This works quite well.
Kelan
is there a reason to use plain `merge` over `merge!` ? (since i presume `merge!` would be faster)
banister
@banister: Using `options.merge!(defaults)` would do the overwriting in the wrong direction. You need to do `defaults.merge(options)` to overwrite the default valuess with the specified options. And, since it would be awkward to store the result in 'defaults', you can't use `merge!` and must instead assign the result back to 'options'.I'm not sure if there is a huge speed difference between `merge` and `merge!`.
Kelan
@Kelan, what? the code *is* equivalent to `defaults.merge(options)` since `{ :default => :value }` is `defaults` (though inline).If you look at the code you'll see he's using `options` for both the parameter and the final hash. If you don't believe me, try it out. Using `merge!` is fine (I just did and it works exactly as you'd expect).
banister
@banister: Oh, did you mean to do `options = defaults.merge!(options)` instead of `options = defaults.merge(options)`? Yeah, that works fine. I was thinking before that you meant doing just `defaults.merge!(options)` and not assigning the result back to options... So, I agree either way works. As for performance, based on a little test I just did, using `merge!` is indeed about 40% faster. So, your presumption was correct. Good call!
Kelan
BTW, here is the test I did: http://gist.github.com/313693
Kelan
A: 

I think you're looking for this

params = { :verbose => true }.merge(params)

-daniel

Daniel