method_missing and respond_to_missing?

· 4 min read · Updated March 18, 2026 · intermediate
ruby metaprogramming dynamic-methods

method_missing is one of Ruby’s most powerful metaprogramming tools. It lets you intercept calls to methods that don’t exist, enabling dynamic interfaces, DSLs, and flexible message handling. But with great power comes responsibility—respond_to_missing? is the companion method that keeps your objects honest.

How method_missing Works

When you call a method that doesn’t exist on an object, Ruby normally raises a NoMethodError. However, if the object defines method_missing, that gets called instead, giving you a chance to handle the unknown method dynamically.

class DynamicRecorder
  def method_missing(method_name, *args, &block)
    puts "Called: #{method_name} with #{args.inspect}"
    # You can store these calls, forward them, or do anything else
  end
end

recorder = DynamicRecorder.new
recorder.foo(1, 2, 3)      # => Called: foo with [1, 2, 3]
recorder.bar                # => Called: bar with []

The first argument is the method name as a symbol, followed by any arguments, and optionally a block.

Use Cases

1. Building DSLs

method_missing shines when creating domain-specific languages:

class HtmlBuilder
  def method_missing(tag_name, *args, &block)
    content = block ? block.call : ""
    attrs = args.first || {}
    attr_string = attrs.map { |k, v| "#{k}='#{v}'" }.join(" ")
    
    "<#{tag_name} #{attr_string}>#{content}</#{tag_name}>"
  end
end

builder = HtmlBuilder.new
puts builder.div(class: "container") do
  builder.p("Hello, world!")
end
# => <div class='container'><p>Hello, world!</p></div>

2. Forwarding to Other Objects

You can use method_missing to delegate method calls:

class User
  attr_reader :name, :email
  
  def initialize(name, email)
    @name = name
    @email = email
  end
end

class UserPresenter
  def initialize(user)
    @user = user
  end
  
  def method_missing(method, *args, &block)
    @user.send(method, *args, &block)
  end
end

user = User.new("Alice", "alice@example.com")
presenter = UserPresenter.new(user)
puts presenter.name    # => Alice
puts presenter.email   # => alice@example.com

3. ActiveRecord-Like Associations

Rails uses method_missing to enable methods like user.posts without explicitly defining them:

class Post
  def self.find_by_author(author)
    # Simulated query
    ["Post by #{author}"]
  end
end

class AuthorProxy
  def method_missing(method_name, *args, &block)
    if method_name.to_s.end_with?("s")
      # Assume plural method names are collections (e.g., posts)
      model = method_name.to_s.singularize.capitalize
      puts "Looking up #{model} records..."
      Object.const_get(model).find_by_author(args.first)
    else
      super
    end
  end
end

Why respond_to_missing? Matters

When you override method_missing, the default respond_to? still returns false for methods you’re handling:

class Greeter
  def method_missing(name, *args)
    "Hello, #{name}!"
  end
end

greeter = Greeter.new
greeter.alice      # => "Hello, alice!"
greeter.respond_to?(:alice)  # => false - WRONG!

This breaks the principle of least surprise. Users expect respond_to? to match method_missing behavior. Override respond_to_missing? to fix this:

class Greeter
  def method_missing(name, *args)
    if name.to_s =~ /\A[a-z_]+\z/
      "Hello, #{name}!"
    else
      super
    end
  end
  
  def respond_to_missing?(name, include_private = false)
    name.to_s =~ /\A[a-z_]+\z/ || super
  end
end

greeter = Greeter.new
greeter.alice              # => "Hello, alice!"
greeter.respond_to?(:alice) # => true - CORRECT!

The include_private parameter defaults to false. Set it to true if you’re also handling private method calls via method_missing.

Performance Considerations

Every call to an undefined method goes through method_missing, which is slower than calling a defined method directly. If you’re building performance-critical code:

# Instead of this:
def method_missing(method, *args)
  send(method, *args)  # Can cause infinite recursion!
end

# Define respond_to_missing? to avoid infinite loops:
def respond_to_missing?(method, include_private = false)
  true
end

Common Gotchas

Infinite Recursion

Avoid calling the method inside method_missing without a guard:

# BAD - causes infinite recursion
def method_missing(method, *args)
  send(method, *args)  # Calls itself forever!
end

swallow_errors Silently

Don’t silently swallow all errors—it hides bugs:

# BAD practice
def method_missing(*)
  # Swallowing everything hides real bugs
end

Always Call super

When you can’t handle a method, call super to preserve normal error behavior:

def method_missing(method_name, *args, &block)
  if can_handle?(method_name)
    handle(method_name, *args, &block)
  else
    super  # Preserves NoMethodError for unhandled methods
  end
end

Best Practices

  1. Always define respond_to_missing? when you override method_missing
  2. Be specific about which methods you handle (use guards/patterns)
  3. Call super for methods you don’t handle
  4. Document what dynamic methods your class supports
  5. Consider define_method for frequently-called methods as an optimization

Summary

method_missing enables flexible, dynamic interfaces in Ruby—from DSLs to delegation to Rails-like associations. But with great power comes the responsibility to implement respond_to_missing?, keeping your objects’ interface consistent and predictable. Together, these methods let you build expressive APIs while maintaining Ruby’s duck typing guarantees.

Mastering these methods opens up advanced metaprogramming patterns. You’ll be able to create fluent interfaces that feel native to Ruby, build internal DSLs that read like plain English, and implement delegation patterns that would otherwise require significant boilerplate. The key is restraint: use these tools when they genuinely simplify your code, not just because they’re available.

Remember that every abstraction has a cost. Dynamic method resolution makes debugging harder, complicates static analysis, and can mask naming typos that would otherwise be caught at compile time. Use method_missing for genuine flexibility, but prefer explicit methods when the interface is stable and well-understood.

See Also