I have the following method:
def test(first_param = nil, keyword_arg: nil)
puts "first_param: #{first_param}"
puts "keyword_arg: #{keyword_arg}"
end
All the following calls do what I expect them to do:
test(:something)
#=> first_param: something
# keyword_arg:
test(nil, keyword_arg: :keyword_arg)
#=> first_param:
# keyword_arg: keyword_arg
test({ first_param: :is_a_hash }, keyword_arg: :is_still_working)
#=> first_param: {:first_param=>:is_a_hash}
# keyword_arg: is_still_working
But omitting the optional keyword_arg
and passing a hash as first argument gives me an error:
test(first_param: :is_a_hash)
#=> test.rb:1:in `test': unknown keyword: first_param (ArgumentError)
# from test.rb:12:in `<main>'
I'd expect it to set first_param
to { first_param: :is_hash }
and keyword_arg
being nil
.
It seems it is interpreting every hash as keyword arg:
test(keyword_arg: :should_be_first_param)
#=> first_param:
# keyword_arg: should_be_first_param
This should have set first_param
to { keyword_arg: :should_be_first_param }
, leaving keyword_arg
nil
in my opinion.
Is this a parser bug or expected behaviour? Tested on ruby 2.3.0 and 2.2.4.
Edit: Making the first parameter mandatory and everything works like I'd expect to:
def test_mandatory(first_param, keyword_arg: nil)
puts "first_param: #{first_param}"
puts "keyword_arg: #{keyword_arg}"
end
test_mandatory(first_param: :is_a_hash)
#=> first_param: {:first_param=>:is_a_hash}
# keyword_arg:
test_mandatory(keyword_arg: :should_be_first_param)
#=> first_param: {:keyword_arg=>:should_be_first_param}
# keyword_arg:
I'd expect making a parameter optional does not change the way parameters are parsed.
I opened an issue on bugs.ruby-lang.org, then the devs can clear up whether it is intended like this or a side effect of kword args.