---
title: 'Catching Missing Methods'
source: 'https://academia.sh/en/courses/object-orientation-and-metaprogramming/catching-missing-methods'
course: 'Object Orientation and Metaprogramming'
language: en
updated: '2026-10-21T05:03:42+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Catches a message that has no definition with method_missing and forwards it to a wrapped object'
    - 'Explains why the author of a caught name is nobody while the catching method is written by the programmer'
    - 'Tells reflection about a caught name with respond_to_missing?'
    - 'Measures how a self-defining proxy turns nobody into code on the first call'
---

# Catching Missing Methods

Measuring what Ruby does when a message has no definition, how a class catches it with method_missing, and what reflection reports about the catch.

The previous lesson measured a method defined at run time with `define_method`, and its author was **code**. There every name had been defined before it was called. This lesson asks the opposite: what does Ruby do when the called name has no definition at all? The default answer is `NoMethodError`. But a class can fill this gap itself. **Catching missing methods** (`method_missing`) is handing a message that was sent to an object, and found no definition in the method lookup chain, to the object's own `method_missing` method before an error is raised. In Ruby the name of this method is code and is written as it is.

The axis is the same again: **who wrote this method?** The author in this lesson is **nobody**. There is no definition under the called name; the message still gets an answer, because another method catches it. For the `author` helper this is the last of the four categories, and the sections below show why it is used here. First the helper is given, in the same form as in the earlier lessons.

```ruby
# author.rb — a helper that classifies who wrote a method

# Classification asks in order: is there a definition in the receiver's chain at all,
# then does the receiver's own class carry it, then how was the definition produced.
def author(receiver, name)
  chain = receiver.singleton_class
  unless chain.method_defined?(name) || chain.private_method_defined?(name)
    return ["nobody", "no definition"]
  end

  definition = chain.instance_method(name)
  owner = definition.owner
  place = owner == chain ? "singleton class" : owner.to_s
  return ["ancestor", place] unless owner == receiver.class || owner == chain

  iseq = RubyVM::InstructionSequence.of(definition)
  return ["code", "#{place}, no ruby body"] if iseq.nil?
  return ["code", "#{place}, block body"] if iseq.label.start_with?("block")

  ["programmer", "#{place}, def body"]
end
```

This block is source; the file is written to disk and pulled in with `require_relative`.

## The Measured Class

The observation of this lesson is on a **proxy object**: an object that wraps another object and forwards the messages it receives to that object. `Proxy` defines none of the methods of the object it wraps.

```ruby
# authors.rb — the definition needed for this lesson's observation
require_relative "author"

class Proxy
  def initialize(target)
    @target = target
  end

  def method_missing(name, *args, &block)
    return super unless @target.respond_to?(name)

    @target.public_send(name, *args, &block)
  end

  def respond_to_missing?(name, include_private = false) = @target.respond_to?(name, include_private) || super
end
```

This block is source too. `method_missing` receives three things: the name of the message, the arguments and, if there is one, the block. The first line hands the message to Ruby's default behavior, which is `NoMethodError`, through `super` if the wrapped object does not understand it. If it does, the message is forwarded to it with the same arguments and the same block through `public_send`. `respond_to_missing?` is the second method; what it is for will be measured shortly.

## Who Wrote the Undefined Method

The observation of this lesson is the `sort` method of a `Proxy` object. The object wraps an array; in the `Proxy` class there is no definition named `sort`.

```ruby
# proxy.rb — who wrote Proxy#sort
require_relative "authors"

px = Proxy.new([3, 1, 2])
p author(px, :sort)
p px.sort
```
```
["nobody", "no definition"]
[1, 2, 3]
```

The author is **nobody**: the `author` helper found no definition for `sort` in the receiver's chain and stopped at the first question. Still, `px.sort` worked and gave `[1, 2, 3]`. So a working method exists, but it is not in the method lookup chain. What makes it work is `method_missing`; it forwards the message to the array's own `sort` method. This contradiction is the subject of the lesson: the author can be nobody and the answer still comes. **MP19.** This measurement is for a single message; the other messages of `Proxy` are measured separately below.

## Caught Messages

