In Ruby, why should a class that overrides method_missing also define respond_to_missing?, and what breaks without it?
answer
- reflection reads method tables first
- respond_to? asks the hook
- method(:x) needs it too
- two parameters, include_private
- super for names you skip
basics
~20 srespond_to? and method consult respond_to_missing? for names not in any method table. Without it, a ghost method works when called but respond_to? says false and method(:name) raises NameError, so code that checks before calling skips a working object.
solid answer
~30 sGhost methods answered by `method_missing` are not in any method table, so by default `respond_to?(:port)` returns `false` and `method(:port)` raises `NameError`, even though `settings.port` works. Ruby fixes this through a hook: when a name is not found, both `respond_to?` and `method` call `respond_to_missing?(name, include_private)`. Returning `true` there makes `respond_to?` agree and lets `method` build a `Method` whose `call` goes through `method_missing`. Define it with two parameters, `include_private = false`, match exactly the names `method_missing` handles, and fall back to `super`. Overriding `respond_to?` directly is the old approach and still leaves `method(:port)` broken.
code
ruby · 15 linesclass Settings
def initialize(values) = @values = values
def method_missing(name, *args)
@values.key?(name) && args.empty? ? @values[name] : super
end
def respond_to_missing?(name, include_private = false)
@values.key?(name) || super
end
end
s = Settings.new(port: 8080)
s.respond_to?(:port) # => true
s.method(:port).call # => 8080go deeper
Recall that respond_to_missing? is the partner of method_missing and makes respond_to? return true for ghost methods.
Explain that respond_to? and method both consult the hook for names outside the method tables, and why overriding respond_to? falls short.
Show that you keep method_missing and respond_to_missing? in lock-step, with a two-parameter signature and super, and test them together.
Treat reflection honesty as part of an object's contract, since serializers, delegators and tooling all rely on it.
## The problem it solves A class that answers names through `method_missing` has methods that work but do not exist in any method table. Ruby's reflective methods consult method tables first, so without help they tell the truth about the tables and lie about the object: ```ruby settings.port # => 8080 (via method_missing) settings.respond_to?(:port) # => false settings.method(:port) # NameError: undefined method 'port' for class 'Settings' ``` Any code that checks before calling, such as a serializer that asks `respond_to?(:to_h)`, a delegation helper, or a `&settings.method(:port)` conversion, then treats a working object as if it lacked the method. ## The hook `respond_to_missing?(name, include_private)` is a hook that Ruby calls when a name is **not** found in the method tables: - **`respond_to?(name, include_all = false)`** first checks the tables. If the name is not there, it calls `respond_to_missing?(name, include_all)` and returns that result. - **`method(name)`** (and `public_method`) does the same. If `respond_to_missing?` returns true, Ruby builds a `Method` object whose `call` goes through `method_missing`. The default on `Object` returns `false`. The core documentation marks it "DO NOT USE THIS DIRECTLY": you override it, but callers use `respond_to?`. ```ruby class Settings def respond_to_missing?(name, include_private = false) @values.key?(name.to_s) || super end end settings.respond_to?(:port) # => true settings.method(:port).call # => 8080 ``` ## Getting the signature right 1. **Two parameters.** Ruby always passes both the name and the `include_private` flag. A one-parameter definition makes every `respond_to?` call on an unknown name raise `ArgumentError`. Give the second a default, `include_private = false`, so direct calls in tests also work. 2. **Call `super`** for names you do not handle, so an ancestor's answer (for example from an included module) is kept. 3. **Match `method_missing` exactly.** If `method_missing` handles `port=` too, `respond_to_missing?` must say yes for `port=`. A mismatch in either direction is a bug: callers are told a method exists that raises, or the reverse. 4. **Stay cheap and side-effect free.** It may run often, during serialization, delegation or debugging. ## Why not override respond_to? instead Older code overrode `respond_to?` itself. That fixes only one of the reflective paths: `method(:port)` does not consult a user-defined `respond_to?`, so it still raises `NameError`. Overriding `respond_to_missing?` fixes both at once and leaves `respond_to?`'s own table lookup, visibility handling and argument parsing untouched. | Override | `respond_to?(:port)` | `method(:port)` | |---|---|---| | neither | `false` | `NameError` | | `respond_to?` only | `true` | `NameError` | | `respond_to_missing?` | `true` | a working `Method` | ## Visibility and the second argument `include_private` is `true` when the caller passed `true` to `respond_to?`, meaning "count private methods too". Most ghost methods are meant to be public, so the flag is usually ignored; a class that answers some names only internally can return `true` for those only when the flag is set. ## Delegating wrappers The same pairing matters for objects that forward every unknown call to a wrapped target. Their `respond_to_missing?` usually asks the target: `@target.respond_to?(name, include_private) || super`. Without it, a wrapper around an Array would answer `each` through `method_missing` yet claim it cannot, and code that checks `respond_to?(:each)` would refuse the wrapper even though every call would work. ## Common mistakes - **Forgetting it entirely**, so duck-typed callers skip a perfectly working object. - **One parameter**, so `respond_to?` raises instead of answering. - **Returning true for everything**, so every name looks callable while `method_missing` still raises for most of them. - **Doing I/O inside it**, such as reloading the settings file, which makes a query method slow and surprising. ## What an interviewer is listening for The key sentence: `respond_to_missing?` is what `respond_to?` and `method` call for names not in the method tables, so defining it keeps reflection consistent with `method_missing`. Strong answers add the two-argument signature, `super`, and why overriding `respond_to?` directly falls short.
- What happens if respond_to_missing? is defined with only one parameter?Ruby always calls it with two arguments, the name and the include_private flag, so every `respond_to?` or `method` call on a name outside the method tables raises `ArgumentError` for the wrong number of arguments. Define it as `respond_to_missing?(name, include_private = false)`.
- When respond_to_missing? returns true, what does calling the Method object from method(:port) actually do?Ruby builds a special `Method` with no real body. Calling it sends the name and arguments to `method_missing` on the receiver, so the result is whatever `method_missing` returns for `port`. It lets ghost methods be passed around as callables, for example with `&settings.method(:port)`.
saying these in an interview costs you the question
- overriding respond_to? also makes method(:name) work
- respond_to_missing? takes just the method name
- respond_to? calls method_missing to find out
- returning true for every name is a safe default
- respond_to_missing? should be called directly by client code