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
