skip to content

Ghost Methods & Fallbacks

method_missing answers calls to methods that do not exist, and respond_to_missing? keeps respond_to? and method honest about them. Interviewers probe the super call, the speed cost and debugging.

on this pageshow

explore

questions

4

In Ruby, why should a class that overrides method_missing also define respond_to_missing?, and what breaks without it?

level: middleimportance: must knowfreq 55%

answer

  1. reflection reads method tables first
  2. respond_to? asks the hook
  3. method(:x) needs it too
  4. two parameters, include_private
  5. super for names you skip

basics

~20 s

respond_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 s

Ghost 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 lines
ruby
class 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        # => 8080

go deeper

for a junior

Recall that respond_to_missing? is the partner of method_missing and makes respond_to? return true for ghost methods.

for a middle

Explain that respond_to? and method both consult the hook for names outside the method tables, and why overriding respond_to? falls short.

for a senior

Show that you keep method_missing and respond_to_missing? in lock-step, with a two-parameter signature and super, and test them together.

for a principal

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
open as a page

In Ruby, how do you write method_missing for a settings object that answers any config key as a method, and why must it call super?

level: middleimportance: must knowfreq 65%

basics

~20 s

Override method_missing to return the stored value when the name is a known key, handle key= writers, and call bare super for every other name. Without super, typos return nil silently instead of raising NoMethodError.

open as a page

In Ruby, what happens when an object receives a call to a method that no class in its ancestor chain defines?

level: juniorimportance: should knowfreq 50%

basics

~20 s

After lookup fails, Ruby calls method_missing on the receiver with the name as a Symbol, the arguments and the block. BasicObject's default raises NoMethodError, or NameError when the name was a bare identifier that could have been a local variable.

open as a page

In CRuby 4.0, what does answering a hot call through method_missing cost, and how does defining the method on the first miss change that?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Each ghost call pays a failed lookup, a second dispatch to method_missing and the handler's own string checks, and YJIT does not compile such a call site. Defining a real method on the first miss makes later calls ordinary, compilable method calls.

open as a page