Keyword Arguments
Remarks#
Keyword arguments were introduced in Ruby 2.0, and improved in Ruby 2.1 with the addition of required keyword arguments.
A simple method with a keyword argument looks like the following one:
def say(message: "Hello World")
puts message
end
say
# => "Hello World"
say message: "Today is Monday"
# => "Today is Monday"
As a reminder, the same method without keyword argument would have been:
def say(message = "Hello World")
puts message
end
say
# => "Hello World"
say "Today is Monday"
# => "Today is Monday"
You can simulate keyword argument in previous Ruby versions using a Hash parameter. This is still a very common practice, especially in libraries that provides compatibility with pre-2.0 Ruby versions:
def say(options = {})
message = options.fetch(:message, "Hello World")
puts
end
say
# => "Hello World"
say message: "Today is Monday"
# => "Today is Monday"
Using keyword arguments
You define a keyword argument in a method by specifying the name in the method definition:
def say(message: "Hello World")
puts message
end
say
# => "Hello World"
say message: "Today is Monday"
# => "Today is Monday"
You can define multiple keyword arguments, the definition order is irrelevant:
def say(message: "Hello World", before: "<p>", after: "</p>")
puts "#{before}#{message}#{after}"
end
say
# => "<p>Hello World</p>"
say message: "Today is Monday"
# => "<p>Today is Monday</p>"
say after: "</p><hr>", message: "Today is Monday"
# => "<p>Today is Monday</p><hr>"
Keyword arguments can be mixed with positional arguments:
def say(message, before: "<p>", after: "</p>")
puts "#{before}#{message}#{after}"
end
say "Hello World", before: "<span>", after: "</span>"
# => "<span>Hello World</span>"
Mixing keyword argument with positional argument was a very common approach before Ruby 2.1, because it was not possible to define required keyword arguments.
Moreover, in Ruby < 2.0, it was very common to add an Hash
at the end of a method definition to use for optional arguments. The syntax is very similar to keyword arguments, to the point where optional arguments via Hash
are compatible with Ruby 2 keyword arguments.
def say(message, options = {})
before = option.fetch(:before, "<p>")
after = option.fetch(:after, "</p>")
puts "#{before}#{message}#{after}"
end
# The method call is syntactically equivalent to the keyword argument one
say "Hello World", before: "<span>", after: "</span>"
# => "<span>Hello World</span>"
Note that trying to pass a not-defined keyword argument will result in an error:
def say(message: "Hello World")
puts message
end
say foo: "Hello"
# => ArgumentError: unknown keyword: foo
Required keyword arguments
Required keyword arguments were introduced in Ruby 2.1, as an improvement to keyword arguments.
To define a keyword argument as required, simply declare the argument without a default value.
def say(message:)
puts message
end
say
# => ArgumentError: missing keyword: message
say message: "Hello World"
# => "Hello World"
You can also mix required and non-required keyword arguments:
def say(before: "<p>", message:, after: "</p>")
puts "#{before}#{message}#{after}"
end
say
# => ArgumentError: missing keyword: message
say message: "Hello World"
# => "<p>Hello World</p>"
say message: "Hello World", before: "<span>", after: "</span>"
# => "<span>Hello World</span>"
Using arbitrary keyword arguments with splat operator
You can define a method to accept an arbitrary number of keyword arguments using the double splat (**
) operator:
def say(**args)
puts args
end
say foo: "1", bar: "2"
# {:foo=>"1", :bar=>"2"}
The arguments are captured in a Hash
. You can manipulate the Hash
, for example to extract the desired arguments.
def say(**args)
puts args[:message] || "Message not found"
end
say foo: "1", bar: "2", message: "Hello World"
# Hello World
say foo: "1", bar: "2"
# Message not found
Using a the splat operator with keyword arguments will prevent keyword argument validation, the method will never raise an ArgumentError
in case of unknown keyword.
As for the standard splat operator, you can re-convert a Hash
into keyword arguments for a method:
def say(message: nil, before: "<p>", after: "</p>")
puts "#{before}#{message}#{after}"
end
args = { message: "Hello World", after: "</p><hr>" }
say(**args)
# <p>Hello World</p><hr>
args = { message: "Hello World", foo: "1" }
say(**args)
# => ArgumentError: unknown keyword: foo
This is generally used when you need to manipulate incoming arguments, and pass them to an underlying method:
def inner(foo:, bar:)
puts foo, bar
end
def outer(something, foo: nil, bar: nil, baz: nil)
puts something
params = {}
params[:foo] = foo || "Default foo"
params[:bar] = bar || "Default bar"
inner(**params)
end
outer "Hello:", foo: "Custom foo"
# Hello:
# Custom foo
# Default bar