Ruby Method Missing Chains
The intermediate course let one object answer unknown calls with method_missing. Here the first handler passes the call along to a second object, so unknown calls travel down a chain.
Forwarding to an owned object
Car holds an engine and defines no start method. Its method_missing forwards the unknown call to the engine with send, and the engine answers it.
class Engine
def start
"engine on"
end
end
class Car
def initialize
@engine = Engine.new
end
def method_missing(name, *args)
@engine.send(name, *args)
end
end
puts Car.new.startThe second handler answers
Helper defines no whistle method, so its own method_missing answers the forwarded call. The answer names the call that travelled down the chain.
class Helper
def method_missing(name, *args)
"helper has no #{name}"
end
end
class Front
def initialize
@helper = Helper.new
end
def method_missing(name, *args)
@helper.send(name, *args)
end
end
puts Front.new.whistleThe name arrives whole
Echo answers any unknown call with the name it received, turned into a string. The long name morning_light arrives whole, with nothing cut off.
class Echo
def method_missing(name, *args)
name.to_s
end
end
class Relay
def initialize(target)
@target = target
end
def method_missing(name, *args)
@target.send(name, *args)
end
end
relay = Relay.new Echo.new
puts relay.morning_lightThe super path raises
Tail answers its unknown calls with super, and nothing further along can answer either. The call raises, and the rescue prints only the class of the error.
class Tail
def method_missing(name, *args)
super
end
end
class Head
def initialize
@tail = Tail.new
end
def method_missing(name, *args)
@tail.send(name, *args)
end
end
begin
Head.new.vanish
rescue => e
puts e.class
endExample
class Cook
def bake(item)
"baked #{item}"
end
def method_missing(name, *args)
"cook cannot #{name}"
end
end
class Waiter
def initialize
@cook = Cook.new
end
def method_missing(name, *args)
@cook.send(name, *args)
end
end
waiter = Waiter.new
puts waiter.bake "bread"
puts waiter.fry "egg"Key points
- A method_missing handler can forward an unknown call with send.
- send takes the original name and the original arguments.
- The second object answers with its real methods or its own method_missing.
- super inside method_missing keeps the search going and raises when nothing answers.
- The unknown name arrives whole as one symbol.
In this example, Waiter defines no bake or fry method, so both calls reach its method_missing. Each call is forwarded to the cook with send. The cook answers bake as a real method and fry through its own method_missing.
Try it Yourself
Edit the code, then press Run.
Running is simulated
Exercise
In a chain, how does the first method_missing pass a call to the second object?