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 withoutrespond_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…