method_missing / respond_to_missing? (Ruby)
In Ruby, method_missing is a hook method that fires automatically when an undefined method is called. Combined with respond_to_missing?, it enables correct implementation of dynamic methods.
Syntax
# Hook that fires when an undefined method is called. def method_missing(method_name, *args, &block) # method_name: the called method name (Symbol). # args: array of arguments passed. # block: block passed (if any). super # Call super to raise NoMethodError if not handled. end # Must be defined alongside method_missing to integrate with respond_to?. def respond_to_missing?(method_name, include_private = false) # Return true if this object should respond to the method name. condition || super end
Method / Hook Reference
| Method / Hook | Description |
|---|---|
| method_missing(name, *args, &block) | Hook method that runs automatically when an undefined method is called. |
| respond_to_missing?(name, include_private) | Integrates with respond_to?. Returns true if the dynamic method can respond to the given name. |
| respond_to?(name) | Checks whether an object can respond to a given method. Delegates to respond_to_missing?. |
| method(name) | Retrieves a Method object. Dynamic methods are also retrievable when respond_to_missing? is correctly defined. |
| super | Calls the same method in the parent class. Always call super to raise NoMethodError when the case is not handled. |
Sample Code
A basic implementation of dynamic methods combining method_missing and respond_to_missing?.
method_missing_basic.rb
# A class that retrieves character stats via dynamic methods.
# Methods starting with "power_of_" are handled dynamically.
class Fighter
attr_reader :name, :stats
def initialize(name, stats)
@name = name
@stats = stats # Hash like { strength: 9000, speed: 7500, ki: 8800 }.
end
# Fires when an undefined method is called.
def method_missing(method_name, *args, &block)
method_str = method_name.to_s
if method_str.start_with?("power_of_")
# "power_of_strength" -> looks up :strength in stats.
stat_key = method_str.sub("power_of_", "").to_sym
if @stats.key?(stat_key)
return @stats[stat_key]
end
end
# Always call super to raise NoMethodError for unhandled cases.
super
end
# Integrates with respond_to?.
def respond_to_missing?(method_name, include_private = false)
method_str = method_name.to_s
if method_str.start_with?("power_of_")
stat_key = method_str.sub("power_of_", "").to_sym
return @stats.key?(stat_key)
end
super
end
def to_s
"#{@name}"
end
end
goku = Fighter.new("Son Goku", { strength: 9000, speed: 9500, ki: 9900 })
vegeta = Fighter.new("Vegeta", { strength: 8800, speed: 9000, ki: 9600 })
# Retrieve stats via dynamic methods.
puts "#{goku} strength: #{goku.power_of_strength}" # 9000
puts "#{goku} speed: #{goku.power_of_speed}" # 9500
puts "#{vegeta} ki: #{vegeta.power_of_ki}" # 9600
# respond_to? works correctly.
puts goku.respond_to?(:power_of_strength) # true
puts goku.respond_to?(:power_of_defense) # false (no defense key in stats)
puts goku.respond_to?(:fly) # false (not defined)
ruby method_missing_basic.rb Son Goku strength: 9000 Son Goku speed: 9500 Vegeta ki: 9600 true false false
A practical example of the DynamicProxy pattern that transparently delegates method calls to another object.
method_missing_proxy.rb
# Character class for Dragon Ball.
class Character
attr_reader :name, :race, :power_level
def initialize(name, race, power_level)
@name = name
@race = race
@power_level = power_level
end
def status
"#{@name} (#{@race}) Power Level: #{@power_level}"
end
def to_s
@name
end
end
# DynamicProxy: delegates method calls to a Character with logging.
class LoggingProxy
def initialize(target)
@target = target
@log = []
end
# Delegates all unrecognized methods to the target.
def method_missing(method_name, *args, &block)
if @target.respond_to?(method_name)
@log << "[LOG] #{method_name} was called."
@target.send(method_name, *args, &block)
else
super
end
end
def respond_to_missing?(method_name, include_private = false)
@target.respond_to?(method_name, include_private) || super
end
def show_log
puts "--- Call Log ---"
@log.each { |entry| puts entry }
end
end
gohan = Character.new("Son Gohan", "Half-Saiyan", 22000)
proxy = LoggingProxy.new(gohan)
# Call gohan's methods through the proxy.
puts proxy.name # Son Gohan
puts proxy.race # Half-Saiyan
puts proxy.status # Son Gohan (Half-Saiyan) Power Level: 22000
puts proxy.respond_to?(:name) # true
puts proxy.respond_to?(:fly) # false
proxy.show_log
ruby method_missing_proxy.rb Son Gohan Half-Saiyan Son Gohan (Half-Saiyan) Power Level: 22000 true false --- Call Log --- [LOG] name was called. [LOG] race was called. [LOG] status was called. [LOG] respond_to? was called. [LOG] respond_to? was called.
A comparison of a flawed and a correct implementation, showing how omitting super makes debugging difficult.
method_missing_debug.rb
# NG: implementation without super.
# Typos silently return nil instead of raising an error.
class BadProxy
def initialize(target)
@target = target
end
def method_missing(method_name, *args, &block)
if @target.respond_to?(method_name)
@target.send(method_name, *args, &block)
end
# Without super, calling an undefined method just returns nil silently.
end
end
piccolo = Object.new
def piccolo.name; "Piccolo"; end
# OK: implementation that always calls super for unhandled cases.
class GoodProxy
def initialize(target)
@target = target
end
def method_missing(method_name, *args, &block)
if @target.respond_to?(method_name)
@target.send(method_name, *args, &block)
else
# Always call super to raise NoMethodError.
super
end
end
def respond_to_missing?(method_name, include_private = false)
@target.respond_to?(method_name, include_private) || super
end
end
krilin = Object.new
def krilin.name; "Krillin"; end
def krilin.race; "Human"; end
proxy = GoodProxy.new(krilin)
puts proxy.name # Krillin
puts proxy.race # Human
begin
proxy.power_level # Undefined method -> raises NoMethodError.
rescue NoMethodError => e
puts "NoMethodError: #{e.message}"
end
ruby method_missing_debug.rb Krillin Human NoMethodError: undefined method 'power_level' for an instance of GoodProxy
Notes
method_missing is a central mechanism in Ruby metaprogramming. Because it fires every time an undefined method is called, it is used to build dynamic APIs such as DynamicProxy, DSL (Domain Specific Language), and ORM patterns.
When defining method_missing, respond_to_missing? must also be defined alongside it. This ensures that respond_to?, method(:xxx), and defined? correctly recognize the dynamic methods.
The most common pitfall in method_missing implementations is forgetting to call super for unhandled cases. Omitting super causes typos and calls to nonexistent methods to fail silently, making debugging very difficult. Delegating all methods without restriction also risks forwarding unintended method calls. It is safer to explicitly limit the scope of handled methods and always delegate the rest to super.
As an alternative to dynamic methods, define_method defines methods upfront, which is superior in both performance and debuggability. See module / include / extend for more on dynamic method definition.
Common Mistakes
Mistake 1: Forgetting to call super (silent failure)
When super is not called for an unhandled method name, a typo just returns nil without raising an error, making bugs harder to find.
ng_no_super.rb
class Fighter
def initialize(name, stats)
@name = name
@stats = stats
end
def method_missing(method_name, *args, &block)
stat_key = method_name.to_s.sub("power_of_", "").to_sym
if @stats.key?(stat_key)
return @stats[stat_key]
end
# Without super, an unmatched method name silently returns nil.
end
end
goku = Fighter.new("Son Goku", { strength: 9000 })
puts goku.power_of_strenght.inspect # typo: returns nil without error
ruby ng_no_super.rb nil
Correct approach: always call super for unhandled method names to raise NoMethodError.
ok_with_super.rb
class Fighter
def initialize(name, stats)
@name = name
@stats = stats
end
def method_missing(method_name, *args, &block)
stat_key = method_name.to_s.sub("power_of_", "").to_sym
if method_name.to_s.start_with?("power_of_") && @stats.key?(stat_key)
return @stats[stat_key]
end
super
end
def respond_to_missing?(method_name, include_private = false)
stat_key = method_name.to_s.sub("power_of_", "").to_sym
(method_name.to_s.start_with?("power_of_") && @stats.key?(stat_key)) || super
end
end
goku = Fighter.new("Son Goku", { strength: 9000 })
begin
puts goku.power_of_strenght.inspect # typo -> NoMethodError
rescue NoMethodError => e
puts "NoMethodError: #{e.message}"
end
puts goku.power_of_strength # correct name -> 9000
ruby ok_with_super.rb NoMethodError: undefined method 'power_of_strenght' for an instance of Fighter 9000
Mistake 2: Forgetting to define respond_to_missing?
When only method_missing is defined without respond_to_missing?, respond_to? returns false for dynamic methods, causing external code to wrongly conclude the object cannot respond to those methods.
ng_no_respond_to_missing.rb
class Fighter
def initialize(name, stats)
@name = name
@stats = stats
end
def method_missing(method_name, *args, &block)
stat_key = method_name.to_s.sub("power_of_", "").to_sym
if method_name.to_s.start_with?("power_of_") && @stats.key?(stat_key)
return @stats[stat_key]
end
super
end
# respond_to_missing? is not defined
end
goku = Fighter.new("Son Goku", { strength: 9000 })
puts goku.power_of_strength # 9000 (method works)
puts goku.respond_to?(:power_of_strength) # false (incorrect result)
ruby ng_no_respond_to_missing.rb 9000 false
Correct approach: implement the same condition in respond_to_missing? as in method_missing.
ok_respond_to_missing.rb
class Fighter
def initialize(name, stats)
@name = name
@stats = stats
end
def method_missing(method_name, *args, &block)
stat_key = method_name.to_s.sub("power_of_", "").to_sym
if method_name.to_s.start_with?("power_of_") && @stats.key?(stat_key)
return @stats[stat_key]
end
super
end
def respond_to_missing?(method_name, include_private = false)
stat_key = method_name.to_s.sub("power_of_", "").to_sym
(method_name.to_s.start_with?("power_of_") && @stats.key?(stat_key)) || super
end
end
goku = Fighter.new("Son Goku", { strength: 9000 })
puts goku.power_of_strength # 9000
puts goku.respond_to?(:power_of_strength) # true
puts goku.respond_to?(:power_of_defense) # false (no defense key in stats)
ruby ok_respond_to_missing.rb 9000 true false
Mistake 3: Infinite loop (calling an undefined method inside method_missing)
Calling an undefined method on the same object inside method_missing triggers method_missing again, causing an infinite loop.
ng_infinite_loop.rb
class Fighter
def initialize(name)
@name = name
end
def method_missing(method_name, *args, &block)
# log_call is undefined, so method_missing is triggered again here (infinite loop).
log_call(method_name)
super
end
def respond_to_missing?(method_name, include_private = false)
super
end
end
goku = Fighter.new("Son Goku")
goku.fly # => SystemStackError: stack level too deep
ruby ng_infinite_loop.rb ng_infinite_loop.rb:7:in 'Fighter#method_missing': stack level too deep (SystemStackError)
Correct approach: only call already-defined methods inside method_missing.
ok_no_loop.rb
class Fighter
def initialize(name)
@name = name
end
def method_missing(method_name, *args, &block)
# Use the already-defined puts for logging.
puts "[LOG] #{method_name} was called (undefined)"
super
end
def respond_to_missing?(method_name, include_private = false)
super
end
end
goku = Fighter.new("Son Goku")
begin
goku.fly
rescue NoMethodError => e
puts "NoMethodError: #{e.message}"
end
ruby ok_no_loop.rb [LOG] fly was called (undefined) NoMethodError: undefined method 'fly' for an instance of Fighter
If you find any errors or copyright issues, please contact us.