5

I have a method defined like this:

def woot(a = nil, b: nil)
  ...
end

What is the least ugly way to pass a Hash instance to a when b is omitted?

I tried

woot(x: 1)
woot({x: 1})
h = {x: 1}
woot(h)

but they all raise

ArgumentError: unknown keyword: x

The only way to make the call that I have figured out is

woot({x: 1}, **{})

Is there a nicer way?

sawa
  • 165,429
  • 45
  • 277
  • 381
jedediah
  • 1,179
  • 9
  • 20
  • 1
    http://stackoverflow.com/questions/27821422/how-can-i-collapse-double-splat-arguments-into-nothing :: **TLDNR;** it’s likely a bug in current ruby impl. – Aleksei Matiushkin Jan 18 '15 at 06:53

2 Answers2

1

Your first argument is optional, so Ruby assumes that the hash you're passing is for the named arguments (*).

I don't think there's a better workaround, though this is a bit shorter (but not necessarily cleaner):

woot({x: 1}, {})

If I were you I would change the method signature to resolve the ambiguity.


(*): Well, not always: woot({1=>3}) will happily pass the hash to the first optional argument.

Even weirder, in case of woot({1=> 5, :b => 4}), Ruby will split the dictionary, and pass {1=>5} to the first optional parameter.

Looks like it tries to grab the named parameters first (and will complain about not existing names), then pass the rest to the optional parameters.

But I would love to see some language-lawyer explanation...

Karoly Horvath
  • 94,607
  • 11
  • 117
  • 176
  • Wow, you're right. That's even weirder. Not only does the type of the argument change the way it's parsed, but the type of keys contained in argument. So much for the principle of least surprise. – jedediah Jan 18 '15 at 12:07
  • There's more surprise... :) Unfortunately Ruby is mostly "defined" by what MRI does, so I it's hard to cite paragraphs, you might want to look up the MRI source... – Karoly Horvath Jan 18 '15 at 15:14
0

If you don't expect b's default value to change, ever, how about passing it explicitly?

woot({x: 1}, b: nil)

Of course, if b's default value does change one day, you'd still be passing in nil, thus overriding the b value for that call to something else than its new default value.

das-g
  • 9,718
  • 4
  • 38
  • 80