I want to pass an optional argument (any datatype, even a Hash) followed by a list of keyword arguments (which can be empty) to a method.
This is what I got:
def my_method(subject = nil, **options)
[subject, options]
end
The method output is as expected in the following cases:
nothing
my_method()
# => [nil, {}]
a subject
my_method('foo')
# => ["foo", {}]
a literal subject with options
my_method('foo', bar: 'bar')
# => ["foo", {:bar=>"bar"}]
a Hash
as subject with options
my_method({foo: 'foo'}, bar: 'bar')
# => [{:foo=>"foo"}, {:bar=>"bar"}]
no subject, only options
my_method(bar: 'bar')
# => [nil, {:bar=>"bar"}]
When passing a Hash
as subject and no options, the desired outcome is:
my_method({foo: 'foo'})
# => [{:foo=>"foo"}, {}]
But I get the following; I don't get the correct subject:
my_method({foo: 'foo'})
# => [nil, {:foo=>"foo"}]
Is my_method(foo: 'foo')
equivalent to my_method({foo: 'foo'})
? Any ideas on how I could get the desired outcome?
See, you have **options
as an argument which do not have any default value & first argument have default value. So understand following single argument case,
Whenever single argument is passed it is tried to assign to second argument (as first one is holding default nil) & if it fails due to type mismatch then it assign to first argument. That's how my_method(4)
works.
Now Suppose you have single argument passed as hash, which match to assign to 2nd argument then of course it get assigned to second argument & first is set default nil.
If you want to make it work, then you can do following,
> my_method({sd: 4}, {})
=> [{:sd=>4}, {}]
Or you can provide argument name while passing,
> my_method(subject: {sd: 4})
=> [{:sd=>4}, {}]
As far as I know this is a limitation of how Ruby passes arguments. It can't tell the difference between my_method({foo: 'foo'})
and my_method(foo: 'foo')
Why not hack around it with this?
if subject.is_a?(Hash) && options.empty?
subject, options = nil, subject
end
This assumes that subject
shouldn't be a hash though.
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