Keyword arguments and forwarding
Required and optional keywords
A keyword argument is named at the call site, so the reader knows what each value means without opening the method. A keyword with no default is required; a keyword with a default is optional:
def greet(name:, greeting: "Hello")
"#{greeting}, #{name}"
end
puts greet(name: "Ada")
puts greet(name: "Ada", greeting: "Hi")
ruby greet_demo.rb
Hello, Ada
Hi, Ada
Calling without the required keyword raises before the method body runs, and the message names the missing one:
ruby -e 'def f(a:) = a; f'
-e:1:in 'f': missing keyword: :a (ArgumentError)
from -e:1:in '<main>'
Collecting the rest with **
**rest collects the keywords the method does not name, which lets a wrapper pass options through without listing them:
ruby -e 'def f(a:, b: 2, **kw) = [a, b, kw]; p f(a: 1, c: 3)'
[1, 2, {c: 3}]
The result is an array of the two named values and a hash of the rest. Ruby 3.4 prints the hash in the short form, {c: 3}, the same change lesson 34, later in the course, showed for p.
A trailing hash and a keyword call are the same thing at the call site in Ruby 3: f(a: 1) passes keywords. The difference appears at the definition: def f(opts = {}) takes a positional hash, and a caller that passes keywords to it gets an ArgumentError in Ruby 3 unless the hash is wrapped in braces.
Forwarding with ...
A method that adds a step and calls another one should forward everything it received: positionals, keywords and the block. ... does exactly that, and it is shorter than spelling out all three:
def inner(a, b:)
a + b
end
def outer(...)
inner(...)
end
puts outer(1, b: 2)
ruby -e 'def inner(a, b:) = a + b; def outer(...) = inner(...); p outer(1, b: 2)'
3
Before Ruby 2.7 the same wrapper needed def outer(*args, **kwargs, &block) and a call with three splats. ... is the version to write now, and it also forwards a block the wrapper never names.
What to watch for
A keyword and a positional argument with the same name are two different
parameters. def f(x, x:) is legal and confusing;
rename one of them.
A method with many optional keywords is a configuration object in
disguise. When the list passes five, a small class or a
Data value with defaults is easier to validate
and to read.
Next: Blocks, yield and closures
Article author: Arthur Isaev