Methods

13.1 Defining Methods

def greet(name)
  "Hello, #{name}!"          # implicit return
end

def full_name(first, last) = "#{first} #{last}"   # endless method (Ruby 3.0+)

def divide(a, b)
  return nil if b.zero?      # early return
  a.to_f / b
end

def min_max(values)
  [values.min, values.max]   # "multiple return values" (an array)
end
lo, hi = min_max([3, 1, 9])

13.2 Parameter Types

Ruby 3.0+ separates positional and keyword arguments strictly: hashes aren't implicitly converted to keywords.

def method_with_all(required,                 # positional
                    optional = "default",     # optional positional
                    *rest,                     # splat: extra positionals as an array
                    keyword:,                  # required keyword
                    opt_kw: 10,                # optional keyword
                    **options,                 # double splat: extra keywords as a hash
                    &block)                    # explicit block
end

def create_user(name:, email:, role: "member", **extra)
  { name:, email:, role:, **extra }
end
create_user(name: "Ana", email: "[email protected]", admin: true)

def sum(*numbers) = numbers.sum
sum(1, 2, 3)
sum(*[4, 5, 6])                                # splat an array into arguments

opts = { name: "Bo", email: "[email protected]" }
create_user(**opts)                            # double splat a hash into keywords

def forward_all(...) = target(...)             # argument forwarding (Ruby 2.7+)
def forward_rest(*, **, &) = target(*, **, &)  # anonymous forwarding (Ruby 3.2+)

13.3 Method Naming Conventions

def empty? = items.none?               # predicate: returns a boolean
def sort! = items.sort!                # bang: mutates or raises (has a non-bang counterpart)
def name=(value)                        # setter
  @name = value.strip
end
def [](index) = items[index]            # operator methods
def +(other) = Money.new(amount + other.amount)
def to_s = "Money(#{amount})"           # conversion conventions: to_s, to_a, to_h, to_i, to_proc

13.4 Method Objects and Introspection

m = "hello".method(:upcase)
m.call                      # "HELLO"
m.arity; m.owner; m.parameters
[1, 2, 3].map(&method(:puts))
5.respond_to?(:+)
Integer.instance_methods(false)
obj.public_methods.grep(/name/)
method(:greet).source_location

13.5 Recursion

Ruby doesn't guarantee tail-call optimization (it's disabled by default). Deep recursion raises SystemStackError.

def factorial(n) = n <= 1 ? 1 : n * factorial(n - 1)

def fib(n, memo = {})
  return n if n < 2
  memo[n] ||= fib(n - 1, memo) + fib(n - 2, memo)
end