Lesson 10 / 25

Method Arguments and Conventions

Use positional, optional, keyword and splat arguments, and follow naming conventions.

Flexible method signatures

Ruby methods accept required positional arguments, optional ones with defaults (def greet(name, greeting = "Hello")), a splat *args collecting extra positional arguments into an array, keyword arguments (def connect(host:, port: 443), where host: without a default is required), a double splat **opts collecting extra keywords into a hash, and a block parameter &block. Ruby 3 separates positional and keyword arguments strictly, so passing a hash where keywords are expected requires **hash. Use keyword arguments for anything optional or easily confused, such as booleans and units. Methods return the value of their last expression, and return is used mainly for early exits. Naming conventions carry meaning: ? for predicates returning booleans (empty?, valid?), ! for dangerous versions of a method that mutate or raise (save!, sort!), and = for setters (name=). Argument forwarding (...) passes all arguments through, handy in wrappers and decorators.

Kinds of method parameters

Positional, optional, splat, keyword, double splat and block parameters, in that order.

A method box with input slots of different styles arranged left to right, the last one shaped like a small code block.
Figure 4.1 — Ruby method parameter kinds.

Keyword arguments, splats and forwarding

Readable calls and safe defaults.

def send_invoice(invoice, to:, cc: [], urgent: false, **headers)
  subject = urgent ? "URGENT: #{invoice}" : invoice.to_s
  puts "To #{to} (cc #{cc.join(', ')}): #{subject} #{headers}"
end

send_invoice("INV-7", to: "asha@example.com", urgent: true, reply_to: "billing@example.com")

def total(*amounts, discount: 0)
  amounts.sum * (1 - discount)
end
puts total(100, 250, 50, discount: 0.1)    # 360.0

options = { to: "ravi@example.com", cc: ["ops@example.com"] }
send_invoice("INV-8", **options)            # pass a hash as keywords explicitly

def logged_send(...)                         # forward all arguments and the block
  puts "sending..."
  send_invoice(...)
end
logged_send("INV-9", to: "meera@example.com")

Keywords for booleans

ship(order, true, false) is a puzzle. ship(order, express: true, gift_wrap: false) explains itself and cannot mix up the two flags.

Quick check: In `def connect(host:, port: 443)`, which statement is true?

  • Both are optional
  • host is positional
  • host is a required keyword argument; port is optional with a default
  • It is a syntax error
Answer

host is a required keyword argument; port is optional with a default — A keyword without a default value is required.