liveBook · Manning

Method_missing

As explained in The Well-Grounded Rubyist, Fourth Edition

method_missing is a Ruby method invoked as a last resort when an object receives a message for which no matching method is found.

Try sending an ordinary object a message it does not understand: `Object.new.blah` raises `NoMethodError`. Ruby first searches the receiver’s method-lookup path for `blah`. Only when that search fails does it call `method_missing`. Ordinary objects inherit a default implementation through `Kernel`, but you can override it to interpret an otherwise unrecognized message. section 4.3 section 4.2.1

Catch a missing message

`def o.method_missing(m, *args); puts "You can't call #{m} on this object"; end` changes what happens when `o.blah` is called. The first parameter receives the missing name as a symbol (`:blah`); `*args` collects any arguments. Define the override on one object, in its class, or in an ancestor. section 4.3

An override need not accept every unknown name. Test for the pattern you intend to support, handle matches, and call `super` for everything else. That passes the call to the next `method_missing` on the lookup path; if no other override handles it, the usual error remains available. Some string operations work on the name symbol, while others require `to_s`. section 4.3.1

Listing 4.14 Full implementation of Person.method_missing
  def Person.method_missing(m, *args)
    if m.start_with?("all_with_")                1
      attr = m[9..-1]                            2
      if Person.public_method_defined?(attr)     3
        people.find_all do |person|              4
          person.send(attr).include?(args[0])    4
        end                                      4
      else
        raise ArgumentError, "Can't find #{attr}"    5
      end
    else
      super     6
    end
  end
  end

A class-level override turns calls such as `Person.all_with_hobbies("rings")` into searches of recorded people. It accepts names beginning `all_with_`, checks that the requested attribute is public, and delegates other names with `super`. section 4.3.1

The receiver matters. To intercept `Person.all_with_hobbies`, define `Person.method_missing`: the message goes to the class object, not to an individual person. This is useful when many potential method names follow one rule, rather than when a fixed, small set of ordinary methods would suffice. section 4.3.1

`BasicObject` makes a different kind of receiver possible. With few inherited methods to collide with new names, a subclass can treat nearly every message as application-specific input. Builder uses this approach for XML: missing method names become tag names, blocks express nesting, string arguments provide element text, and hash arguments provide attributes. section 13.3.1

Listing 13.2 Lister class: Generating indented lists from a BasicObject subclass
class Lister < BasicObject
  attr_reader :list
  def initialize      1
    @list = ""
    @level = 0
  end
  def indent(string)         2
    " " * @level + string.to_s
  end
  def method_missing(m, &block)    3
    @list << indent(m) + ":"         4
    @list << "\n"
    @level += 2       5
    @list << indent(yield(self)) if block     6
    @level -= 2
    @list << "\n"
    return ""          7
  end
end

A small `BasicObject` subclass uses missing names as list headings and yields to nested blocks at a deeper indentation level. Returning an empty string prevents a nested call from appending the accumulated list to itself. section 13.3.2

A more conventional use is delegation: a cookbook can keep its own title and author while forwarding unknown messages, arguments, and blocks to its array of recipes with `public_send`. That lets calls such as `<<` and `select` operate on the collection. Dynamic handling also has an introspection consequence: if an object accepts names such as `set_age` through `method_missing`, define `respond_to_missing?` as well so method queries recognize them. section 15.1.1

How The Well-Grounded Rubyist, Fourth Edition covers it

How deep it goes
Works through override signatures, selective handling with `super`, class-level interception, and block-driven `BasicObject` behavior.
Examples
Uses an unknown `blah` call, patterned people searches, XML tags, indented lists, and a cookbook backed by an array.
What it assumes
Assumes familiarity with Ruby objects, method calls, and lookup; introduces the missing-name symbol and defers detailed block handling initially.
What you can do after
Build it from scratch: intercept patterned messages or create a `BasicObject` list builder; preserve ordinary missing-method behavior with `super`.
How it connects
Connects method lookup and `NoMethodError` with `super`, `BasicObject`, delegation, and `respond_to_missing?`.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of The Well-Grounded Rubyist, Fourth Edition. The section links point to the parts of the book it draws on.