The proxy answers not only `sort` but any message that has no definition in its chain and is a public method of the wrapped array. The measurement below tries three messages: one without arguments, one that returns a result of another type, and one that takes a block.

```ruby
# catch.rb — the proxy catching messages
require_relative "authors"

px = Proxy.new([3, 1, 2])
p px.sort
p px.size
p px.map { |x| x * 10 }
```
```
[1, 2, 3]
3
[30, 10, 20]
```

`sort` gave the sorted array and `size` the length. `map` took a block: `method_missing` caught the block with `&block` and passed it on to `public_send`, so the array multiplied every element by ten. The proxy answered without defining any of these three messages. **MP20.** This measurement covers only three messages; array methods that have no definition in the chain pass the same way, but more than these three were not seen here, and names that do have a definition in the chain are measured separately below.

## Arguments and Block

The three messages showed half of the signature of `method_missing`. Passing arguments and the block together must be measured separately, because if `*args` and `&block` are not written, the parts of the message silently drop.

```ruby
# forward.rb — forwarding arguments and the block
require_relative "authors"

px = Proxy.new([3, 1, 2])
p px.first(2)
p px.inject(10) { |total, x| total + x }
p px.each_slice(2).to_a
```
```
[3, 1]
16
[[3, 1], [2]]
```

`first(2)` carried an argument, `inject(10) { ... }` carried both an argument and a block, and `each_slice(2)` returned an enumerator without a block, which `to_a` then unrolled. All three gave the same result as the array's own call: the proxy carried the message without breaking it. The carrying is the work of the `public_send(name, *args, &block)` line; had `*args` or `&block` been missing from that line, the argument or the block would not have reached the message in these calls and the result or the error would have changed, but this was not tried in the lesson. **MP21.** Three calls were measured; keyword arguments were not tried in this lesson.

## What Reflection Sees

The reflection lesson gave the ways of asking an object about its methods. Below, the proxy is asked four questions: `respond_to?`, `defined?`, `method(:sort).owner` and the class's `method_defined?` question.

```ruby
# query.rb — asking the proxy through reflection
require_relative "authors"

px = Proxy.new([3, 1, 2])
p px.respond_to?(:sort)
p defined?(px.sort)
p px.method(:sort).owner
p Proxy.method_defined?(:sort)
p author(px, :size)
```
```
true
"method"
Proxy
false
["nobody", "no definition"]
```

The answers look inconsistent, and the reason is `respond_to_missing?`. Since this method was defined when the proxy was written, `respond_to?` gave `true`, `defined?` returned `"method"` and `method(:sort)` produced a method object. The owner of the produced object came out `Proxy`, but when the definition table of the class was asked, `Proxy.method_defined?(:sort)` gave `false`. So `respond_to_missing?` tells reflection about the catch, but it adds no entry to the definition table. Because the helper looks at the definition table, it said **nobody** for `size` as well. Reflection looks at a method in two ways: is the message answered, and is there a definition. These two questions part ways in the proxy. **MP22.** These answers were given because `respond_to_missing?` is defined by the proxy; the result without it is measured in the section Telling Reflection.

## The Proxy Is Not an Array

The proxy wraps an array, but it is not an array. Assuming that an object that behaves like an array is an array is dangerous; the measurement below draws the limits of this.

```ruby
# identity.rb — the proxy and identity
require_relative "authors"

px = Proxy.new([3, 1, 2])
begin
  px.sortt
rescue NoMethodError => e
  puts e.class
end
p px.is_a?(Array)
p px == [3, 1, 2]
p px.class
p author(px, :class)
```
```
NoMethodError
false
false
Proxy
["ancestor", "Kernel"]
```

The misspelled `sortt` message raised `NoMethodError`: `method_missing` saw that the wrapped array did not understand the message and left it to Ruby's default through `super`. `is_a?(Array)` came out `false`; the proxy is not an array, it is an object that holds an array. `px == [3, 1, 2]` gave `false` too, because `==` is a method that has a definition in the chain and the proxy does not catch it: catching works only for names that are not found in the chain. The `class` message was not caught for the same reason and returned `Proxy`; in Ruby 4.0.7 its author is **ancestor**, `Kernel`. The messages the proxy catches are those that have no definition in the chain at all. **MP23.** Only `==`, `is_a?` and `class` were measured in this section; `to_s` is tested in a separate section.

