Ruby - Keyword Arguments - Can You Treat All of the Keyword Arguments as a Hash? How

Pass hash to a function that accepts keyword arguments

You have to convert the keys in your hash to symbols:

class Song
def initialize(*args, **kwargs)
puts "args = #{args.inspect}"
puts "kwargs = #{kwargs.inspect}"
end
end

hash = {"band" => "for King & Country", "song_name" => "Matter"}

Song.new(hash)
# Output:
# args = [{"band"=>"for King & Country", "song_name"=>"Matter"}]
# kwargs = {}

symbolic_hash = hash.map { |k, v| [k.to_sym, v] }.to_h
#=> {:band=>"for King & Country", :song_name=>"Matter"}

Song.new(symbolic_hash)
# Output:
# args = []
# kwargs = {:band=>"for King & Country", :song_name=>"Matter"}

In Rails / Active Support there is Hash#symbolize_keys

How do methods use hash arguments in Ruby?

Example:

def foo(regular, hash={})
puts "regular: #{regular}"
puts "hash: #{hash}"
puts "a: #{hash[:a]}"
puts "b: #{hash[:b]}"
end

foo("regular argument", a: 12, :b => 13)

I use hash={} to specify that the last argument is a hash, with default value of empty hash. Now, when I write:

foo("regular argument", a: 12, :b => 13)

It's actually a syntactic sugar for:

foo("regular argument", {a: 12, :b => 13})

Also, {a: 12} is syntactic sugar for {:a => 12}.

When all of this is combined together, you get a syntax that looks similar to named arguments in other languages.

Ruby automatically expands Hash into keyword arguments without double splat

Since x is optional, hash moves over to kwarg argument.
Unspecified keywords raise error in that case:

def foo(name:)
p name
end

foo # raises "ArgumentError: missing keyword: name" as expected
foo({name: 'Joe', age: 10}) # raises "ArgumentError: unknown keyword: age"

Check out this article

splat operator on hash for keyword arguments in ruby method definition

Yeah... that's not a thing you can do.

**foo in an argument list is how you collect a kwargs hash, so it can't also be how you inject one.

More importantly, the main point of kwargs is that they explode the hash into local variables -- that can't work if it's expanding a hash at runtime.

The closest you could get would be something like:

def initialize(**values)
values = DEFAULTS.merge(values)
raise "..." unless (values.keys - DEFAULTS.keys).empty?
@a = values[:a]
@b = values[:b]
end

ruby keyword arguments of method

Ruby doesn't actually have keyword arguments. Rails is exploiting a feature of Ruby which lets you omit the braces around a hash. For example, with find, what we're really calling is:

Person.find(:all, { :conditions => "...", :offset => 10, :limit => 10 } )

But if the hash is the last argument of the method, you can leave out the braces and it will still be treated as a hash:

Person.find(:all, :conditions => "...", :offset => 10, :limit => 10)

You can use this in your own methods:

def explode(options={})
defaults = { :message => "Kabloooie!", :timer => 10, :count => 1 }
options = defaults.merge(options)

options[:count].times do
sleep options[:timer]
puts options[:message]
end
end

And then call it:

explode :message => "Meh.", :count => 3

Or call it without an argument, resulting in all default values being used:

explode

Collecting keyword arguments in Ruby

It's because the first parameter i, is a required parameter (no default value), so, the first value passed to the method (in your first example, this is the hash {a: 7, b: 8}) is stored into it.

Then, since everything else is optional, the remaining values (if any, in this example, there are none) are filled in, as applicable. IE, the second parameter will go to j, unless it is a named parameter, then it goes to kargs (or k). The third parameter, and any remaining--up until the first keyword argument, go into args, and then any keyword args go to kargs

def foo(i, j= 9, *args, k: 11, **kargs)
puts "i: #{i}; args: #{args}; kargs: #{kargs}"
end

foo(a: 7, b: 8)
# i: {:a=>7, :b=>8}; args: []; kargs: {}

Handling a method with an optional argument followed by a list of keyword arguments

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}, {}]

Argument error due to ruby 3.0 deprecation

:params => {} is a Hash of {params: {}} to use kwargs (keyword arguments) the syntax is similar but distinctly different to the parser.

To resolve this (and clean up your code a bit) change all instances of :symbol => value to be symbol: value.

For Example:

post :create, :params => { 
:identifiedBy => { :label => epc },
:model => { :productId => productId } }

becomes

post :create, params: { 
identifiedBy: { label: epc },
model: { productId: productId } }

Now params will be treated as a keyword argument, additionally I would change all the old Hash rocket usages where the key is a symbol (as I did in my recommended change)



Related Topics



Leave a reply



Submit