skip to content

In Ruby, which hook methods run when a class is subclassed, gains or loses an instance method, or gains a singleton method?

level: juniorimportance: should knowfreq 30%

answer

  1. private no-op defaults you override
  2. inherited(subclass) on the parent
  3. method_added(name) after a def
  4. method_removed vs method_undefined
  5. singleton_method_added for def self.x

basics

~10 s

Ruby calls inherited(subclass) on the parent when a class is subclassed, method_added(name) after an instance method is defined, method_removed or method_undefined after remove_method or undef_method, and singleton_method_added(name) when an object gains a singleton method.

solid answer

~40 s

These are **callbacks**: Ruby's defaults on `BasicObject`, `Module` and `Class` are private methods that do nothing, and you override them to react. `Class#inherited(subclass)` runs on the parent when a subclass is created. `Module#method_added(name)` runs on a class or module after an instance method appears there — from `def`, `define_method`, `attr_reader` or `alias_method`. `Module#method_removed(name)` follows `remove_method`, and `Module#method_undefined(name)` follows `undef_method` or `undef`. `BasicObject#singleton_method_added(name)` runs on the object that gained a singleton method, so `def self.build` in a class body calls the class's `singleton_method_added(:build)`, not `method_added`. Because they are called on the class itself, you define them as class-level methods (`def self.inherited(subclass)`) and call `super` so other hooks still run.

go deeper

for a junior

Recall the hook names and what each receives: inherited gets the subclass, the method hooks get the method name.

for a middle

Explain where each hook is defined and called, why they are class-level methods, and why super matters.

for a senior

Anticipate surprises such as singleton_method_added reporting itself and attr_accessor firing method_added twice.

for a principal

Decide when definition-time hooks are worth their global reach versus explicit registration calls.

## What a hook method is Ruby lets a class or object **react to changes in its own definition**. At certain moments the interpreter calls a method with a well-known name — a **hook** or **callback**. Ruby ships these hooks as **private methods that do nothing** on `BasicObject`, `Module` and `Class`, so defining your own version simply overrides them (one exception: `Numeric` overrides `singleton_method_added` to raise, because numbers cannot have singleton methods). The return value is ignored: hooks observe; they cannot cancel the change. ## The definition hooks | Hook | Defined on | Called on | When | Argument | |---|---|---|---|---| | `inherited` | `Class` | the parent class | a subclass is created | the new subclass | | `method_added` | `Module` | the class or module that got the method | an instance method is defined | the method name (Symbol) | | `method_removed` | `Module` | same | `remove_method` removed it from this class | the name | | `method_undefined` | `Module` | same | `undef_method` or `undef` blocked it | the name | | `singleton_method_added` | `BasicObject` | the object itself | a singleton method is defined on it | the name | | `singleton_method_removed` / `singleton_method_undefined` | `BasicObject` | the object | its singleton method was removed or undefined | the name | Hooks for mixing in modules (`included`, `extended`, `prepended`) are a separate family and not covered here. ## How to define one The hook is called **on the class**, so you define it at class level: ```ruby class ReportFormat def self.inherited(subclass) super puts "new format: #{subclass}" end def self.method_added(name) super puts "instance method: #{name}" end end ``` - Writing `def inherited(subclass)` without `self.` defines an ordinary instance method that Ruby never calls as a hook. - Call **`super`** first. Another library, or a parent class, may have its own hook in the chain, and skipping `super` silently disables it. ## What triggers each hook - `method_added` fires for every way of creating an instance method: `def`, `define_method`, `attr_reader`/`attr_accessor` (once per generated method), and `alias_method` (with the new name). - `def self.build` does **not** fire `method_added`; it defines a singleton method of the class, so the class's `singleton_method_added(:build)` runs instead. - `remove_method :x` fires `method_removed(:x)`; afterwards a superclass's `x` becomes visible again. `undef_method :x` fires `method_undefined(:x)` and blocks `x` even if a superclass defines it. - `inherited` fires for `class Csv < ReportFormat` and for `Class.new(ReportFormat)`, and it is itself inherited, so grandchildren trigger it too. ## A classic surprise Defining `singleton_method_added` on a class is itself the definition of a singleton method, so the new hook is immediately called with `:singleton_method_added`. The same happens for any later `def self.` in that class, including `def self.method_added`. A hook that logs or registers names must expect its own name, and the other hook names, in the stream. ## Common mistakes - **Forgetting `self.`** on the hook definition, so it becomes an instance method Ruby never calls. - **Expecting `inherited` to see the subclass's methods**: the subclass body has not run yet when the hook fires. - **Registering names in `method_added` without filtering**: accessors, aliases and generated methods all arrive in the same stream as hand-written ones. - **Defining hooks in a module that is mixed in** and expecting them to fire for the host class: a module's instance method `method_added` becomes a hook only when the host `extend`s the module, not when it `include`s it. ## When hooks are the right tool 1. Registering subclasses or methods automatically (plugin systems, report formats, command lists). 2. Enforcing rules at definition time, for example raising when a subclass forgets a required method (checked later, since the body has not run yet when `inherited` fires). 3. Wrapping methods as they are defined (decorator-style macros). They are global to the class and run on every matching definition, so keep them cheap and predictable.

  • Which hook runs when `def self.build` is written inside `class Report`?
    `Report.singleton_method_added(:build)`. `def self.build` defines a singleton method of the class object, so the singleton hook on that object fires; `method_added` is reserved for instance methods defined in the class.
  • What is the difference between the events behind method_removed and method_undefined?
    `remove_method` deletes the method from this class only, so a superclass's method of the same name becomes callable again, and `method_removed` fires. `undef_method` (or `undef`) records that the name is undefined here, blocking lookup even through superclasses, and `method_undefined` fires.

saying these in an interview costs you the question

  • method_added also fires for def self.x in the class body.
  • Defining def inherited without self. makes it a working hook.
  • A hook can return false to cancel the definition.
  • attr_accessor bypasses method_added because it is not a def.
  • Overriding a hook without super is harmless.