## Names in the Chain Are Not Caught

The section The Proxy Is Not an Array showed that catching does not work for `==` and `class`. The same rule should hold for other names that every object has, such as `to_s`. It is compared with `to_a`, which is not in the chain.

```ruby
# in_chain.rb — comparing to_s and to_a
require_relative "authors"

px = Proxy.new([3, 1, 2])
p px.to_a
p px.to_s == [3, 1, 2].to_s
p author(px, :to_s)
p author(px, :to_a)
```
```
[3, 1, 2]
false
["ancestor", "Kernel"]
["nobody", "no definition"]
```

`to_a` was caught because it is not in the chain, and it gave the array. `to_s` was not caught because in Ruby 4.0.7 it comes from `Kernel`: the text of the proxy is not the text of the array, and its author is **ancestor**. The two names are equally ordinary for an array, yet for the proxy one came out **nobody** and the other **ancestor**. This difference draws the limit of the proxy: if you want to copy the behavior of the wrapped object, the names that every object already has must be handled separately. **MP24.** The measurement is for `to_s` and `to_a`; other ready-made names such as `inspect` and `hash` follow the same rule, but they were not measured.

## The Catcher's Own Author

So far we have looked at the name that is caught. The same question can be asked of the catching method itself: whose is `method_missing`?

```ruby
# catcher.rb — the authors of method_missing and respond_to_missing?
require_relative "authors"

px = Proxy.new([3, 1, 2])
p author(px, :method_missing)
p author(px, :respond_to_missing?)
p author(Object.new, :method_missing)
p BasicObject.private_method_defined?(:method_missing)
```
```
["programmer", "Proxy, def body"]
["programmer", "Proxy, def body"]
["ancestor", "BasicObject"]
true
```

The caught `sort` came out nobody; the catching `method_missing` is the **programmer**: it was written with `def` inside `Proxy`, and its owner is the receiver's own class. When the same question was asked of an ordinary `Object.new`, the author in Ruby 4.0.7 was **ancestor**, `BasicObject`, and the last line said that this definition is private. So every object already has a `method_missing` method, and its job is to raise `NoMethodError`; `Proxy` shadowed this method and put its own version in. The author nobody is therefore not a void but a handover: a name without a definition falls to a catcher that has one, and the catcher's author is known. **MP25.** The measurement was made only for `Proxy` and `Object`; which one `super` reaches when there are several `method_missing` methods in the chain was not tested in this lesson.

## Private Methods Are Not Forwarded

Because the proxy forwards the message with `public_send` and asks first with `respond_to?`, the private methods of the wrapped object cannot be called through the proxy. This is a consequence of the design and can be measured.

```ruby
# private.rb — calling a private method through the proxy
require_relative "authors"

class Box
  def shown = "shown"

  private

  def secret = "secret"
end

px = Proxy.new(Box.new)
p px.shown
begin
  px.secret
rescue NoMethodError => e
  puts e.class
end
p px.respond_to?(:secret)
p px.respond_to?(:secret, true)
```
```
"shown"
NoMethodError
false
true
```

The public `shown` method passed, and the private `secret` raised `NoMethodError`. The last two lines part ways. `respond_to?(:secret)` gave `false`, because `respond_to_missing?` passes the second argument on to the target, and by default only the public methods of the target count. `respond_to?(:secret, true)` gave `true`: when private methods are asked about too, the `Box` object recognized `secret`. Still, `px.secret` did not work. The call through reflection parts ways here: reflection may say "this name can be answered", but the `public_send` that carries the call refuses a private method all the same. The proxy carries the public face of the object it wraps, not all of it; this is sometimes the wanted result, because wrapping does not leak private details to the outside. **MP26.**

## Silent Swallowing

If the `super` call is forgotten while writing `method_missing`, every message is caught and does not turn into an error. The class below does not call `super` and returns `nil` for every message.

```ruby
# silent.rb — a method_missing that does not call super
class Silent
  def method_missing(*) = nil
end

s = Silent.new
p s.typo_name
p s.respond_to?(:typo_name)
```
```
nil
false
```

