liveBook · Manning

Method lookup

As explained in The Well-Grounded Rubyist, Fourth Edition

A method lookup path is the ordered sequence of singleton classes, classes, and modules Ruby searches when resolving a method for an object.

Suppose `D` inherits from `C`, `C` includes module `M`, and only `M` defines `report`. Calling `D.new.report` works: Ruby searches `D`, then `C`, then `M`, where it finds the method. The object does not store a copy of `report`; it follows a path through classes and modules that hold method definitions. The search stops at the first match. section 4.2.1 section 4.2

Listing 4.6 Demonstration of module inclusion and inheritance
module M
  def report
    puts "'report' method in module M"
  end
end
class C
  include M
end
class D < C
end
obj = D.new
obj.report

Run this small example, then add a `report` definition to `C` or `D` to see which definition wins. section 4.2.1

For an ordinary instance, the search order follows these rules:

  • Search modules prepended to its class, most recently prepended first; then search the class itself. section 4.2.5
  • Search modules included in the class, most recently included first. Repeat the prepend–class–include sequence for each superclass, continuing through `Object`, its `Kernel` mixin, and `BasicObject`. section 4.2.5
  • Use the first matching definition. Defining a method again *within the same class or module* replaces its earlier definition; defining the same name at different points on the path leaves multiple definitions, of which the first is called. section 4.2.2

That order makes `include` and `prepend` importantly different. A class method takes precedence over a method in a module it includes; a prepended module takes precedence even over the class. When two included modules define the same name, the later inclusion is searched first. Including the same module twice in a class does not add it twice, but prepending an already included module can place it before the class as well. section 4.2.2 section 4.2.4

There is another stop before the object's ordinary class: its singleton class. A method defined for just that object lives there, and modules included in that singleton class are searched before the ordinary class. `extend` adds a module at this object-specific level. Consequently, an object can acquire an overriding method without changing other instances. section 4.2.5 section 13.1.3

Continue past the first match

If `C#report` overrides `M#report`, a call to `super` inside `C#report` invokes the next `report` on the path: `M#report`. This lets an override wrap or supplement existing behavior. Bare `super` forwards the current arguments; `super()` forwards none. The same pattern lets a subclass adjust its initialization while retaining its superclass's setup. section 4.2.6

To investigate a surprising result, inspect a class's `ancestors` for its class-and-module order, or inspect an individual object's `singleton_class.ancestors` to include its singleton level. `obj.method(:name)` identifies the selected definition; its `super_method` finds the next one, returning `nil` when none remains. `obj.methods` answers a different question: it lists callable methods but flattens away where they came from. If lookup finds no matching method, `method_missing` handles the unmatched message; a custom implementation can use `super` to pass unhandled names onward. section 13.1.3 section 4.2.7 section 7.7.1 section 4.3.1

How The Well-Grounded Rubyist, Fourth Edition covers it

How deep it goes
Works through search order, overrides, singleton classes, `super`, and inspection; also traces failed searches to `method_missing`.
Examples
Uses a subclass calling a method from an included module, competing definitions, and `super` wrapping an overridden method.
What it assumes
Assumes familiarity with objects, classes, superclasses, and messages; develops how included modules and singleton classes enter the search.
What you can do after
Diagnose/debug with `ancestors`, `method`, and `super_method` to locate the selected method and subsequent definitions.
How it connects
Connects module inclusion, prepending, inheritance, singleton classes, `super`, and `method_missing` through one search path.

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.