Skip to content
method_missing
step 1/5

Reading — step 1 of 5

Learn

~1 min readMetaprogramming

method_missing is called when a method is invoked that doesn't exist. Implementing it lets you handle ANY method call — basis for ORMs, proxies, DSLs.

class DynamicHash
  def initialize(hash)
    @hash = hash
  end

  def method_missing(name, *args, &block)
    if @hash.key?(name)
      @hash[name]
    else
      super       # delegate to NoMethodError
    end
  end

  def respond_to_missing?(name, include_private = false)
    @hash.key?(name) || super
  end
end

d = DynamicHash.new(name: "Ada", age: 36)
d.name        # "Ada"
d.age         # 36
d.unknown     # NoMethodError

Always pair with respond_to_missing? — code like obj.respond_to?(:foo) should agree with obj.foo actually working.

Use cases:

  • ORM associations: user.posts → triggers query, returns AR collection
  • HTTP client builders: client.users.show(1) → /users/1
  • Configuration DSL: config.api_key → reads env var

Caveats:

  • Slower than real methods — every miss triggers method_missing
  • respond_to? won't work without respond_to_missing?
  • Stack traces become harder to debug
  • Static analysis tools struggle

Modern alternative: define methods explicitly via define_method when you can predict the set. Use method_missing only when the set is truly open-ended (e.g., proxying to another object).

Forwardable module — a focused pattern:

require 'forwardable'
class Wrapper
  extend Forwardable
  def_delegators :@inner, :size, :first, :last
end

Delegates wrapper.size to @inner.size etc. — explicit, fast, debuggable. Prefer this when you just need delegation.

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…