There is no method named `typo_name` anywhere, but the call returned `nil` and raised no error. The error report was our last assurance that we had spelled the name right, and here it was swallowed. For this reason, leaving a message the method does not recognize to `super` is a rule when writing `method_missing`, not a preference. Also, `respond_to?` gave `false`: this class answers the message, but does not tell reflection so. **MP27.** Since this class swallows every message, no name raises an error; in a real class the catching is tied to a condition such as a prefix or a wrapped object.

## Telling Reflection

When the catching is conditional, reflection must be informed too. The class below catches the names that begin with the `find_` prefix; it is measured first without `respond_to_missing?`, then with it.

```ruby
# finder.rb — without and with respond_to_missing?
class Finder
  def method_missing(name, *)
    return super unless name.start_with?("find_")

    name.to_s.delete_prefix("find_")
  end
end

f = Finder.new
p f.find_amy
p f.respond_to?(:find_amy)
begin
  f.method(:find_amy)
rescue NameError => e
  puts e.class
end

class Finder
  def respond_to_missing?(name, include_private = false) = name.start_with?("find_") || super
end

p f.respond_to?(:find_amy)
p f.method(:find_amy).call
```
```
"amy"
false
NameError
true
"amy"
```

The `find_amy` message gave `"amy"`, but `respond_to?` came out `false` and `method(:find_amy)` raised `NameError`. So an object that answers the message did not, according to reflection, recognize the method. When the class was reopened with `respond_to_missing?`, both questions were fixed: `respond_to?` gave `true`, and `method(:find_amy).call` gave `"amy"`. The rule is this: whoever writes `method_missing` must write `respond_to_missing?` too. Otherwise the object hides from reflection. **MP28.** The prefix condition was measured only with `find_`; the catching condition and `respond_to_missing?` were written in the same form, and if they are written separately, an inconsistency can arise.

## The Bounding Measurement

Is the author nobody permanent? The class below turns every name it catches into a real method with `define_method` on the first call. Thus the catching runs only once.

```ruby
# limit.rb — a self-defining proxy
require_relative "authors"

class Squarer
  attr_reader :misses

  def initialize
    @misses = 0
  end

  def method_missing(name, *)
    return super unless name.start_with?("square_")

    @misses += 1
    n = name.to_s.delete_prefix("square_").to_i
    self.class.define_method(name) { n * n }
    n * n
  end

  def respond_to_missing?(name, include_private = false) = name.start_with?("square_") || super
end

sq = Squarer.new
p [sq.square_4, sq.square_4, sq.square_5, sq.misses]
p author(sq, :square_4)
p author(sq, :square_9)
```
```
[16, 16, 25, 2]
["code", "Squarer, block body"]
["nobody", "no definition"]
```

`square_4` was called twice, but `method_missing` ran only once: the method was defined on the first call, and the second call reached the definition directly. `misses` became two, because `square_4` and `square_5` were each caught once. Then `author` classified `square_4` as **code**; the method is now a real definition produced from a block. `square_9`, which was never called, is still **nobody**. So the author nobody is not permanent: the same name can turn into code after one call. The category is a property of the definition table at that moment, not of the name. **MP29.** A single class was measured; calling `define_method` through `self.class` is a design choice, and defining a method for a single instance only is possible too.

## Summary

- A message with no definition in the chain is handed to the object's `method_missing` method; `Proxy` forwards the message that reaches the array it wraps in this way.
- The author of caught names is nobody: the `author` helper finds no name in the definition table, but the message is still answered; the catching `method_missing` is the programmer and shadows the private default in `BasicObject`.
- `respond_to_missing?` tells reflection about the catch; `Proxy.method_defined?` still stays `false`.
- A `method_missing` without a `super` call swallows every message and hides typos.
- The proxy does not take the place of the type it wraps: `is_a?(Array)`, `==` and `to_s` are definitions in the chain and are not caught; private methods are not forwarded with `public_send` either.
- The author nobody is not permanent: in a proxy that defines itself with `define_method` on the first call, the same name turns into code.

## Next Step

The last observation of this lesson showed that a name can be nobody first and code afterward. The closing lesson of the course builds this transition on purpose: a single declaration line such as `field :price, 0` gives birth to methods behind it. Domain-Specific Languages asks which author this declaration hides.
