I have a method with a lengthy list of optional arguments, such as:
def foo(foo = nil, bar = nil, baz = nil, qux = nil)
# no-op
end
I thought that calling the method and passing a split hash as a parameter would map the hash items to parameters by matching the key with the method parameter:
params = { bar: 'bar', foo: 'foo' }
foo(*params)
Unfortunately, when I examine the local variables after calling the method with a split hash, I get exactly what I'd expect if I passed in a split array, but it's not what I was hoping for:
foo == [:bar, 'bar'] # hoped: foo == 'foo'
bar == [:foo, 'foo'] # hoped: bar == 'bar'
What am I lacking here?
(this answer refers to the version of Ruby that was current when the question was asked. See edit for the situation today.)
Ruby does not support passing arguments by name. The splat operator (*
) expands an arbitrary enumerable by calling to_ary
on it and splices the result into the argument list. In your case, the enumerable you pass in is a hash, which gets transformed into an array of key-value pairs:
[2] pry(main)> params.to_a
=> [[:bar, "bar"], [:foo, "foo"]]
So the first two arguments of the function will be the values [:bar, "bar"]
and [:foo, "foo"]
(regardless of their parameter name!).
If you want to have something similar to keyword arguments in Ruby, you can make use of the fact that you don't need the braces when passing a hash to a function as the last argument:
def foo(opts = {})
bar = opts[:bar] || <default>
foo = opts[:foo] || <default>
# or with a lot of parameters:
opts = { :bar => <default>, :foo => <default>, ... }.merge(opts)
end
foo(foo: 3) # equivalent to foo({ foo: 3 })
EDIT:
As of version 2.0, Ruby now supports named arguments with a dedicated syntax. Thanks to user jgoyon for pointing that out.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With