Ruby Method Arguments Without the Guesswork

Ruby supports more than three kinds of arguments. The useful mental model is: positional arguments, keyword arguments, collection arguments, and blocks.

Required and optional positional arguments

1
2
3
4
5
6
def greet(name, punctuation = "!")
"Hello #{name}#{punctuation}"
end

greet("Hudson") # => "Hello Hudson!"
greet("Hudson", ".") # => "Hello Hudson."

name is required; punctuation has a default. Missing a required argument or passing too many positional arguments raises ArgumentError.

Collecting positional arguments with splat

1
2
3
4
5
def greet_all(*names)
names.map { |name| "Hello #{name}" }
end

greet_all("Chinh", "Chuot", "Cho")

*names collects remaining positional arguments into an array. It is not really an “optional argument”; it is a rest parameter.

Keyword arguments

1
2
3
4
5
6
def profile(name:, age: nil)
{ name: name, age: age }
end

profile(name: "Hudson")
profile(name: "Hudson", age: 30)

name: is required, while age: nil is optional. Modern Ruby treats keyword arguments separately from a positional hash, so do not assume a hash will always be converted automatically.

Collect extra keywords with **:

1
2
3
4
5
def request(method:, path:, **options)
{ method: method, path: path, options: options }
end

request(method: :get, path: "/cats", timeout: 5)

Blocks

Methods can receive behavior as well as values:

1
2
3
4
5
6
def with_timing
started_at = Process.clock_gettime(Process::CLOCK_MONOTONIC)
result = yield
elapsed = Process.clock_gettime(Process::CLOCK_MONOTONIC) - started_at
[result, elapsed]
end

Use &block only when the block must be stored, forwarded, or called explicitly. Otherwise yield is simpler and avoids creating an unnecessary Proc object.

Ordering

A readable method usually follows this order:

1
2
def example(required, optional = nil, *rest, keyword:, **options, &block)
end

Ruby allows expressive APIs, but flexibility is not permission to make callers solve a puzzle. If a signature needs a documentary, an options object may be the kinder interface.