method_missing and respond_to_missing?
Prerequisites: Metaprogramming: send and define_method
When you call a method that doesn't exist, Ruby doesn't give up
immediately: it calls method_missing on the object, passing the
name and arguments. The default implementation raises
NoMethodError — but you can override it.
Example: a wrapper that forwards dynamic readers
class Settings
def initialize(values)
@values = values
end
def method_missing(name, *args)
key = name.to_s
if key.end_with?("?") && @values.key?(key.chomp("?").to_sym)
!!@values[key.chomp("?").to_sym]
elsif @values.key?(name)
@values[name]
else
super
end
end
def respond_to_missing?(name, include_private = false)
@values.key?(name.to_s.chomp("?").to_sym) || super
end
end
settings = Settings.new({ theme: "dark", beta: true })
p settings.theme
p settings.beta?
p settings.respond_to?(:theme)
settings.colour
"dark"
true
true
settings.rb:13:in 'Settings#method_missing': undefined method 'colour' for an instance of Settings (NoMethodError)
from settings.rb:26:in '<main>'
The error is raised by the super call on line 13 — the default
method_missing — which is why it's reported from inside
Settings#method_missing.
Two rules that matter:
- Call
superfor names you don't handle, so unknown methods still raise a normalNoMethodErrorinstead of silently returningnil. - Always define
respond_to_missing?alongside it, sorespond_to?andmethod(:theme)tell the truth about what the object supports.
Prefer the alternatives
method_missing is slower than a real method (Ruby has to fail a
lookup first), invisible to editors and documentation, and easy to get
subtly wrong. Before reaching for it, consider:
define_method— if the set of names is known when the class loads (as it is for@valueskeys in many cases), generate real methods up front.- Explicit delegation —
ForwardableorSimpleDelegator(covered in the Design Patterns lesson) forward calls to another object without any magic.
Reach for method_missing when the set of names is genuinely open-
ended — proxies, wrappers around remote APIs, or builder DSLs.