method_missing and Dynamic Methods
How Ruby handles calls to methods that don't exist, and the metaprogramming door that opens.
2 min read
Ruby lets you intercept method calls that would otherwise fail, and even define methods on the fly. This is metaprogramming territory — genuinely powerful, and genuinely easy to misuse, so it's worth understanding both what it does and when to reach for it.
What happens when a method doesn't exist
Normally, calling an undefined method raises NoMethodError:
"hello".fly_to_moon
# NoMethodError: undefined method `fly_to_moon' for "hello":StringBehind the scenes, Ruby's method lookup fails to find fly_to_moon anywhere in the object's class hierarchy, and as a last resort calls a method named method_missing — which, by default, is the one that raises NoMethodError. You can override it.
Overriding method_missing
class Ghost
def method_missing(name, *args)
"You called #{name} with #{args.inspect}, but I don't respond to that."
end
end
Ghost.new.anything_at_all(1, 2)
# "You called anything_at_all with [1, 2], but I don't respond to that."method_missing receives the method name (as a symbol), the arguments, and an optional block. Overriding it lets an object appear to respond to methods it never explicitly defined — useful for things like a wrapper around a dynamic data structure (think a JSON or database record with arbitrary fields).
class DynamicRecord
def initialize(data)
@data = data
end
def method_missing(name, *args)
key = name.to_s
if @data.key?(key)
@data[key]
else
super # important -- fall back to default behavior
end
end
def respond_to_missing?(name, include_private = false)
@data.key?(name.to_s) || super
end
end
record = DynamicRecord.new({ "name" => "Ada", "role" => "admin" })
record.name # "Ada"
record.role # "admin"
record.email # NoMethodError -- falls through to superAlways pair it with respond_to_missing?
If you override method_missing, always override respond_to_missing? too. Without it, record.respond_to?(:name) incorrectly returns false even though record.name works — code that checks respond_to? before calling (a common defensive pattern) will behave wrongly around your object.
Always call super for unhandled cases
The else super above matters: if your method_missing doesn't recognize the call, pass it up the chain rather than swallowing it or returning nil. Otherwise, genuine typos and bugs silently do nothing instead of raising a clear NoMethodError, which turns an easy-to-find bug into a confusing one.
define_method: the safer alternative
For most cases where you're tempted to reach for method_missing, defining methods dynamically with define_method is safer and faster, because the methods actually exist afterward (so respond_to? and introspection work correctly automatically):
class Config
%w[host port timeout].each do |setting|
define_method(setting) { instance_variable_get("@#{setting}") }
define_method("#{setting}=") { |value| instance_variable_set("@#{setting}", value) }
end
endWhen to actually use this
Rails itself uses method_missing-style techniques internally (dynamic finder-like behavior, for instance), but in application code, reach for it rarely — only when you're building a generic wrapper around genuinely dynamic data whose fields aren't known ahead of time. For anything with a fixed, known set of fields, plain methods or attr_accessor (covered later) are simpler, faster, and far easier for the next person to debug.
Test what you just learned
4 quick questions. Get all of them right to unlock the next lesson.
You can take the quiz without an account — logging in just lets your result count toward your progress.