This is the formal specification of method and call arguments.
A method definition consist of:
For example:
def foo(
# These are positional arguments:
x, y, z = 1,
# This is the splat argument:
*args,
# These are the named arguments:
a, b, c = 2,
# This is the double splat argument:
**options
)
end
Each one of them is optional, so a method can do without the double splat, without the splat, without keyword arguments and without positional arguments.
A method call also has some parts:
foo(
# These are positional arguments
1, 2,
# These are named arguments
a: 1, b: 2
)
Additionally, a call argument can have a splat (*
) or double splat (**
). A splat expands a literals/tuple.html into positional arguments, while a double splat expands a literals/named_tuple.html into named arguments. Multiple argument splats and double splats are allowed.
When invoking a method, the algorithm to match call arguments to method arguments is:
When a splat method argument has no name, it means no more positional arguments can be passed, and next arguments must be passed as named arguments. For example:
# Only one positional argument allowed, y must be passed as a named argument
def foo(x, *, y)
end
foo 1 # Error, missing argument: y
foo 1, 2 # Error: wrong number of arguments (given 2, expected 1)
foo 1, y: 10 # OK
But even if a splat method argument has a name, arguments that follow it must be passed as named arguments:
# One or more positional argument allowed, y must be passed as a named argument
def foo(x, *args, y)
end
foo 1 # Error, missing argument: y
foo 1, 2 # Error: missing argument; y
foo 1, 2, 3 # Error: missing argument: y
foo 1, y: 10 # OK
foo 1, 2, 3, y: 4 # OK
There's also the possibility of making a method only receive named arguments (and list them), by placing the star at the beginning:
# A method with two required named arguments: x and y
def foo(*, x, y)
end
foo # Error: missing arguments: x, y
foo x: 1 # Error: missing argument: y
foo x: 1, y: 2 # OK
Arguments past the star can also have default values. It means: they must be passed as named arguments, but they aren't required (so: optional named arguments):
# A method with two required named arguments: x and y
def foo(*, x, y = 2)
end
foo # Error: missing argument: x
foo x: 1 # OK, y is 2
foo x: 1, y: 3 # OK, y is 3
Because arguments (without a default value) after the splat method argument must be passed by name, two methods with different required named arguments overload:
def foo(*, x)
puts "Passed with x: #{x}"
end
def foo(*, y)
puts "Passed with y: #{y}"
end
foo x: 1 # => Passed with x: 1
foo y: 2 # => Passed with y: 2
Positional arguments can always be matched by name:
def foo(x, *, y)
end
foo 1, y: 2 # OK
foo y: 2, x: 3 # OK
An external name can be specified for a method argument. The external name is the one used when passing an argument as a named argument, and the internal name is the one used inside the method definition:
def foo(external_name internal_name)
# here we use internal_name
end
foo external_name: 1
This covers two uses cases.
The first use case is using keywords as named arguments:
def plan(begin begin_time, end end_time)
puts "Planning bewteen #{begin_time} and #{end_time}"
end
plan begin: Time.now, end: 2.days.from_now
The second use case is making a method argument more readable inside a method body:
def increment(value, by)
# OK, but reads odd
value + by
end
def increment(value, by amount)
# Better
value + amount
end