---
title: 'Object Orientation and Metaprogramming'
source: 'https://academia.sh/en/courses/object-orientation-and-metaprogramming'
language: en
updated: '2026-10-21T05:03:41+00:00'
license: 'CC BY-SA 4.0'
lessons: 9
outcomes:
    - 'Organizes state and behavior with classes, inheritance and super'
    - 'Shares behavior through modules and mixins'
    - 'Explains the method lookup chain and measures where in the chain a method is defined'
    - 'Distinguishes whether a method was written by the programmer, by an ancestor or by code that generates it, or has no definition at all'
    - 'Uses metaprogramming tools in a measured and readable way'
---

# Object Orientation and Metaprogramming

In Ruby, account.balance_text, point.x, settings.color and proxy.sort are called the same way, yet the four bodies come from four different routes: the programmer wrote a def in the receiver's class, the definition sits in an ancestor along the chain, a piece of code produced the definition at run time, or there is no definition at all and method_missing catches the message. The course measures every mechanism with a single question — who wrote this method? Classes, inheritance, modules, visibility, the method lookup chain, reflection, dynamic method definition, proxy objects and domain-specific languages all pass through the same frame.

## Classes and Modules

Instance variables living in the object and methods in the class, inheritance and reaching the superclass with super, sharing behavior without multiple inheritance through modules, public, protected and private methods, and how the ancestor chain is resolved.

### Class Definition and Instance Variables

Source: https://academia.sh/en/courses/object-orientation-and-metaprogramming/class-definition-and-instance-variables

Classifying who wrote a method with four authors; instance variables living in the object and methods in the class; initialize being private; how a method written with attr_reader differs from one written with def.

The previous course measured where a message finds its answer: in the receiver's class, in a mixed-in module, in a given block, or in the syntax itself (M13/K01). It closed with one question left open. `account.balance_text`, `point.x`, `settings.color` and `proxy.sort` are called in the same way, yet the bodies of the four come from four different routes. It is known that a method lives in a particular owner; it is not known who wrote that definition.

This course measures that question. The first lesson takes the smallest form of a class: where do instance variables live, where do methods live, and how is the author of a method classified?

#### Four Authors and the Measurement Core

Every lesson of the course asks the same question: who wrote this method? The answer is one of four categories. The **programmer** is the person who wrote the body with `def` in the receiver's own class. An **ancestor** left the definition higher in the chain, in a superclass or a module. **Code** is the machinery that produces a definition at run time through a call: `attr_reader`, `define_method`, `Struct`. **Nobody** is the case where the chain holds no definition at all and the message is caught by `method_missing`. In the output these four names are written in ASCII: `programmer`, `ancestor`, `code`, `nobody`.

**CM1.** In this course the "author" is the category that says where the definition of a method came from. What makes the classification is a rule, not a label: if the receiver's singleton class has no definition, the author is nobody; if the owner of the definition is neither the receiver's class nor its singleton class, the author is an ancestor; if the `iseq` of the definition is `nil` or its label starts with `block`, the author is code; otherwise the author is the programmer. The rule reads the form of a definition, not its intent. This sentence is the common face of several bounding measurements in the course.

The rule lives in the file `author.rb`. It is not rewritten during the course; every lesson loads it in the same form. The order inside the file matters. First it asks whether the definition exists, because the owner of a method with no definition cannot be asked. Then it asks for the owner, because a definition whose owner is not the receiver's class has already come from somewhere else. Last it looks at how the definition was produced: only for definitions that sit in the receiver's own class is it necessary to separate a body written with `def` from the product of a call.

```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
```

`author(receiver, name)` returns an array of two values: the name of the author and the evidence. The evidence states the owner of the definition and how the body was written. The second file, which runs nine observations together, is `authors.rb`. Each observation is a method called in the same way; the difference lies only behind the call.

```ruby
# authors.rb — nine observations: who wrote this method
require_relative "author"

module Reportable
  def report = "report: #{balance_text}"
end

class Account
  include Reportable

  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def balance_text = "#{@owner} #{@balance}"
  def net = @balance - fee

  private

  def fee = 2
end

class TermDeposit < Account
end

module Lock
  def unlock = "lock opened, #{super}"
end

class Safe
  prepend Lock
  def unlock = "safe opened"
end

Point = Struct.new(:x, :y)

class Settings
  %i[color width].each do |name|
    define_method(name) { "#{name} default" }
  end
end

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

class Model
  def self.field(name, default)
    define_method(name) { instance_variable_get("@#{name}") || default }
  end
end

class Product < Model
  field :name, "unnamed"
  field :price, 0
end

account = Account.new("amy", 100)
observations = {
  "state reading" => [account, :balance_text, -> { account.balance_text }],
  "superclass"    => [TermDeposit.new("tom", 50), :balance_text, -> { TermDeposit.new("tom", 50).balance_text }],
  "mixin"         => [account, :report, -> { account.report }],
  "private helper" => [account, :fee, -> { account.net }],
  "prepended"     => [Safe.new, :unlock, -> { Safe.new.unlock }],
  "member reader" => [Point.new(3, 4), :x, -> { Point.new(3, 4).x }],
  "generated"     => [Settings.new, :color, -> { Settings.new.color }],
  "proxy"         => [Proxy.new([3, 1, 2]), :sort, -> { Proxy.new([3, 1, 2]).sort }],
  "declaration"   => [Product.new, :price, -> { Product.new.price }],
}

tally = { "programmer" => 0, "ancestor" => 0, "code" => 0, "nobody" => 0 }

printf("%-15s%-11s%-27s%s\n", "observation", "author", "evidence", "result")
observations.each do |name, (receiver, message, call)|
  kind, evidence = author(receiver, message)
  tally[kind] += 1
  printf("%-15s%-11s%-27s%s\n", name, kind, evidence, call.call.inspect)
end

puts
tally.each { |kind, n| printf("%-11s%d\n", kind, n) }
puts "total observations: #{tally.values.sum}"
```

```
observation    author     evidence                   result
state reading  programmer Account, def body          "amy 100"
superclass     ancestor   Account                    "tom 50"
mixin          ancestor   Reportable                 "report: amy 100"
private helper programmer Account, def body          98
prepended      ancestor   Lock                       "lock opened, safe opened"
member reader  code       Point, no ruby body        3
generated      code       Settings, block body       "color default"
proxy          nobody     no definition              [1, 2, 3]
declaration    code       Product, block body        0

programmer 2
ancestor   3
code       3
nobody     1
total observations: 9
```

Of the nine observations, a programmer wrote 2 and an ancestor wrote 3; code produced 3; in 1 there was no definition and `method_missing` caught the message. These numbers do not change during the course. The programmer wrote the `state reading` and `private helper` rows: the bodies of both sit inside `Account` as a `def`. The ancestor wrote the `superclass`, `mixin` and `prepended` rows; the three definitions sit away from the receiver, in `Account`, in `Reportable` and in `Lock`. In the `member reader`, `generated` and `declaration` rows a call produced the definition. In the `proxy` row there is no definition and the result still came back.

The share of this lesson is the first programmer row: `state reading`. The following lessons open the ancestor, code and nobody authors in turn; this lesson measures the simplest structure a programmer writes, a class and its instance variables.

#### State in the Object, Behavior in the Class

The `initialize` method of the `Account` class takes two values and assigns both to instance variables. An instance variable is a variable that starts with `@` and is bound to an object (M13/K01). The file below redefines a small slice of the class, because loading `authors.rb` prints the table of nine rows; this slice runs without repeating it.

```ruby
# state.rb — instance variables live in the object
class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end
end

account = Account.new("amy", 100)
p account.instance_variables
p account.instance_variable_defined?(:@balance)
p Account.instance_methods(false)
begin
  account.balance
rescue NoMethodError => e
  puts e.class
end
```

```
[:@owner, :@balance]
true
[]
NoMethodError
```

`instance_variables` listed the two variables in the order of assignment: `@owner` and `@balance`. The variables live not in the `Account` class but in the `account` object. `Account.instance_methods(false)`, on the other hand, returned an empty array: the class has no public method. `initialize` is private, and the `false` argument asks only for the class's own definitions. That is why the call `account.balance` raised `NoMethodError`: the variable is inside the object, but because no method was written to read it, it is not visible from outside.

**CM2.** In this lesson an "instance variable" is a variable that is bound to an object and starts with `@`. That a variable exists in an object does not mean that a method reading it exists. State lives in the object and behavior lives in the class; the two sides are tied together through a name such as `@balance`.

An instance variable does not come into being in the object at all until it is assigned. The file below measures this: the variable that is read has not been assigned yet.

```ruby
# unassigned.rb — an unassigned instance variable reads as nil and is not listed
class Account2
  def read_value = @missing
  def write_value(value) = @missing = value
end

object = Account2.new
p object.read_value
p object.instance_variables
object.write_value(5)
p object.read_value
p object.instance_variables
```

```
nil
[]
5
[:@missing]
```

The unassigned variable was read as `nil`, but it did not enter the `instance_variables` list. After the assignment, the variable was both read and listed. Reading creates nothing in the object; only assignment does. This behavior also creates a difference among the objects of a class such as `Account2`: an object on which `write_value` has been called carries the variable `@missing`, and an object on which it has not been called does not. Even when two objects come from the same class, their lists of instance variables need not be the same; the list reflects the history of the object, not of the class. A read that raises no error can for this reason hide a typing mistake: if `@balanse` is written, Ruby quietly gives `nil`.

#### Two Objects, One Class

That state lives in the object and behavior in the class is measured with two objects. The file below produces two accounts from the same class and compares two things: the owner of the method and the names of the instance variables.

```ruby
# two.rb — two objects share the same method, they do not share the values
class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def balance_text = "#{@owner} #{@balance}"
end

a = Account.new("amy", 100)
b = Account.new("tom", 50)
puts a.balance_text
puts b.balance_text
p a.method(:balance_text).owner == b.method(:balance_text).owner
p a.instance_variables == b.instance_variables
p Account.instance_of?(Class)
p a.class
```

```
amy 100
tom 50
true
true
true
Account
```

The same method gave two separate results on two objects: `amy 100` and `tom 50`. The owner of the method is the same for both objects, and the names of their instance variables are the same too; only the values differ. The method body is written once, in the class, and on every call it reads the variables of the object that receives the call. `Account.instance_of?(Class)` gave `true`: the line `class Account` sets up a constant named `Account`, and what the constant points to is an object of type `Class`. `a.class` returns this object. A class is the place where methods live, and it is also an object that produces objects.

This distinction has a practical consequence. The number of methods does not grow with the number of objects: when a thousand accounts are created, `balance_text` still sits in a single place, once. What multiplies per object is only the values of the instance variables. Asking for the author of a method therefore always points at the class or at the chain; the object itself carries only state, and the measurement that asks for the author does not look at its variables.

#### initialize Is Private and Checks the Arity

The call `Account.new("amy", 100)` produces a new object and calls the `initialize` method on this object. It is `new` that calls `initialize`, not code outside. The class keeps it as a private method; code cannot call it by writing `account.initialize(...)`. The file below uses the full definition of `Account` from the core, because the private method `fee` is measured here as well.

```ruby
# private_init.rb — initialize is a private method
require_relative "author"

class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def balance_text = "#{@owner} #{@balance}"
  def net = @balance - fee

  private

  def fee = 2
end

account = Account.new("amy", 100)
p account.respond_to?(:initialize)
p Account.private_instance_methods(false).sort
p author(account, :initialize)
begin
  Account.new("a")
rescue ArgumentError => e
  puts e.class
end
```

```
false
[:fee, :initialize]
["programmer", "Account, def body"]
ArgumentError
```

`respond_to?(:initialize)` gave `false`: a private method is not visible when asked from outside. `private_instance_methods(false)` listed the two private methods of the class: `fee` and `initialize`. For `initialize`, `author` found the programmer, because a `def` in the class wrote the body. When `Account.new("a")` was called with a single argument it raised `ArgumentError`: `initialize` expects two arguments, and Ruby checks the count at call time.

**CM3.** In this lesson the "arity" is the number of arguments a method accepts; `Account.new` takes this number from the `initialize` definition. `initialize(owner, balance)` asks for two arguments; that is why `Account.new("a")` raised `ArgumentError` and returned no object. Ruby checks the number before the body runs, so the `@owner` assignment did not happen either. The arity thus also bounds the first state of the object: a constructor that sets up two instance variables does not run without two values. A variable that is not assigned inside `initialize`, and not assigned later by another method, does not exist in that object at all; the constructor of the class is therefore the place where the first state of the object is set up and the most reliable point for seeing the variable names together.

Reopening a class means writing one more `class` block with the same class name. The existing definition is not deleted; the new one is added (M12/K03). The definition of a class therefore does not have to sit in a single place; the measurement below reopens the class.

#### The Bounding Measurement

Two authors can live together in the same class, in the same owner. `Account` is reopened and `attr_reader :owner` is added. `attr_reader` generates a reader method: the `owner` method returns `@owner`. In the earlier courses (M08/K01) it was measured what the compiler adds to a class. Here the question is different: of the two methods sitting in the class, one was written by a `def` and the other was produced by a call.

```ruby
# reopened.rb — two authors in the same class: def and attr_reader
require_relative "author"

class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def balance_text = "#{@owner} #{@balance}"
  def net = @balance - 2
end

class Account
  attr_reader :owner
end

account = Account.new("amy", 100)
p author(account, :owner)
p author(account, :balance_text)
p Account.instance_methods(false).sort
[:owner, :balance_text].each do |name|
  definition = Account.instance_method(name)
  puts "#{name} #{definition.owner} #{definition.source_location.nil?} #{RubyVM::InstructionSequence.of(definition).nil?}"
end
```

```
["code", "Account, no ruby body"]
["programmer", "Account, def body"]
[:balance_text, :net, :owner]
owner Account false true
balance_text Account false false
```

`author(account, :owner)` found code, and `author(account, :balance_text)` found the programmer. The owner of both is `Account`, and both appear in the `instance_methods(false)` list: `[:balance_text, :net, :owner]`. Knowing the owner does not tell the author. The last loop shows where the distinction comes from. `source_location` is not `nil` for either method: the line of the `attr_reader` call is recorded as its source location. The distinction is made by `iseq`. For `balance_text`, written with `def`, there is an `iseq`; for `owner`, generated with `attr_reader`, it is `nil`. The rule therefore looks at `iseq`, not at the source location.

This measurement shows that the owner does not determine the author: two methods sat in the same owner and came out with two different authors. The same measurement also shows that a class is not an identity card. `Account` does not carry a single author; the author of each method is measured separately. At the class level there is no category that says "the programmer wrote this class".

#### Summary

- The measure of the course is who wrote a method: the author may be the programmer, an ancestor, code or nobody, and the decision is made by the rule in `author.rb`.
- Of the nine observations, a programmer wrote 2 and an ancestor wrote 3; code produced 3; in 1 there was no definition and `method_missing` caught the message.
- Instance variables live in the object and methods live in the class; if there is no reader, `account.balance` raises `NoMethodError`, and an unassigned variable reads as `nil` and does not enter `instance_variables`.
- `initialize` is a private method and checks the arity; `Account.new("a")` raises `ArgumentError`.
- The author of this lesson is the programmer: in the `state reading` row, a `def` inside `Account` wrote `balance_text`.
- In the same class, a method written with `def` comes out as the programmer and a method generated with `attr_reader` comes out as code; the owner of the two is the same, and `iseq` makes the distinction.

#### Next Step

`balance_text` is written in `Account`, yet an empty subclass named `TermDeposit` can also call the same method and get an answer. There is not a single line of definition in the subclass. Who brings the method to the subclass, and what changes if the same method is rewritten in the subclass? The next lesson, Inheritance and `super`, measures this.

### Inheritance and `super`

Source: https://academia.sh/en/courses/object-orientation-and-metaprogramming/inheritance-and-super

An empty subclass taking a method from its superclass; the ancestor chain with superclass and ancestors; super in an overriding method; super inside initialize; the difference between bare super and super(...).

The previous lesson said that `balance_text` is written in the `Account` class, yet an empty subclass named `TermDeposit` can also call the same method and get an answer. There is not a single line of definition in the subclass. Who brought the method there, and what changes when the same method is rewritten in the subclass?

The author of this lesson is the ancestor: the definition sits away from the receiver, in a superclass higher in the chain. The lesson first measures an empty subclass, then looks at how overriding with `super` makes a definition change hands.

#### Where an Empty Subclass Gets Its Method

The line `class TermDeposit < Account` makes `Account` the superclass of `TermDeposit`. The methods of the superclass are open to the objects of the subclass. This relation is called inheritance (M08/K02). The file below rebuilds the `superclass` observation of the core for `TermDeposit`. The file `author.rb` is the rule given in the first lesson and is loaded unchanged.

```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
```

`author.rb` adds nothing new in this lesson; it stands as the source. The measurement is in the file that calls it.

```ruby
# superclass.rb — an empty subclass takes the method from an ancestor
require_relative "author"

class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def balance_text = "#{@owner} #{@balance}"
end

class TermDeposit < Account
end

term = TermDeposit.new("tom", 50)
p term.balance_text
p author(term, :balance_text)
p TermDeposit.instance_methods(false)
```

```
"tom 50"
["ancestor", "Account"]
[]
```

`term.balance_text` gave `tom 50`. `author` found the ancestor author, with `Account` as the evidence: the owner of the definition is not `TermDeposit`, the receiver's class, but `Account`. `TermDeposit.instance_methods(false)` returned an empty array, so `TermDeposit` carries no method of its own. The call `TermDeposit.new("tom", 50)` worked too, because `initialize` also came from `Account` by the same route. Even when the subclass is empty, the object carries the complete behavior of an `Account`.

**CM4.** In this lesson the "superclass" is the class written to the right of the `<` sign in the line `class TermDeposit < Account`; the "subclass" is the one written on the left. An "ancestor" is every class or module that comes after a class in its chain. The rule counts the author as an ancestor when the owner of the method is not the receiver's class.

#### The Ancestor Chain

A superclass points at a single class. But `Account` has a superclass as well, and the chain continues from there. The file below asks the chain in two ways: `superclass` gives one step, `ancestors` gives the whole order.

```ruby
# chain.rb — superclass and ancestors
module Reportable
  def report = "report: #{balance_text}"
end

class Account
  include Reportable
end

class TermDeposit < Account
end

p TermDeposit.superclass
p Account.superclass
p TermDeposit.ancestors.take(5)
p Integer.ancestors.take(4)
```

```
Account
Object
[TermDeposit, Account, Reportable, Object, Kernel]
[Integer, Numeric, Comparable, Object]
```

`TermDeposit.superclass` gave `Account`, and `Account.superclass` gave `Object`. The `ancestors` list carries classes and modules together: the receiver's class at the very front, then `Account`, then the `Reportable` module that `Account` mixed in, then `Object` and `Kernel`. The place of modules in the chain is the subject of the next lesson; here it is enough to notice that `Reportable` stands between `Account` and `Object`. The core classes have a chain in the same way: `Integer` links first to `Numeric`, then to the `Comparable` module, then to `Object`. So the `between?` in the call `1.between?(0, 2)` is also a method written not by `Integer` but by an ancestor in the chain.

#### super in an Overriding Method

A subclass can rewrite a method of the superclass under the same name. This operation is called overriding. Overriding does not delete the definition of the superclass; because the subclass comes first in the receiver's chain, the message reaches it first. `super`, in turn, descends from this definition to the next definition of the same name in the chain. The file below rewrites `balance_text` in the class `TermDeposit2` and leaves half of the body to `super`.

```ruby
# overriding.rb — super descends to the next definition in the chain
class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def balance_text = "#{@owner} #{@balance}"
end

class TermDeposit2 < Account
  def balance_text = "term #{super}"
end

t2 = TermDeposit2.new("tom", 50)
puts t2.balance_text
p t2.method(:balance_text).owner
p t2.method(:balance_text).super_method.owner
p t2.method(:balance_text).super_method.super_method
```

```
term tom 50
TermDeposit2
Account
nil
```

The output `term tom 50` is the combination of two parts: `TermDeposit2` wrote the prefix `term `, and `Account` wrote the `tom 50` part through the `super` call. `method(:balance_text).owner` gave `TermDeposit2`, and `super_method.owner` gave `Account`. `super_method` gives the next definition in the chain as a method object. Because no ancestor that defines `balance_text` remained after `Account`, `super_method.super_method` returned `nil`. So a `super` call is a hop that keeps walking down the chain, but only as long as there is a definition of the same name.

#### Telling the Owner from the Copy

`TermDeposit.instance_methods(false)` came out empty, yet `TermDeposit` still answers `balance_text`. Two questions have to be kept apart: is the method written in this class, or can it be called by the objects of this class? `instance_methods(false)` asks only the first. `method_defined?` asks the second, and `instance_method(...).owner` says where the definition actually sits. The file below puts the three questions to the same class, and then to the overriding `TermDeposit2`.

```ruby
# owner_question.rb — being written and being callable are separate questions
class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def balance_text = "#{@owner} #{@balance}"
end

class TermDeposit < Account
end

class TermDeposit2 < Account
  def balance_text = "term #{super}"
end

p TermDeposit.method_defined?(:balance_text)
p TermDeposit.instance_methods(false)
p TermDeposit.instance_method(:balance_text).owner
p TermDeposit2.instance_methods(false)
p TermDeposit2.instance_method(:balance_text).owner
p Account.instance_method(:balance_text).owner
```

```
true
[]
Account
[:balance_text]
TermDeposit2
Account
```

`TermDeposit.method_defined?(:balance_text)` gave true, while `TermDeposit.instance_methods(false)` stayed empty; the two do not contradict. The first looks at the whole chain, the second only at the body of the class. `instance_method(...).owner` gave `Account` for `TermDeposit`: the definition that runs when the objects of `TermDeposit` call it is written in `Account`, and there is no copy in `TermDeposit`. For `TermDeposit2` the picture changes: `instance_methods(false)` now carries `balance_text`, and the `owner` is `TermDeposit2`. The definition of `Account` stays in place and still comes out with `Account` as its owner. Overriding is not an overwrite; it puts a second definition at the front of the chain.

#### super Inside initialize

`initialize` is also a method and can be overridden in a subclass. While the subclass sets up its own extra variable, it asks with `super` for the superclass to set up its variables. The file below defines two subclasses; the `initialize` methods of both carry the same signature, but `super` is written differently.

```ruby
# initialize_super.rb — super(...) and bare super
class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def balance_text = "#{@owner} #{@balance}"
end

class Interest < Account
  def initialize(owner, balance, rate)
    super(owner, balance)
    @rate = rate
  end
end

class Faulty < Account
  def initialize(owner, balance, rate)
    super
    @rate = rate
  end
end

p Interest.new("amy", 100, 5).instance_variables
begin
  Faulty.new("amy", 100, 5)
rescue ArgumentError => e
  puts e.class
end
```

```
[:@owner, :@balance, :@rate]
ArgumentError
```

`Interest` passed two arguments to the superclass with `super(owner, balance)`, and `instance_variables` listed three variables in order: the two variables of the superclass, then the `@rate` of the subclass. In the `Faulty` class a bare `super` was written and an `ArgumentError` arose. A bare `super` passes on all the arguments of the calling method unchanged; because `Faulty#initialize` took three arguments, three arguments went to `Account#initialize`, which expects two.

**CM5.** In this lesson `super(...)` means the call with parentheses, and "bare `super`" means the call written without parentheses and without arguments. The call with parentheses passes to the superclass only the arguments that are written; the bare call passes the method's own arguments as they are. When the signature of the subclass differs from that of the superclass, a bare `super` may pass the wrong arguments; in that case the call with parentheses is written.

The difference between the forms with and without parentheses is completed by a third spelling: `super()` with empty parentheses. A bare `super` passes the method's own arguments, while `super()` passes no argument at all. The difference becomes visible only when the method in the superclass has a parameter with a default value. The file below puts the three forms side by side in three subclasses.

```ruby
# super_forms.rb — bare super, super() and super(argument)
class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def label(prefix = "account") = "#{prefix}: #{@owner}"
end

class Bare < Account
  def label(prefix = "bare") = super
end

class Empty < Account
  def label(prefix = "empty") = super()
end

class Explicit < Account
  def label(prefix = "explicit") = super("fixed")
end

puts Bare.new("tom", 1).label
puts Empty.new("tom", 1).label
puts Explicit.new("tom", 1).label
puts Bare.new("tom", 1).label("manual")
```

```
bare: tom
account: tom
fixed: tom
manual: tom
```

The bare `super` carried the `prefix` parameter of the subclass to the superclass; the default `account` in the superclass was never used. `super()` passed nothing, so the superclass returned to its own default. `super("fixed")` passed the written value and ignored the parameter of the subclass. The fourth line shows that what the bare `super` passes is the current value of the parameter: when `manual` was given to the call, `manual` went to the superclass. This is what makes the bare `super` useful, and it is also what makes it dangerous; the moment the signature of the subclass departs from that of the superclass, what is passed departs as well.

What happens when a `super` call finds no definition of the same name in the chain? The measurement below asks this.

```ruby
# orphan_super.rb — super with no definition in the ancestors
class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end
end

class Orphan < Account
  def missing = super
end

begin
  Orphan.new("a", 1).missing
rescue NoMethodError => e
  puts e.class
end
```

```
NoMethodError
```

`super` found no definition named `missing` in the chain and raised `NoMethodError`. So `super` is not magic but a search query: it looks for the next definition of the same name in the chain, and if it does not find one, it meets the error that meets every message.

The chain is not limited to two steps. Each subclass descends to the next with its own `super` call, and the calls return in order. The file below builds a chain of three classes and walks the chain from start to end with `super_method`, collecting the owners.

```ruby
# three_steps.rb — the super_method chain is walked to its end
class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def balance_text = "#{@owner} #{@balance}"
end

class TermDeposit2 < Account
  def balance_text = "term #{super}"
end

class Gold < TermDeposit2
  def balance_text = "gold #{super}"
end

g = Gold.new("tom", 50)
puts g.balance_text

owners = []
current = g.method(:balance_text)
while current
  owners << current.owner
  current = current.super_method
end
p owners
```

```
gold term tom 50
[Gold, TermDeposit2, Account]
```

The text was assembled from the innermost definition outward: `Account` wrote the `tom 50` part, `TermDeposit2` wrote the `term ` prefix, and `Gold` wrote the `gold ` prefix. The definition closest to the receiver is the first to run, yet it is the one waiting for the next in the middle of its body. The `super_method` loop gave the owners in the same order and stopped when it returned `nil`. This confirms that a `super` call is not an invisible mechanism but a path read over the order of `ancestors`.

#### The Chain Is Searched at Call Time

When the `TermDeposit` class was defined, it did not copy the definitions of `Account` into itself; when a call arrives, it looks at the chain. This has an observable consequence: a method added to `Account` after `TermDeposit` was set up can be called at once on a `TermDeposit` object as well. The file below puts the same question to the same object, before and after `Account` is reopened.

```ruby
# late_addition.rb — a method an ancestor gains later is visible in the subclass
class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end
end

class TermDeposit < Account
end

t = TermDeposit.new("tom", 50)
p t.respond_to?(:interest)

class Account
  def interest = @balance / 10
end

p t.respond_to?(:interest)
p t.interest
p TermDeposit.instance_methods(false)
```

```
false
true
5
[]
```

The first `respond_to?` gave false, and after `Account` was reopened it gave true, and `t.interest` returned `5`. In the meantime `t` was never rebuilt. `TermDeposit.instance_methods(false)` is still empty: `interest` was not written to the subclass; it only became a definition of an ancestor in the chain. The definition that is searched for is found according to the current state of the chain at call time.

**CM13.** In this lesson the "chain" is the search order that starts at the receiver's class and runs as far as `BasicObject`. The members of the chain are not copied into the class; the search is made from scratch on every call. A method an ancestor gains later is therefore visible in the existing objects of the subclass too.

#### The Bounding Measurement

`TermDeposit` wrote nothing, and its author came out as an ancestor. `TermDeposit2` took half of the body from the ancestor and wrote the other half itself. How does the rule classify this second one?

```ruby
# half_ancestor.rb — an overriding definition that calls super counts as the programmer
require_relative "author"

class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def balance_text = "#{@owner} #{@balance}"
end

class TermDeposit < Account
end

class TermDeposit2 < Account
  def balance_text = "term #{super}"
end

p author(TermDeposit.new("tom", 50), :balance_text)
p author(TermDeposit2.new("tom", 50), :balance_text)
```

```
["ancestor", "Account"]
["programmer", "TermDeposit2, def body"]
```

The result was an ancestor for `TermDeposit` and the programmer for `TermDeposit2`. Although the body of `TermDeposit2` took half from the ancestor, the rule does not measure that. The rule counts the first definition found in the chain and compares the owner of that definition with the receiver's class; it does not look at the rest of the chain.

**CM6.** In this lesson the "first definition found" is the first definition met in the search order that starts at the receiver. `instance_method(name)` gives it. The rule does not see that a `super` call descends to another definition further along the chain, because it does not look at the body of the first definition.

The consequence runs both ways. Writing a single `def` in the subclass turns the author from an ancestor into the programmer; how much of the body is handed over to the superclass does not change. On the other hand, the body of a method whose author is the programmer need not belong wholly to that programmer. The author category says the place of the first definition in the chain, not the source of the body.

#### Summary

- An empty subclass takes its methods from the superclass: `TermDeposit.instance_methods(false)` is empty and `TermDeposit.new("tom", 50).balance_text` comes from an ancestor.
- `superclass` gives one step and `ancestors` gives the whole chain; `TermDeposit.ancestors.take(5)` shows the order of subclass, superclass, mixed-in module, `Object` and `Kernel`.
- Overriding does not delete the definition of the superclass; `super` descends to the next definition of the same name in the chain, and `super_method.owner` gives the owner of that definition.
- Inside `initialize`, `super(owner, balance)` passes only the arguments that are written; in the same signature a bare `super` passes the arguments unchanged and so raises `ArgumentError`.
- The author of this lesson is the ancestor: in the `superclass` row `Account` wrote `balance_text`, and `TermDeposit` wrote nothing.
- A `super` with no definition in the ancestors raises `NoMethodError`; in an overriding method that calls `super` the author comes out as the programmer, because the rule counts the first definition found.

#### Next Step

The method `report` was never written in the `Account` class, yet `account.report` worked and gave `report: amy 100`. The method comes neither from a superclass nor from the body of the class itself. How does `Reportable`, which stands between `Account` and `Object` in the chain, carry that method? The next lesson, Modules and Mixins, measures this.

### Modules and Mixins

Source: https://academia.sh/en/courses/object-orientation-and-metaprogramming/modules-and-mixins

How a module differs from a class; mixing a module into the ancestor chain with include; Comparable and <=>; order among two modules of the same name; extend on a single object.

The previous lesson left a fact behind: `report` was not written in the `Account` class, yet the call `account.report` gave `report: amy 100`. `Reportable`, which stands between `Account` and `Object` in the chain, carries the method, but what exactly does it mean for a module to give a method to a class? If a module is not a class, how does it enter the chain?

The author of this lesson is again the ancestor, but the type of the ancestor changes: the definition sits not in a superclass but in a mixed-in module. The lesson first separates a module from a class, then measures the place and the order of a mixin in the chain.

#### A Mixed-In Module Becomes the Author

The file below mixes the `Reportable` module into the `Account` class with `include`. `author.rb` is the rule from the earlier lessons and is loaded unchanged here as well.

```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
```

```ruby
# mixin.rb — the author of a module mixed in with include
require_relative "author"

module Reportable
  def report = "report: #{@owner} #{@balance}"
end

class Account
  include Reportable

  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end
end

account = Account.new("amy", 100)
puts account.report
p author(account, :report)
p Account.include?(Reportable)
p Object.include?(Reportable)
p Account.ancestors
```

```
report: amy 100
["ancestor", "Reportable"]
true
false
[Account, Reportable, Object, Kernel, BasicObject]
```

`author` gave the ancestor author with `Reportable` as the evidence: the definition is written not in the body of `Account` but in the module. `Account.include?(Reportable)` came out true and `Object.include?(Reportable)` false; a mixin affects only the class that receives the `include` call. In the `Account.ancestors` list, `Reportable` stands between `Account` and `Object`. Mixing a module into a class means placing the module directly above that class.

**CM7.** In this lesson "to mix in" means placing a module, with an `include` call, in the ancestor chain of a class, directly above the class. The line of code that does it is `include Reportable`, and the result is the new place in the chain. In the language of the rule the author of a mixin comes out as an ancestor, because the definition is not in the receiver's class but in the upper part of the chain.

That a mixin adds a link to the chain and does not copy the methods into the class is seen through two further questions. `include` is an expression and returns a value; `instance_methods(false)` asks for the body of the class itself. The file below measures both.

```ruby
# no_copy.rb — include adds a link to the chain, it does not copy the method
module Reportable
  def report = "report"
end

class Account
  p(include(Reportable))
end

p Account.instance_methods(false)
p Reportable.instance_methods
p Account.instance_method(:report).owner
p Account.included_modules.take(2)
```

```
Account
[]
[:report]
Reportable
[Reportable, Kernel]
```

The `include` call returned the receiver, that is, the `Account` class. `Account.instance_methods(false)` came out empty: `report` was not moved into the body of the class; it stayed in the module. `Reportable.instance_methods` showed the method in the module's own list, and `instance_method(:report).owner` confirmed that the owner is `Reportable`. `included_modules` gave the modules in the chain in the order `Reportable`, `Kernel`. So the same situation as with the empty subclass of the previous lesson holds: being callable and being written in the class are separate things, and a module does not move what is written; it changes the length of the chain.

#### A Module Is Not a Class

A mixin makes the module look like a class, but the two are not the same thing. No object can be produced from a module; a module exists only to enter the chain of others. The file below measures this with two questions.

```ruby
# module_not_class.rb — a module has no instances
module Reportable
  def report = "report"
end

p Reportable.instance_of?(Class)
p Reportable.instance_of?(Module)
begin
  Reportable.new
rescue NoMethodError => e
  puts e.class
end
```

```
false
true
NoMethodError
```

`Reportable` is an instance of `Module`, not an instance of `Class`, and the `new` method is not defined on it. This difference also explains why a mixin is needed: inheritance accepts only one superclass, whereas any number of modules can be mixed into a class. Independent behaviors, for example reporting and comparing, are not squeezed into a single superclass chain; they are written in separate modules and mixed into the class that needs them.

#### A Mixin from the Core: Comparable

A mixin is not limited to modules we write ourselves. The `Comparable` module of the core gives a class the rest of the comparison methods when only `<=>` is written. The file below measures this on a small `Score` class.

```ruby
# compare.rb — Comparable and <=>
require_relative "author"

class Score
  include Comparable

  attr_reader :value

  def initialize(value)
    @value = value
  end

  def <=>(other) = value <=> other.value
end

a = Score.new(3)
b = Score.new(5)
c = Score.new(9)
p c.between?(a, c)
p b > a
p [c, a, b].min.value
p author(a, :between?)
p author(a, :<=>)
```

```
true
true
3
["ancestor", "Comparable"]
["programmer", "Score, def body"]
```

Only `<=>` is written inside `Score`, yet the calls `between?` and `>`, and the `min` call that works over `<=>`, gave results. For `between?` the author is an ancestor with `Comparable` as the evidence; for `<=>` it is the programmer, because `Score` wrote it in its own body. Two methods in the same class, two separate authors: a mixin does not delete the class's own definition; it adds next to it.

`Comparable` gives not only `between?` but all the comparisons built over `<=>`. The file below measures two more behaviors that are never written in `Score`: equality and clamping into a range.

```ruby
# compare_more.rb — Comparable provides three more methods
require_relative "author"

class Score
  include Comparable

  attr_reader :value

  def initialize(value)
    @value = value
  end

  def <=>(other) = value <=> other.value
end

a = Score.new(3)
b = Score.new(5)
p a == Score.new(3)
p a.equal?(Score.new(3))
p Score.new(9).clamp(a, b).value
p author(a, :==)
p Score.ancestors.take(3)
```

```
true
false
5
["ancestor", "Comparable"]
[Score, Comparable, Object]
```

`a == Score.new(3)` gave true, because `Comparable#==` tests whether the result of `<=>` on the two objects is zero. `equal?`, however, showed that they are two separate objects and gave false: equality is asked by value, identity by object. `clamp` brought the value nine down to five. For `==`, `author` pointed at `Comparable`; `BasicObject` also defines an `==`, but because `Comparable` comes first in the chain, that one is shadowed. This is the core example of the order rule measured below.

#### The Same Name in Two Modules

If two modules are mixed into a class and both define the same name, which one wins? The order in the chain decides, and the order of mixing decides the order of the chain: the one mixed in later goes in front of the earlier one.

```ruby
# two_modules.rb — the same name, two modules
module A1
  def label = "A1"
end

module B1
  def label = "B1"
end

class C1
  include A1
  include B1
end

p C1.ancestors.take(3)
puts C1.new.label
p C1.ancestors.count(B1)
```

```
[C1, B1, A1]
B1
1
```

`include A1` was written first and `include B1` second, and the chain came out as `[C1, B1, A1]`: the one mixed in last stands closest to the receiver. The `label` call therefore gave `B1`. `ancestors.count(B1)` gave one: a module stands in the chain as a single link. Whether mixing in the same module a second time changes this table is asked by the measurement below.

```ruby
# repeated_include.rb — a second include does not change the chain
module A1
  def label = "A1"
end

module B1
  def label = "B1"
end

class C1
  include A1
  include B1
  include A1
end

p C1.ancestors.take(3)
puts C1.new.label
p C1.ancestors.count(A1)
```

```
[C1, B1, A1]
B1
1
```

The second `include A1` at the end moved nothing in the chain. Because `A1` was already in the chain, Ruby did not bring it to the front again, so `B1` kept winning and `ancestors.count(A1)` gave one. A mixin holds a module in the chain at most once; the way to change the order is not to `include` again.

**CM8.** In this lesson the "order in the chain" is the reading of the `ancestors` array from left to right, and method lookup walks in that order. The first matching definition wins. Because a module included later takes its place directly above the class, it shadows the earlier one.

#### super in Modules

A method inside a module can also call `super`, and `super` descends to the next link in the current chain. This gives two different results depending on whether the module comes before or after the class. The file below combines two modules with a superclass and a subclass.

```ruby
# module_super.rb — super passes through the modules in the chain
module A2
  def label = "A2 " + super
end

module B2
  def label = "B2 " + super
end

class Base
  def label = "base"
end

class C2 < Base
  include A2
  include B2
end

class D2 < Base
  include A2
  def label = "D2 " + super
end

puts C2.new.label
p C2.ancestors.take(4)
puts D2.new.label
```

```
B2 A2 base
[C2, B2, A2, Base]
D2 A2 base
```

For `C2` the call entered `B2`, descended with `super` to `A2`, and from there to `Base`, and the text was assembled in the order of the chain. `ancestors` showed the same path as `[C2, B2, A2, Base]`: `super` is walking along the chain. In `D2` the class wrote its own `label`; because the module stays below the class, `D2` ran first, and with `super` the call went down to `A2` and from there to `Base`. Combined with the `super_method` measurement of the previous lesson, the result is this: both a subclass that overrides and a module that is mixed in are links of the same chain, and `super` does not tell the two apart.

**CM14.** In this lesson "super in the chain" means a `super` call descending to the next link in the `ancestors` order. Whether the link is a class or a module does not change the call; only the order decides.

Modules can also be mixed into one another. The chain of a module carries the modules mixed into it, and the class that includes that module inherits all of them at once. The file below measures a two-layer mixin.

```ruby
# nested.rb — a module mixed into a module comes along with it to the class
module Upper
  def upper = "upper"
end

module Lower
  include Upper

  def lower = "lower"
end

class Box
  include Lower
end

p Lower.ancestors
p Box.ancestors.take(3)
p Box.new.upper
p Box.include?(Upper)
```

```
[Lower, Upper]
[Box, Lower, Upper]
"upper"
true
```

`Lower.ancestors` showed the module's own chain, and `Upper` stood in it directly behind `Lower`. `Box` mixed in only `Lower`, yet `Upper` entered its chain too; that is why `Box.new.upper` worked and `Box.include?(Upper)` gave true. It can be seen here that a mixin brings not a single link but the whole of the module's current chain. When reading the chain of a class, the mixins of the modules have to be counted as well.

#### The Bounding Measurement

There are two limits. The first: if the class writes its own `report`, what happens to the definition of the mixin? The second: if a module is mixed not into the whole class but into a single object only, how do the author and the chain look?

```ruby
# bounding.rb — the class shadows, extend mixes into a single object
require_relative "author"

module Reportable
  def report = "report: #{@owner} #{@balance}"
end

class Account2
  include Reportable

  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def report = "account2 report"
end

class Account3
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end
end

p author(Account2.new("amy", 100), :report)

single = Account3.new("tom", 50)
single.extend(Reportable)
puts single.report
p author(single, :report)
p Account3.new("a", 1).respond_to?(:report)
```

```
["programmer", "Account2, def body"]
report: tom 50
["ancestor", "Reportable"]
false
```

When `Account2` wrote its own `report`, the rule gave the programmer: the class's own definition comes before the module it mixed in and shadows it; `Reportable#report` keeps standing in the chain and can be reached only with a `super` call. `single.extend(Reportable)`, in turn, mixed the module into the `single` object only; that object gave the answer `report: tom 50`, the ancestor author came out as `Reportable`, but a new object produced from the same class answered false to the question `respond_to?(:report)`. The chain of this object has become different from that of its class; where that chain is placed will be covered in the coming lessons under the name of the singleton class.

What `extend` does to an object can be measured with the same questions as `include`: is the object counted as an instance of the module, are the other objects of the class counted so, and where does the module stand in the object's own chain?

```ruby
# extend_measure.rb — extend adds a link only to the chain of that object
module Reportable
  def report = "report"
end

class Account3
end

single = Account3.new
other = Account3.new
single.extend(Reportable)

p single.is_a?(Reportable)
p other.is_a?(Reportable)
p single.singleton_class.include?(Reportable)
p single.singleton_class.ancestors.drop(1).take(2)
p Account3.include?(Reportable)
p other.singleton_class.ancestors.drop(1).take(2)
```

```
true
false
true
[Reportable, Account3]
false
[Account3, Object]
```

`single` behaved like an instance of the module, `other` did not, and the `Account3` class never saw the module. After the first link of the object's own chain came `Reportable` and then `Account3`; for `other`, in the same place there are directly `Account3` and `Object`. So `extend` adds the module not to the chain of the class but to the chain particular to that single object. The structure that holds this chain is called the singleton class, and it will be examined separately in later lessons.

#### Choosing Between a Mixin and Inheritance

The choice between a module and a superclass depends on the kind of relation. `TermDeposit` is an `Account`: a specialized form of the same kind. `Account`, on the other hand, is something reportable: a capability of the kind. Capabilities recur across different kinds, for example an account and an invoice can both be reportable, yet they need not share the same superclass. Writing the capability in a module is the way to give it to two separate inheritance trees as something written in one place.

There is also a constraint: in Ruby a class has one superclass but can have as many mixins as wanted. The chain therefore always stays linear. Modules enter the chain as ordered links, and as seen in the measurement, this order is predictable: the one mixed in last comes first. If there were several superclasses, a separate rule would be needed to say along which path the chain walks; a single chain makes that unnecessary.

A mixin also has a cost: it scatters the author. To see which module a method of a class comes from, the body of the class is not enough; measurements such as `ancestors` and `author` are needed. The job of the `author.rb` used throughout this lesson is exactly this: to find the place of a definition by asking the running object, without looking at the body of the class.

#### Summary

- A module is an instance of `Module`, not an instance of `Class`; `Reportable.new` raises `NoMethodError`. `include` mixes the module directly above the class, between `Account` and `Object`; `Account.include?(Reportable)` is true and `Object.include?(Reportable)` is false.
- The author of a mixin is an ancestor: for `account.report` the evidence came out as `Reportable`; `Account.instance_methods(false)` is empty, because the method is not copied into the class.
- In the `Comparable` mixin only `<=>` is written; `between?` and `==` have an ancestor author, and `<=>` has a programmer author.
- If the same name exists in two modules, the one mixed in later wins (`[C1, B1, A1]`); the same module stands in the chain once, and a second `include` does not change the order.
- Inside a module, `super` descends to the next link in the chain; a module mixed into a module comes to the class together with it (`Box.include?(Upper)`).
- If the class writes its own definition, it shadows the mixin and the author is the programmer; `extend` mixes the module into a single object only.

#### Next Step

The `Account` class has a method named `fee`, and when `account.fee` is called from outside it raises `NoMethodError`; even so, the `net` method can call it from inside the same object. The author is still the programmer, but who can call it, and why are some calls refused? The next lesson, Visibility, measures this question.

### Visibility

Source: https://academia.sh/en/courses/object-orientation-and-metaprogramming/visibility

Public, protected and private methods; refusal of a call from outside, the difference between send and public_send, the self receiver, a protected method working on another object of the same class, and changing visibility in a subclass.

The previous lesson ended by leaving a fact: the call `account.fee` is refused from outside, yet the `net` method can call it from inside the same object. The method is the same, the author is the same, but one call is accepted and the other refused. Does the difference lie in the method itself or in whoever makes the call?

The author of this lesson is the programmer: most of the measured methods are written by the classes in their own body with `def`; accessors that are not written are handled separately in the bounding measurement. The lesson measures a second property that the author does not change: the visibility of a method. In M11/K03 we saw through packages to whom methods are open; here the same question is asked inside a single class.

#### A Private Method Is Not Called from Outside

Ruby has three visibilities: public, protected and private. A public method is open to everyone. A private method, on the other hand, can be called only from inside the object itself, without an explicit receiver written. The `private` keyword makes the methods defined after it private.

```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
```

```ruby
# private_fee.rb — a private method is refused from outside
require_relative "author"

class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def net = @balance - fee

  private

  def fee = 2
end

account = Account.new("amy", 100)
p account.net
p author(account, :fee)
begin
  account.fee
rescue NoMethodError => e
  puts e.class
end
p account.send(:fee)
begin
  account.public_send(:fee)
rescue NoMethodError => e
  puts e.class
end
p Account.private_method_defined?(:fee)
p Account.public_method_defined?(:fee)
```

```
98
["programmer", "Account, def body"]
NoMethodError
2
NoMethodError
true
false
```

`account.net` gave 98: the call from inside found `fee`. `author` gave the programmer author, so being private did not change the author. `account.fee` from outside, however, raised `NoMethodError`. Calling the same method with `send` worked and gave 2, whereas `public_send` was refused. `send` is the call that ignores visibility, and `public_send` is the one that enforces it. The last two lines ask visibility at the level of the method definition: `fee` is registered as private, not as public.

**CM9.** In this lesson "visibility" is the three-valued property that decides who may call a method: public, protected, private. Visibility does not change the author; the author says where the definition is, and visibility says whether the call is accepted.

#### The self Receiver and a Private Method

The rule of a private method is a receiver rule: a call with an explicit receiver is refused. When the explicit receiver is `self`, this rule bends. The file below calls the same method in three forms.

```ruby
# self_receiver.rb — a self receiver can call a private method
class Account
  def initialize(balance)
    @balance = balance
  end

  def bare = fee
  def with_self = self.fee
  def with_other(other) = other.fee

  private

  def fee = 2
end

a = Account.new(100)
b = Account.new(50)
p a.bare
p a.with_self
begin
  a.with_other(b)
rescue NoMethodError => e
  puts e.class
end
```

```
2
2
NoMethodError
```

The call without a receiver and `self.fee` worked, and the call made through another `Account` object was refused. A private method belongs not to a class but to an object: even when it is of the same class, another object cannot reach its private method.

#### A Protected Method

A method that compares two accounts needs the balance of the other side but may not want to open the balance to everyone. A protected method serves exactly this purpose: it cannot be called from outside, but another object of the same class can call it.

```ruby
# protected.rb — a protected method is open to objects of the same class
class Jar
  def initialize(amount)
    @amount = amount
  end

  def greater?(other) = amount > other.amount
  def secret_greater?(other) = secret > other.secret

  protected

  def amount = @amount

  private

  def secret = @amount
end

a = Jar.new(10)
b = Jar.new(7)
p a.greater?(b)
begin
  a.amount
rescue NoMethodError => e
  puts e.class
end
begin
  a.secret_greater?(b)
rescue NoMethodError => e
  puts e.class
end
p Jar.protected_method_defined?(:amount)
```

```
true
NoMethodError
NoMethodError
true
```

Inside `greater?` the call `other.amount` worked: a protected method is accepted on another object as long as the calling object is of the same class. `a.amount` from outside was refused. The similarly built `secret_greater?` raised `NoMethodError` on the line `other.secret`, because a private method never accepts an explicit receiver. The limit of a protected method is the class, the limit of a private method is the object.

What exactly does the "same class" criterion of a protected method cover? The file below changes the caller to a superclass, a subclass and an unrelated class.

```ruby
# protected_lineage.rb — the limit of a protected method is the lineage of its owner
class Jar
  def initialize(amount)
    @amount = amount
  end

  def greater?(other) = amount > other.amount

  protected

  def amount = @amount
end

class Gold < Jar
end

class Stranger
  def ask(jar) = jar.amount
end

p Jar.new(10).greater?(Gold.new(4))
p Gold.new(4).greater?(Jar.new(10))
begin
  Stranger.new.ask(Jar.new(1))
rescue NoMethodError => e
  puts e.class
end
```

```
true
false
NoMethodError
```

A superclass object could call the protected method of a subclass object, and a subclass object that of a superclass object (the results are `true` and `false`: 10 > 4 and 4 > 10): the criterion is that the caller descends from the class that owns the method. `Stranger` is not of this lineage, so the same call stayed closed to it. The limit of a protected method is therefore not a narrow match on a class name but the inheritance tree.

**CM10.** In this lesson "public" is a method open to everyone, "protected" is open to objects of the same class, and "private" is open only to a call without a receiver or with a `self` receiver. A call from outside with an explicit receiver is refused in every case except public.

#### private Returns a Value

`private` is not only a declaration; placed in front of a method definition, it makes that definition private too. The `def` expression returns the name of the method as a symbol, and `private` takes that symbol.

```ruby
# private_def.rb — private def makes a single method private
class Access
  private def only_this = 1

  def plain = 2
end

access = Access.new
p access.plain
p Access.private_instance_methods(false)
p Access.public_instance_methods(false)
begin
  access.only_this
rescue NoMethodError => e
  puts e.class
end
```

```
2
[:only_this]
[:plain]
NoMethodError
```

`private def only_this = 1` made only that method private; the `plain` after it stayed public. Visibility can thus be written not only for a stretch of lines but for a single method.

To see the value that `private` takes directly, it is enough to print the return of the `def` expression and the return of the `private` call itself. The file below measures both.

```ruby
# private_value.rb — def returns a symbol, private returns what it received
class Access
  p(def first = 1)
  p(private def second = 2)
  p(private(:first))
end
```

```
:first
:second
:first
```

The `def` expression returned the name of the method as a symbol, and `private def` gave back the symbol it received unchanged. `private(:first)` also returned the name it took. That is why the form `private def only_this = 1` works: the inner expression produces a name, and the outer call takes that name and changes its visibility. The same contract shows that changing visibility afterwards is also a call of the form `private :name`; the place of the definition does not change, only its record does.

#### Query Methods and Inheritance

Visibility appears in two more places. The first is the `respond_to?` question: whether an object responds depends on whether the call would be accepted from outside. The second is inheritance: does a private method pass to a subclass? The file below measures both.

```ruby
# query_and_inheritance.rb — respond_to? and a call from a subclass
class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  private

  def fee = 2
end

class TermDeposit < Account
  def net_term = @balance - fee * 2
  def from_outside(other) = other.fee
end

v = TermDeposit.new("tom", 50)
p v.respond_to?(:fee)
p v.respond_to?(:fee, true)
p v.net_term
begin
  v.from_outside(TermDeposit.new("a", 1))
rescue NoMethodError => e
  puts e.class
end
p TermDeposit.private_method_defined?(:fee)
```

```
false
true
46
NoMethodError
true
```

`respond_to?(:fee)` gave false, whereas `respond_to?(:fee, true)` gave true: the second argument includes private methods in the question. `TermDeposit` could call `fee` without a receiver in its own body, and `net_term` gave `46`; a private method passes to the subclass, because the rule looks at the form of the call, not at the owner of the definition. But a call with an explicit receiver such as `other.fee` was refused even inside the subclass. `TermDeposit.private_method_defined?(:fee)` is true: visibility too is carried along the chain.

#### A Private Setter and a Private Class Method

The bending of the receiver rule with `self` is most useful in setter methods: the form `value = 5` defines a local variable and does not call a method; to call the writer, `self.value = 5` is needed. The file below calls a private writer this way. The second part measures the visibility of class methods.

```ruby
# writer_and_class.rb — a self setter and private_class_method
class Counter
  def initialize
    @value = 0
  end

  def increment
    self.value = value + 1
    value
  end

  private

  attr_accessor :value
end

class Gadget
  def self.build = new
  private_class_method :new
end

s = Counter.new
p s.increment
p s.increment
begin
  s.value = 9
rescue NoMethodError => e
  puts e.class
end
p Gadget.build.class
begin
  Gadget.new
rescue NoMethodError => e
  puts e.class
end
```

```
1
2
NoMethodError
Gadget
NoMethodError
```

`increment` called the private `value=` setter in the form `self.value = ...`, and the counter became `1`, then `2`; `s.value = 9` from outside was refused. For class methods the same rule is applied with `private_class_method`: `Gadget.new` was refused, but `Gadget.build` produced the object because it called `new` without a receiver from inside the class itself. The only door to producing an object thus becomes a public class method.

#### The Bounding Measurement

Visibility did not change the author. But when a generator such as `attr_reader` produces a private accessor, who is the author? And if a subclass makes a private method of its superclass public, whose does the method become?

```ruby
# bounding.rb — private attr_reader and public in a subclass
require_relative "author"

class Jar
  def initialize(amount)
    @amount = amount
  end

  private attr_reader :amount
end

class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  private

  def fee = 2
end

class Account3 < Account
  public :fee
end

p author(Jar.new(3), :amount)

h = Account3.new("amy", 100)
p h.fee
p h.method(:fee).owner
p author(h, :fee)
p Account.new("a", 1).respond_to?(:fee)
```

```
["code", "Jar, no ruby body"]
2
Account3
["programmer", "Account3, def body"]
false
```

Although the accessor produced with `private attr_reader :amount` is private, `author` gave the code author: the definition was produced by the `attr_reader` call and not by `def`, and in Ruby it has no body. In Ruby 4.0.7, `public :fee` in `Account3` changed two results: `h.fee` now gave 2 and the owner of the method came out as `Account3`, not `Account`. This result is understood by looking at what the visibility call does: the definition of the superclass stays as it is, and `public :fee` puts a record of the same name in the subclass. `author` counts the first record found in the chain, that record stands in `Account3`, so the programmer and `Account3, def body` came out. The superclass's own object kept answering false to the `respond_to?` question: changing visibility affects only the subclass. The measurement below tests the correctness of this explanation.

```ruby
# bounding_why.rb — public :name puts a record in the subclass
class Account
  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  private

  def fee = 2
end

class Account3 < Account
  public :fee
end

h = Account3.new("amy", 100)
p Account3.instance_methods(false)
p Account.instance_method(:fee).owner
p Account3.instance_method(:fee).owner
p h.method(:fee).super_method.nil?
p Account.private_instance_methods(false)
p Account.public_method_defined?(:fee)
```

```
[:fee]
Account
Account3
true
[:fee, :initialize]
false
```

`Account3.instance_methods(false)` listed `fee`: a record was really added to the subclass, although there is not a single line of definition in its body. The definition of `Account` stayed owned by `Account`, `Account.private_instance_methods(false)` still listed it as private (the `initialize` in the list is a private method of every class), and `Account.public_method_defined?` gave false. The `super_method` of the record in the subclass came out `nil`: Ruby 4.0.7 holds this record not as a separate body but as a visibility record that looks at the definition of the superclass, and it does not count it as a second link in the chain. Visibility is not a property of the definition; it is a property of the record that finds the definition in the chain.

**CM15.** In this lesson "record" is the pair of name and visibility in the method table of a class; public and protected records are read with `instance_methods(false)`, private records with `private_instance_methods(false)`. A visibility call puts the record in the subclass; it does not copy the body of the definition.

#### Which Visibility, When

The choice among the three visibilities depends on whom a method serves. The contract a class gives to the outside is its public methods: code that calls them may exist, and if the class changes them, that code breaks. In the `Account` example `net` is such a contract. `fee` is a detail that explains how `net` is computed; because it is made private, the class can change it as it likes, since no code calls it from outside.

A protected method fills a narrow gap between the two: the contract that the objects of a class use when talking to one another but that is not opened to the outside. In the `Jar` example `amount` is like this. A protected method is needed in methods that handle two objects together, such as a comparison: `greater?` read the `amount` of the other object only because it is protected. If only the object itself calls the method, making it private and calling it with a `self` receiver is enough.

Another result seen in this lesson is that visibility is not a security measure. `send` could call the private method; so visibility documents a contract and prevents an accidental call, but does not stop a deliberate one. Code that calls a private method with `send` therefore does knowingly something the class does not allow, and when the class changes that method, it is the first to break.

#### Summary

- A private method cannot be called from outside with an explicit receiver and raises `NoMethodError`; it can be called from inside the object without a receiver or with a `self` receiver.
- `send` ignores visibility and gives 2; `public_send` enforces it and refuses.
- A protected method can be called on another object of the same class; a private method cannot be called on another object.
- `private def x = 1` makes only that method private; visibility is asked with `private_instance_methods(false)`.
- The author of this lesson is the programmer: for `fee` `author` gave the programmer, for `private attr_reader :amount` the code.
- `public :fee` in a subclass changes the visibility only in that class; it puts a record in the subclass, the private definition of `Account` stays in place and the owner comes out as `Account3`.

#### Next Step

A `Lock` module has been mixed into a `Safe` class, and both define `unlock`. In the call `Safe.new.unlock`, which definition runs first, and how does the `super` chain follow this order? The next lesson, The Method Lookup Chain, measures this question.

### The Method Lookup Chain

Source: https://academia.sh/en/courses/object-orientation-and-metaprogramming/the-method-lookup-chain

How a method is searched in the ancestor chain when a message reaches the receiver; a prepended module, super_method, the end of the chain, the chain of class methods and a singleton method added to a single object.

The previous lesson ended by leaving a question: the `Safe` class and the mixed-in `Lock` module both have a method named `unlock`, and in the call `Safe.new.unlock` which one runs? The previous three lessons answered the parts of this question separately: a class writes its own definition, a superclass is inherited, a module is mixed in. All of them sit inside the same lookup order.

The author of this lesson is the ancestor author, but this time it does not matter whether the ancestor is a superclass or a module. In M13/K01 we measured the author through the author of the definition; here the question of the author stays the same and the lookup order itself is measured. Whether the definition stands in a superclass or in a module is not separated: the two are a single category, ancestor.

#### A Prepended Module

`include` places the module above the class, so the class wins. `prepend`, on the other hand, places the module in front of the class; a message that reaches the receiver goes first to the module. The file below adds a `Lock` module to `Safe` with `prepend`.

```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
```

```ruby
# prepended.rb — with prepend the module goes in front of the class
require_relative "author"

module Lock
  def unlock = "lock opened, #{super}"
end

class Safe
  prepend Lock

  def unlock = "safe opened"
end

safe = Safe.new
puts safe.unlock
p author(safe, :unlock)
p Safe.ancestors.take(3)
p safe.method(:unlock).owner
p safe.method(:unlock).super_method.owner
```

```
lock opened, safe opened
["ancestor", "Lock"]
[Lock, Safe, Object]
Lock
Safe
```

`Safe.new.unlock` combined two parts: first the prefix of `Lock`, then, with `super`, the class's own definition of `Safe`. `author` gave the ancestor author with `Lock` as the evidence: the first definition found in the chain is in `Lock`, because in the `Safe.ancestors` list `Lock` stands in front of `Safe`. `method(:unlock).owner` came out `Lock` and `super_method.owner` `Safe`. The lookup walks from left to right, the first match runs, and `super` advances one step in the chain from that point.

**CM11.** In this lesson "method lookup" means that when a message reaches the receiver, Ruby walks the receiver's ancestor chain from left to right in the `ancestors` order and finds the first definition whose name matches. "To prepend" means placing a module in front of a class with `prepend`; `include`, in contrast, places the module behind the class.

#### include and prepend Side by Side

The difference between the two keywords shows when the same module and the same class are set up twice and the chains are compared. The file below adds `Lock` to one class with `include` and to the other with `prepend`; then it tests the order as well by prepending two modules one after the other.

```ruby
# include_prepend.rb — the place of the two keywords in the chain
module Lock
  def unlock = "lock opened"
end

module Alarm
  def unlock = "alarm, #{super}"
end

class SafeInclude
  include Lock

  def unlock = "safe opened"
end

class SafePrepend
  prepend Lock

  def unlock = "safe opened"
end

class SafeTwice
  prepend Lock
  prepend Alarm

  def unlock = "safe opened"
end

p SafeInclude.ancestors.take(3)
puts SafeInclude.new.unlock
p SafePrepend.ancestors.take(3)
puts SafePrepend.new.unlock
p SafeTwice.ancestors.take(4)
puts SafeTwice.new.unlock
```

```
[SafeInclude, Lock, Object]
safe opened
[Lock, SafePrepend, Object]
lock opened
[Alarm, Lock, SafeTwice, Object]
alarm, lock opened
```

With `include`, the class came first in the chain of `SafeInclude` and `Lock` fell behind it; the call to `unlock` stopped at the class's own definition and never reached the definition of the module. With `prepend`, in the chain of `SafePrepend` `Lock` went in front of the class, but because `Lock#unlock` did not call `super`, this time the definition of the class did not run. So a prepended module shadows the class completely if it does not write `super`. In the chain of `SafeTwice` the second `prepend` went in front of the first: the one added later stands closest to the receiver, just as in the order of `include`. The rule is the same in all three classes; only whether the link enters in front of or behind the class changes.

To walk the chain to its end, `super_method` can also be used in a loop. The file below stacks a singleton method, a prepended module and a class on top of one another and collects the owner of each link in order. Because the name of the singleton class carries a memory address, the owner is written to the output not by its name but with the label `:singleton`.

```ruby
# walk_chain.rb — counting the links with super_method
module Lock
  def unlock = "lock opened, #{super}"
end

class Safe
  prepend Lock

  def unlock = "safe opened"
end

k = Safe.new
def k.unlock = "singleton, #{super}"

owners = []
current = k.method(:unlock)
while current
  owner = current.owner
  owners << (owner == k.singleton_class ? :singleton : owner)
  current = current.super_method
end

p owners
puts k.unlock
p Safe.new.method(:unlock).super_method.super_method
```

```
[:singleton, Lock, Safe]
singleton, lock opened, safe opened
nil
```

The loop found three links: the singleton class, `Lock` and `Safe`; this order coincides exactly with the reading of `ancestors`. `puts k.unlock` combined the part of each link in the same order. In a `Safe` object without a singleton method the chain is two links, and `super_method.super_method` gives `nil`: after `Safe` there is no other link in the chain that defines `unlock`.

#### The End of the Chain

The chain is not endless. The chain of every class that descends from `Object` ends with `Object`, `Kernel` and `BasicObject`. The file below reads the chain of the `Account` class to its end and asks about a private method at the last stop.

```ruby
# chain_end.rb — the chain ends with BasicObject
class Account
end

p Account.ancestors
p Account.ancestors.last
p Account.ancestors.size
p BasicObject.private_method_defined?(:method_missing)
p Kernel.private_method_defined?(:puts)
```

```
[Account, Object, Kernel, BasicObject]
BasicObject
4
true
true
```

The chain of an empty `Account` class is the array `[Account, Object, Kernel, BasicObject]`, and `ancestors.size` gave four; with a mixed-in module it would be five. `BasicObject` is the last stop of the chain. `method_missing` is defined there as private: if the name is not found in the chain, this method is called as a last resort, and its default behavior is to raise `NoMethodError`. `puts`, on the other hand, stands in `Kernel` as private; that is why it can be called without a receiver from inside any object, but `"x".puts` cannot be written.

What happens when the end of the chain is reached and the name is not found depends on the private `method_missing` definition of `BasicObject`. If a class redefines this method in its own body, the unmatched message falls to it. The file below compares the answer two classes give to the same unmatched call.

```ruby
# last_resort.rb — a name not found in the chain falls to method_missing
class Account
end

class Account2
  def method_missing(name, *) = "missing: #{name}"
  def respond_to_missing?(name, include_private = false) = false
end

begin
  Account.new.other_name
rescue NoMethodError => e
  puts e.class
end

puts Account2.new.other_name
p Account2.new.respond_to?(:other_name)
p Account2.instance_method(:method_missing).owner
p Account.instance_method(:method_missing).owner
```

```
NoMethodError
missing: other_name
false
Account2
BasicObject
```

For `Account` the name was not found along the chain, `BasicObject#method_missing` received the message and raised `NoMethodError`. Because `Account2` wrote its own `method_missing`, the message did not go down to the end of the chain and fell to the class's own definition. `respond_to?` gave false; an object responding in this way does not mean that it has defined a method. The `owner` comparison pointed at the class itself for `Account2` and at `BasicObject` for `Account`. So the end of the chain is a default behavior, and a class can change it at any link of the chain.

**CM16.** In this lesson "the end of the chain" means the `method_missing` call that receives the message when the name is not found in the receiver's chain. Its default definition is in `BasicObject` and it raises `NoMethodError`; a closer link in the chain can redefine it.

#### The Chain of Class Methods

The `new` in the call `Account.new` is a method, and its receiver is the `Account` class itself. A class too has a chain. This chain is built over the singleton class particular to the class.

```ruby
# class_chain.rb — the class's own chain
class Account
end

p Account.method(:new).owner
p Account.singleton_class
p Account.singleton_class.ancestors.take(4)
```

```
Class
#<Class:Account>
[#<Class:Account>, #<Class:Object>, #<Class:BasicObject>, Class]
```

For `Account.new` the owner of the method `new` came out `Class`: class methods are found by the same lookup rule, only the receiver is a class and the chain starts from its singleton class. The singleton class of a class is printed under the name `#<Class:Account>`. The chain walks from this singleton class to the singleton class of `Object`, to the singleton class of `BasicObject` and finally to `Class`. So a class method defined in a superclass is found when called from a subclass as well.

Why the chain connects to `Class` after the singleton classes, and how a subclass finds the class method of its superclass, are two separate questions. The file below measures both; the comparisons are made not with the name in the output but with the question of equality, so that the result rests on the objects themselves and not on the spelling of a name.

```ruby
# class_chain_more.rb — a class method is found from the subclass
class Account
  def self.default_account = new("-", 0)

  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end
end

class TermDeposit < Account
end

p TermDeposit.default_account.class
p TermDeposit.method(:default_account).owner == Account.singleton_class
p TermDeposit.singleton_class.superclass == Account.singleton_class
p TermDeposit.singleton_methods
p TermDeposit.singleton_methods(false)
p BasicObject.singleton_class.superclass
p Account.singleton_class.ancestors.drop(3)
```

```
TermDeposit
true
true
[:default_account]
[]
Class
[Class, Module, Object, Kernel, BasicObject]
```

`TermDeposit.default_account` produced a `TermDeposit` object: although the method is written in the singleton class of `Account`, the receiver is `TermDeposit`, so `new` was applied to it. The `owner` comparison showed that the definition stands in the singleton class of `Account`, and the `superclass` comparison showed that the singleton class of `TermDeposit` is tied to the singleton class of `Account`. `singleton_methods` counted the class method coming from the superclass as well, whereas `singleton_methods(false)` stayed empty. The reason the chain passes to `Class` after the singleton classes can be read from here too: the superclass of the singleton class of `BasicObject` is `Class`, and from that point on the chain merges into the ordinary class chain with `Module`, `Object`, `Kernel` and `BasicObject`.

#### The Bounding Measurement

Until now the chains were built from classes and modules. The chain of an object can also carry a link particular to that object: the singleton class. The file below adds a singleton method to a single `Safe` object and extends a module.

```ruby
# singleton.rb — a link particular to a single object
require_relative "author"

module Extra
  def unlock = "extra, #{super}"
end

module Lock
  def unlock = "lock opened, #{super}"
end

class Safe
  prepend Lock

  def unlock = "safe opened"
end

k = Safe.new
def k.unlock = "singleton, #{super}"

puts k.unlock
puts Safe.new.unlock
p author(k, :unlock)
p k.singleton_methods

j = Safe.new
j.extend(Extra)
p j.singleton_class.ancestors.drop(1).take(4)
puts j.unlock
```

```
singleton, lock opened, safe opened
lock opened, safe opened
["programmer", "singleton class, def body"]
[:unlock]
[Extra, Lock, Safe, Object]
extra, lock opened, safe opened
```

`def k.unlock` added a singleton method to a single object. `k.unlock` gave its own prefix first, then the rest of the chain; a newly produced `Safe` was not affected. The author came out as the programmer, with `singleton class, def body` as the evidence: in the language of the rule the owner of the definition is the singleton class of the object. `k.singleton_methods` gave only `[:unlock]`. `j.extend(Extra)`, in turn, mixed the `Extra` module into the singleton class of the object `j`, and the list `j.singleton_class.ancestors.drop(1)` began with `Extra` and continued with `Lock`, `Safe`, `Object`. The name of the singleton class at the head of the chain was not written to the output, because that name carries a memory address.

**CM12.** In this lesson the "singleton class" is the hidden class particular to a single object or to a class, added at the very head of the ancestor chain. A "singleton method" is a method defined in that class. Other objects do not share this chain.

#### Asking Who Provided the Method

A distinction often made when reading the chain is between `method` and `instance_method`. The first asks the object, the second asks the class; that is why only the first sees a singleton method. The file below puts the two and a method coming from `Kernel` side by side.

```ruby
# who_gave.rb — the difference between method and instance_method
module Lock
  def unlock = "lock opened"
end

class Safe
  prepend Lock

  def unlock = "safe opened"
end

k = Safe.new
def k.unlock = "singleton opened"

p k.method(:unlock).owner == k.singleton_class
p Safe.instance_method(:unlock).owner
p Safe.new.method(:unlock).owner
p k.method(:puts).owner
p k.method(:class).owner
p Safe.instance_method(:unlock).super_method.owner
```

```
true
Lock
Lock
Kernel
Kernel
Safe
```

Because `method` asks the object, the first line showed that the singleton class claimed ownership. `instance_method` looked only at the chain of the class; since the singleton method is not in that chain, the answer was `Lock`, that is, because the prepended module stands in front of the class, it appeared as the owner. On a `Safe.new` object without a singleton method, `method` gave the same answer. For `puts` and `class` the owner came out `Kernel`: the author of these two methods is neither the programmer nor the class; it is the `Kernel` module mixed into the `Object` chain. The last line asks about the link one below `Lock#unlock` and finds `Safe#unlock`; even a prepend of a single link can be read as a two-link chain with `super_method`.

All of these questions come down to the same habit: which definition a method runs with is decided not by where the definition is written in the text but by which link comes first in the receiver's chain. The owner question gives the name of that link.

#### Three Rules for Reading the Lookup

Three rules for the lookup come out of the measurements above. The first: the order comes from the receiver's chain; what counts is not the class in which the method is written but the chain the receiver has. The second: the lookup takes the first match in the chain, and the other definitions run only if they are called again with `super`. The third: `super` does not return to the head of the chain; it advances one step to the right from the link it is in. The `super` inside `Lock#unlock` therefore descends not to `Lock` but to `Safe`.

These rules also say that `include`, `prepend` and a singleton method are three views of a single mechanism. All three add a link to the chain; the difference is where the link enters. `include` puts the link behind the class, `prepend` in front of it, and the singleton class at the very head of the object's own chain. To understand which link wins, no guessing is needed: reading the `ancestors` array from left to right is enough. The singleton class does not appear in that list, but if asked with `singleton_class.ancestors` it stands at the head of the chain.

A readable chain is useful in debugging as well. When a method gives an unexpected result, the first question is which definition ran. The answer is the line `method(:name).owner`. The second question is whether there is another definition behind that one; the answer is taken by asking one link further with `super_method`, and repeating until `nil` returns. `super_method` gives the next definition in the chain; whether a definition calls `super` is shown by the body of the method. With these two methods the chain is not guessed, it is read.

#### Summary

- Method lookup walks the receiver's `ancestors` chain from left to right and runs the first definition whose name matches.
- `prepend` puts the module in front of the class; `Safe.new.unlock` runs `Lock` first and then, with `super`, `Safe`.
- `method(:unlock).owner` gives the owner of the first definition, `super_method.owner` the next one in the chain.
- The chain of a class that descends from `Object` ends with `BasicObject`; `method_missing` stands there, and `puts` stands in `Kernel`, both as private.
- Class methods come from the singleton class chain of the class: for `Account.new` the owner of the method `new` is `Class`.
- The author of this lesson is the ancestor; a singleton method added to a single object enters the head of the chain and makes the author the programmer.

#### Next Step

In a `Struct` class named `Point` the method `x` was never written with `def`, yet `point.x` works and `author` gives its author as code. Is it possible to ask an object about its methods at run time, and to have the object tell who wrote `x`? The next lesson, Reflection, measures asking the object these questions.

## Metaprogramming

Querying objects at run time, defining methods with code that writes code, proxy objects that catch missing methods and their risks, and building domain-specific languages with declarative interfaces.

### Reflection

Source: https://academia.sh/en/courses/object-orientation-and-metaprogramming/reflection

Measuring how to ask an object about its own methods, state and type at run time, and how those questions classify who wrote a method.

The previous lesson showed that the same message can reach two different authors in two instances of the same class: the singleton class of a single object sits at the very front of the method lookup chain. The last sentence of that lesson left a question behind: can we ask the object itself, at run time and without opening the file, who wrote the `x` method of `Point`? This lesson answers it. **Reflection** is a program examining its own objects, classes and methods while it runs. Instead of calling a method, the program asks a question about the method.

The subject of the second half of the course is **metaprogramming**: a program that writes programs, or that changes the structure of the program while it runs. Reflection is its read-only half. It only asks and changes nothing. The next two lessons move on to writing. The axis stays the same: **who wrote this method?** There are four possible authors: programmer, ancestor, code and nobody. The programmer wrote the method with `def`. An ancestor wrote it as a superclass or a module. Code produced it: a piece of code generated the method, and no body was written by hand. Nobody means there is no definition under that name at all.

#### The Classifying Helper

The file below is the shared helper of the course, and this lesson gives it as it is. It asks three questions: is there a definition under that name in the receiver's chain, does the receiver's own class carry the definition, and does the definition rest on a `def` body. Each question is a reflection call, and we will look at them one by one.

```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 and has no output; it is only written to disk, and the other files pull it in with `require_relative`. The helper reads only the form: it looks at where the definition sits and what kind of body it has. It does not read the intent of the author. **MP1.** This classification is meaningful only for user classes written in Ruby; the result for core classes is measured at the end of this lesson.

#### The Measured Classes

The objects measured throughout the lesson are in the file below. It is an excerpt of the shared definitions of the course, with only what this lesson's observation needs: an account class, its subclass, a module and a `Struct`.

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

module Reportable
  def report = "report: #{balance_text}"
end

class Account
  include Reportable

  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def balance_text = "#{@owner} #{@balance}"
  def net = @balance - fee

  private

  def fee = 2
end

class TermDeposit < Account
end

Point = Struct.new(:x, :y)
```

`Struct` is a core class that produces a class with the given names. Each name becomes a **member**, and a reader and a writer method for that member come ready-made. `Point` is a class produced this way. This block is source too.

#### Who Wrote the Member Reader

The observation of the course in this lesson is the `x` method of a `Point` object. The file below calls the helper and prints the value the method produces next to it.

```ruby
# member_reader.rb — who wrote Point#x
require_relative "authors"

pt = Point.new(3, 4)
p author(pt, :x)
puts pt.x
```
```
["code", "Point, no ruby body"]
3
```

The author is **code**: neither the programmer wrote `x` with `def` nor a superclass did; the `Struct.new` call created it while the class was being produced. The helper concluded this from two facts. The owner of the method turned out to be `Point` itself, so it is not an ancestor. And there was no Ruby body, because `RubyVM::InstructionSequence.of` returned `nil` for this method. Which question gives which answer is measured one by one in the next sections. **MP2.** One `Struct` class was measured in Ruby 4.0.7; this measurement cannot show whether another version of `Struct` writes its members differently.

#### Asking the Class and the Object

The first group of reflection asks about the structure of a class and an object: which members exist, who the superclass is, what the object holds as its own state.

```ruby
# query.rb — asking a Point object through reflection
require_relative "authors"

pt = Point.new(3, 4)
p Point.members
p pt.to_a
p Point.superclass
p author(pt, :to_a)
p author(pt, :x=)
p pt.instance_variables
```
```
[:x, :y]
[3, 4]
Struct
["ancestor", "Struct"]
["code", "Point, no ruby body"]
[]
```

`members` returned the member names in order, and `to_a` returned the values in the same order. `superclass` said that `Point` descends from `Struct`. The two `author` calls gave two different results: `Struct` wrote the `to_a` method, because the owner is not `Point`, so the author is **ancestor**. `Point` itself carries the `x=` method and it has no body, so the author is **code**. Two methods of the same object reached two different authors. The last line is surprising: `instance_variables` came out empty. `Struct` keeps its values in its own internal layout and not in instance variables, so reflection finds no `@x`. **MP3.** The empty list was measured only for `Struct`; the instance variables of an ordinary class are seen in the next block.

#### Asking the State and the Private Method

In an object of an ordinary class, state lives in instance variables. Reflection lists them by name and reads their values by name. Private methods become visible when `respond_to?` is asked with a second argument of `true`.

```ruby
# state.rb — asking an object for its state and its private method
require_relative "authors"

acct = Account.new("amy", 100)
p acct.instance_variables
p acct.instance_variable_get(:@balance)
p acct.respond_to?(:fee)
p acct.respond_to?(:fee, true)
p acct.respond_to?(:net)
```
```
[:@owner, :@balance]
100
false
true
true
```

`instance_variables` returned the two instance variables in the order they were created. `instance_variable_get(:@balance)` read the value; this call works even if no reader method is defined from outside, because it reaches instance variables directly through reflection. `respond_to?(:fee)` returned `false`, because `fee` is private and the default question covers only public methods. When the second argument `true` was given, the same question returned `true`. `net` is public and returned `true`. So the default `respond_to?` question asks not whether the method exists but whether it is visible to the caller; with `true` as the second argument, private methods enter the scope. **MP4.** The private method question was measured only with `respond_to?`; other queries such as `private_methods` were not used in this lesson.

#### Asking the Method Itself

Reflection can also catch a method as an object. `instance_method` gives a method object from the class, and `method` gives one from the object. A method object can be asked for its argument count, its parameters and the place it was defined, and it can be called.

```ruby
# method_query.rb — asking the method itself
require_relative "authors"

acct = Account.new("amy", 100)
p Account.instance_method(:net).arity
p Account.instance_method(:initialize).parameters
p acct.method(:net).call
p Account.instance_method(:net).source_location.nil?
p Point.instance_method(:x).source_location.nil?
p Object.const_get(:Account)
```
```
0
[[:req, :owner], [:req, :balance]]
98
false
true
Account
```

The **arity** came out as `0` for `net`: the method takes no argument. `parameters` listed the two required parameters of `initialize` with their names; `:req` means required. `acct.method(:net).call` caught the method and called it, and gave `98`: `100` minus the `2` of the private `fee`. `source_location` gives the file and line of the method; since the path depends on the environment, only whether it is empty was printed here. It is not empty for `Account#net`, because that method was written with `def`; it is empty for `Point#x`, because `Struct` wrote it and there is no source line. This is the observable counterpart of the `nil` check in `author`. The last line finds a constant from its name: `const_get(:Account)` returned the class itself. **MP5.** `source_location` was measured for only two methods; that it is not `nil` does not show by itself that the author is the programmer.

#### Asking the Owner

The heart of the `author` helper is a single question: who is the owner of the method object. The owner is the class or module where the definition of the method sits. To find the owner, the helper looks at the class chain of the receiver; we can list the same chain by hand as well.

```ruby
# owner.rb — the owner of a method and the receiver's chain
require_relative "authors"

acct = Account.new("amy", 100)
td = TermDeposit.new("a", 1)
p TermDeposit.ancestors.first(3)
p acct.singleton_class.ancestors.drop(1).first(3)
p acct.method(:report).owner
p TermDeposit.instance_method(:net).owner
p author(td, :net)
p author(acct, :report)
```
```
[TermDeposit, Account, Reportable]
[Account, Reportable, Object]
Reportable
Account
["ancestor", "Account"]
["ancestor", "Reportable"]
```

`ancestors` gives the **ancestor chain** of a class from left to right: first the class itself, then the modules it includes and its superclasses, in the order the method lookup reads them. For `TermDeposit` the first three were `TermDeposit`, `Account` and `Reportable`: a module stands right behind the class that includes it. The chain of the object's own singleton class starts with the singleton class itself; that name changes in every run, so it is not printed, the first element is dropped, and the rest of the chain appears as `Account`, `Reportable`, `Object`. The owner of the `report` method came out as `Reportable`, while `TermDeposit.instance_method(:net)` reached the definition of `Account`, not of `TermDeposit`. For this reason `author(td, :net)` and `author(acct, :report)` both classified the author as **ancestor**: the owner is neither the class nor the singleton class of the receiver. **MP6.** The chain was printed with only its first three elements; the tail after `Object` was not measured.

#### The Method of a Single Object

Back to the observation of the previous lesson: the same message can reach two authors in two instances of the same class. Reflection asks this too. `singleton_methods` lists only the methods defined in the object's own singleton class. The measurement below builds two `Account` objects and writes its own `net` method for only one of them.

```ruby
# singleton.rb — the same message in two instances
require_relative "authors"

a = Account.new("amy", 100)
b = Account.new("tom", 50)
def b.net = 0

p a.singleton_methods
p b.singleton_methods
p a.net
p b.net
p author(a, :net)
p author(b, :net)
```
```
[]
[:net]
98
0
["programmer", "Account, def body"]
["programmer", "singleton class, def body"]
```

The list is empty for `a`, and `net` is in it for `b`. Two instances of the same class answered the same message with two different values: `98` for `a` and `0` for `b`. The two `author` calls name the difference. The `def` in the `Account` class wrote `a.net`; a `def` wrote `b.net` too, but this time the definition sits in the singleton class of `b`, and the helper calls it **singleton class** in place of an owner name. The author is the **programmer** in both cases; the difference is in the place of the definition. Reflection therefore separates two questions: who produced the method, and in which layer does it sit. **MP7.** Only the `net` method was measured in two instances; the relation of a singleton method to a class method was not established in this lesson.

#### Type and Method List

The last group asks what type an object is and what the list of a class's own methods is. The list is taken once more after the class is reopened: an `attr_reader` is added to the reopened class and the list changes.

```ruby
# kind.rb — asking an object's type and a class's methods
require_relative "authors"

acct = Account.new("amy", 100)
td = TermDeposit.new("a", 1)
p acct.is_a?(Reportable)
p td.is_a?(Account)
p td.instance_of?(Account)

class Account
  attr_reader :owner
end

p Account.public_instance_methods(false).sort
p author(acct, :owner)
```
```
true
true
false
[:balance_text, :net, :owner]
["code", "Account, no ruby body"]
```

`is_a?` returns `true` for every class and module in the chain: `acct` belongs to `Reportable`, and `td` belongs to `Account`. `instance_of?` accepts only the exact class; the `Account` question came out `false` for a `TermDeposit` instance. `public_instance_methods(false)` lists only the public methods of `Account` itself; `false` leaves out those that come from ancestors. For this reason `report`, which comes from `Reportable`, is not in the list, and neither is `initialize`, because `initialize` is private. `attr_reader :owner` added `owner` to the list. And `author` classified its author as **code**: `attr_reader` leaves no `def` body, it produces the method itself. **MP8.** The list was taken after the moment of reopening; the class is assumed not to be reopened anywhere else.

#### The Bounding Measurement

So far reflection has worked on classes the programmer wrote. What happens when the helper is turned to core classes?

```ruby
# limit.rb — the limits of the rule
require_relative "authors"

p author("a", :upcase)
p author(Account, :new)
[5, :a].each do |value|
  begin
    value.singleton_class
  rescue => e
    puts "#{value.inspect} -> #{e.class}"
  end
end
p nil.singleton_class
```
```
["code", "String, no ruby body"]
["code", "Class, no ruby body"]
5 -> TypeError
:a -> TypeError
NilClass
```

For `String#upcase` and `Class#new` the helper said **code**, but this answer misleads. The core of Ruby wrote these methods with compiled code; since there is no `def` body, the rule assigned them to code, for the same reason as `Struct` members. The difference between them is invisible to the helper: one is produced at run time, the other was written in the core from the start. The rule reads the form of a definition, not its intent. The second limit was harsher: an integer and a `Symbol` value have no singleton class, and both gave a `TypeError`. `nil` returned `NilClass`, so the helper works for `nil`. The limit that follows from these three results is this: reflection says correctly where a definition sits, but it does not always say who the author is. **MP9.** The two results for core classes were measured for only these two methods; other core methods may come out differently.

#### Summary

- Reflection is asking an object about its methods, state and type at run time; `members`, `instance_variables`, `respond_to?`, `is_a?` and `public_instance_methods(false)` are such questions.
- Code wrote `Point#x`: `Struct.new` created it while the class was being produced, and there is no `def` body.
- An ancestor wrote `Point#to_a`: the owner is `Struct`.
- A method object is queried with `arity`, `parameters` and `source_location`, and it is called with `call`.
- A reader added with `attr_reader` also counts as written by code.
- The helper gives a meaningful result only for user classes written in Ruby; the two measured core methods (`String#upcase`, `Class#new`) went to code.

#### Next Step

In this lesson, code produced the method of a class while the class was being defined, and we read the production only from its result. The next lesson does the production itself: `define_method` defines, with a block, a method whose name is decided at run time. The same question asks, this time, how code comes to be the author.

### Dynamic Method Definition

Source: https://academia.sh/en/courses/object-orientation-and-metaprogramming/dynamic-method-definition

Measuring how define_method produces methods at run time, how those methods check their parameters, and how the production classifies the author.

The previous lesson covered reflection: asking an object about its methods, state and type. `Struct.new` wrote the `Point#x` method, and the `author` helper called it **code**. There the production was a black box, and we looked only at the result. This lesson does the production itself. **Dynamic method definition** is defining a method not by writing `def` but by giving a name and a body while the program runs. In Ruby the tool for it is `define_method`. M11/K01 measured at which stage a decision is made in C; here the question is not when the definition comes into being. The question is again who wrote it.

The axis is the same: **who wrote this method?** The author in this lesson is **code**. The programmer writes a loop once, and the loop writes two methods. What leaves the programmer's hands is only the `define_method` call, not the individual method bodies. To recall the four authors: the programmer writes with `def`, an ancestor is a superclass or a module, code is a production call, and nobody means there is no definition under that name. Dynamic definition is one of the paths of code. The reason it is needed is plain too: if dozens of methods follow the same pattern, writing the pattern once and taking the names from a list is shorter than writing each method, and when a name is added to the list the method is born by itself. This lesson measures the price of that brevity: a method name that is searched for in the source may never appear in the source. The helper below is the same as in the previous lesson and is given as it is.

```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 Classes

The observation of this lesson rests on two classes. `Account` provides a method written with `def` for comparison. `Settings` takes its methods from a loop. The excerpt of the shared definitions is as follows.

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

module Reportable
  def report = "report: #{balance_text}"
end

class Account
  include Reportable

  def initialize(owner, balance)
    @owner = owner
    @balance = balance
  end

  def balance_text = "#{@owner} #{@balance}"
  def net = @balance - fee

  private

  def fee = 2
end

class Settings
  %i[color width].each do |name|
    define_method(name) { "#{name} default" }
  end
end
```

This block is source too. In the `Settings` definition, `%i[color width]` builds an array of two `Symbol` values; `each` calls `define_method` for every name and makes the block the method of that name. In the `Settings` class there is no `def` named `color` or `width`.

#### Who Wrote the Generated Method

The observation of this lesson is the `color` method of a `Settings` object. The file below calls the helper and prints the value the method produces next to it.

```ruby
# generated.rb — who wrote Settings#color
require_relative "authors"

s = Settings.new
p author(s, :color)
puts s.color
```
```
["code", "Settings, block body"]
color default
```

The author is **code**. The owner of the method turned out to be `Settings` itself, so it is not an ancestor; but the body is not a `def`, it is a block given to `define_method`. The helper read this distinction from the backtrace label starting with `block`. `Point#x` in the previous lesson was code too, but it had no body at all: the answer there was `no ruby body`, and here it is `block body`. Two different forms fall into the same category. **MP10.** Only the `color` method was measured; `width` comes from the same loop and will give the same result, but that is an assumption.

#### The Trace of Production

The body of a method object has a **backtrace label** that Ruby writes into the compiled body. The helper looks at the label and asks whether it starts with `block`. We print the label directly to compare two methods: `color`, produced by the loop, and `net`, written with `def`.

```ruby
# label.rb — the body label of two methods
require_relative "authors"

p Settings.instance_methods(false)
iseq = RubyVM::InstructionSequence
puts iseq.of(Settings.instance_method(:color)).label
puts iseq.of(Account.instance_method(:net)).label
```
```
[:color, :width]
block (2 levels) in <class:Settings>
net
```

`instance_methods(false)` listed only the public methods of `Settings` itself. The labels show the difference: `block (2 levels) in <class:Settings>` for `color`, and `net` for `net`. The two levels say that the definition sits inside two blocks: one is the `each` block, the other is the `define_method` block. The label of `net` is the name of the method itself. The `start_with?("block")` question of the helper separates these two labels. **MP11.** The label format was measured in Ruby 4.0.7; the text may change in another version, but starting with `block` is the only thing drawn from this measurement.

#### Adding a Method While Running

`define_method` takes a name and a block, binds the method to that name and returns the name as a `Symbol`. The method can be called right after that line. It works when the class is already loaded: adding a new method to a class does not require reopening it with the `class` keyword.

```ruby
# define.rb — adding and removing a method with define_method
require_relative "authors"

result = Settings.define_method(:theme) { "dark" }
p result
p Settings.new.theme
p author(Settings.new, :theme)
Settings.remove_method(:theme)
begin
  Settings.new.theme
rescue NoMethodError => e
  puts e.class
end
```
```
:theme
"dark"
["code", "Settings, block body"]
NoMethodError
```

The return value was `:theme`; the new method answered at once, and `author` classified it as **code**, just like those produced by the loop. The time of production changed and the form of the definition did not: the body is again a block. `remove_method` deleted the method from the own table of `Settings`, and the same message then raised `NoMethodError`. If an ancestor had a definition under the same name, that ancestor definition would become visible again; `remove_method` deletes only the class's own definition. **MP12.** `remove_method` was measured only in this class and with a name that has no ancestor definition; the case with an ancestor definition is not shown here.

#### Deciding the Name While Running

The name of a method written with `def` is fixed in the source. `define_method` takes the name as a string or a `Symbol`; the name can come from a variable or a computation. The class below produces one reader and one writer for each of two field names, and it never carries the method names it produces as plain text in its source.

```ruby
# form.rb — methods with computed names
require_relative "authors"

class Form
  %w[first last].each do |field|
    define_method("set_#{field}") { |value| instance_variable_set("@#{field}", value) }
    define_method("get_#{field}") { instance_variable_get("@#{field}") }
  end
end

f = Form.new
f.set_first("amy")
p f.get_first
p f.get_last
p f.instance_variables
p Form.instance_methods(false).sort
p author(f, :set_first)
```
```
"amy"
nil
[:@first]
[:get_first, :get_last, :set_first, :set_last]
["code", "Form, block body"]
```

Four methods came out of a single loop: `get_first`, `get_last`, `set_first` and `set_last`. When the written field was read, `"amy"` came back; the field that was never written gave `nil`, because that instance variable was never assigned. `instance_variables` listed only `@first`: the instance variable of the second field does not exist until a first value is written to it. The author again came out **code**. Here the programmer wrote only a pattern; code produced the four instances of the pattern. **MP13.** This class was measured with two fields; the case where the name string contains characters that are invalid in a method name was not tested.

#### Parameter Checking

A block normally behaves loosely: a missing argument becomes `nil`. The block given to `define_method` behaves like a method and checks the number of arguments. The measurement below tests this in two ways: giving the block directly, and passing a `proc` object with `&`.

```ruby
# parameters.rb — define_method blocks and argument checking
class Settings
  define_method(:double) { |n| n * 2 }
  define_method(:pdouble, &proc { |n| n * 2 })
end

s = Settings.new
p Settings.instance_method(:double).arity
p s.double(4)
p proc { |n| n }.call
[:double, :pdouble].each do |name|
  begin
    s.public_send(name)
  rescue ArgumentError => e
    puts "#{name} -> #{e.class}"
  end
end
```
```
1
8
nil
double -> ArgumentError
pdouble -> ArgumentError
```

The arity came out `1` for `double`: the generated method carries the parameter of the block as a required parameter. `double(4)` gave `8`. The third line shows the contrast: an ordinary `proc` called without an argument raised no error and returned `nil`. The block of the same form inside `define_method` raised `ArgumentError`; passing the `proc` object with `&` did not change this, and `pdouble` raised the error too. So at the moment of production the block moves to the rules of a method. **MP14.** Only blocks with a single required parameter were measured; blocks with default values and variable arguments were not tested in this lesson.

#### Variables the Block Sees

The most useful difference between `define_method` and `def` is that the block sees the local variables around it. A **closure** is a block carrying the local variables of the place where it was defined, even when it is called later. `def` opens a new scope and does not see the local variables around it; a block does.

```ruby
# counter.rb — a define_method block sees the variable around it
class Counter
  total = 0
  define_method(:bump) { total += 1 }
  def peek = defined?(total).inspect
end

a = Counter.new
b = Counter.new
p [a.bump, a.bump, b.bump]
puts a.peek
```
```
[1, 2, 3]
nil
```

Three calls gave `[1, 2, 3]`. `total` was a local variable in the class body, and the `bump` block carried it as a closure. The counter was shared between the two objects: the first call on the second object gave `3`, because the variable is not in the object, it is in the closure of the block. `peek`, written with `def`, could not see the same name; `defined?(total)` returned `nil` there, and `inspect` printed it as `"nil"`. For this reason the state of a method produced with `define_method` may not be in instance variables; the `instance_variables` call of reflection does not show this counter. **MP15.** The closure of the block was measured with only one local variable in this class body; sharing the closure among other methods is not shown in this example.

#### The Hook

When a new method is added to a class, Ruby calls the `method_added` method of the class. A **hook** is a method defined to run when a certain event happens. The class below writes this hook and collects the added names in a list. Methods are added in three different ways: `def`, `define_method` and `attr_reader`.

```ruby
# hook.rb — in which ways method_added fires
class Jar
  @added = []

  class << self
    attr_reader :added

    def method_added(name)
      @added << name
    end
  end

  def a = 1
  define_method(:b) { 2 }
  attr_reader :c
end

p Jar.added
```
```
[:a, :b, :c]
```

All three ways fired the hook the same way: `[:a, :b, :c]`. The hook does not know by which way the method was produced; it receives only the name. This must be read together with the observations of the earlier lessons: the firing of the hook does not tell the author. What tells the author is the form of the body of the definition, and the hook does not see it. So the hook is good for watching where methods are added, but it does not answer the question of who wrote it. **MP16.** The hook was measured only in this class and while the class body was running.

#### The Accessor Also Produces Code

`attr_accessor` produces a reader and a writer method with the same name. **Accessor** is the joint name of these two methods; only the reader is called `attr_reader`, and only the writer `attr_writer`.

```ruby
# access.rb — attr_accessor produces two methods
require_relative "author"

class Access
  attr_accessor :color
end

p Access.instance_methods(false).sort
p author(Access.new, :color)
p author(Access.new, :color=)
```
```
[:color, :color=]
["code", "Access, no ruby body"]
["code", "Access, no ruby body"]
```

Two methods came out: `color` and `color=`. The author of both is **code**, but the form of the body differs from `define_method`: there is no label, because `attr_accessor` leaves no Ruby body. No `def` was written in the `Access` class. `block body` and `no ruby body` are the two sub-forms of code. **MP17.** Only `attr_accessor` was measured; `attr_writer` alone was not measured.

#### The Bounding Measurement

So far code has meant a `define_method` or `attr_*` call. There is one more form of production: making a class interpret a string as Ruby code. `class_eval` runs a string in the context of the class. The measurement below does the same job in two ways: one with a string, the other with a block.

```ruby
# limit.rb — the same job, two ways of production
require_relative "authors"

Settings.class_eval "def from_string = 4"
Settings.define_method(:from_block) { 4 }

s = Settings.new
p [s.from_string, s.from_block]
p author(s, :from_string)
p author(s, :from_block)
```
```
[4, 4]
["programmer", "Settings, def body"]
["code", "Settings, block body"]
```

Both methods gave `4`, but their authors split. The method produced from the string was created at run time, yet `author` classified it as **programmer**, because there is a `def` body inside the string. The one produced with `define_method` is **code**. The rule reads the form of a definition, not its intent: the category measures the form in which the definition is written, not when the production happened. So production by code does not always fall to code. The reverse holds too: seeing `define_method` in the source does not say the author is code, and a `def` written inside a string falls to the programmer even though it was produced at run time. Reflection sees the owner of the method and the form of its body; it does not see the call that started the production. Tracing the production is therefore less a matter of looking at the label than of searching the source for `define_method` and `class_eval` calls. **MP18.** Production from a string was measured only with a simple one-line `def`; in more complex strings the label and the body form may differ.

#### Summary

- `define_method` takes a name and a block, binds the method to that name and returns the name as a `Symbol`; the new method can be called at once.
- The author of a generated method is code: its body is a block, and its backtrace label starts with `block`. The label of a method written with `def` is the name of the method.
- The block of `define_method` checks the number of arguments like a method; an ordinary `proc` does not.
- The `method_added` hook fires the same for `def`, `define_method` and `attr_reader`; the hook does not tell the author.
- `attr_accessor` produces two methods and the author of both is code, but the form of the body is different.
- A `def` produced from a string with `class_eval` comes out programmer: the category measures the form of the definition, not the moment of production.

#### Next Step

In this lesson we defined the methods ourselves, before they were called. The next lesson measures the opposite: what Ruby does when no definition exists for the called name, and how a class can fill that gap itself. In that lesson the fourth category of the author, nobody, appears.

### Catching Missing Methods

Source: https://academia.sh/en/courses/object-orientation-and-metaprogramming/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.

### Domain-Specific Languages

Source: https://academia.sh/en/courses/object-orientation-and-metaprogramming/domain-specific-languages

Measuring which author hides behind a declaration line such as field :price, 0, where class methods are looked up, and where the block form breaks.

The previous lesson showed that a name can be **nobody** first and **code** afterward. This lesson uses the same transition on purpose: a single line such as `field :price, 0` looks like a call when it is read, but when it runs it adds a new method to the class. A **domain-specific language** is a form of writing that is reduced for a particular job and speaks with the words of that job. In Ruby one way to build it is declaration methods called in a class body. The shortness of the line does not make measuring it unnecessary; on the contrary, behind such lines it is not obvious at first glance who wrote the method.

The axis has been the same since the start of the course: **who wrote this method?** The author in this lesson is **code**. The programmer wrote the line `field :price, 0`, but the `price` method this line gave birth to is written by Ruby code, that is, by a `define_method` call. The measurements below draw this distinction; at the end, the place where the block form breaks is shown. The `author` helper is given in its form from 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 is on two classes. `Model` carries the declaration method; `Product` uses it.

```ruby
# model.rb — a base class that carries a declaration method
require_relative "author"

class Model
  def self.field(name, default)
    define_method(name) { instance_variable_get("@#{name}") || default }
  end
end

class Product < Model
  field :name, "unnamed"
  field :price, 0
end
```

This block is source too. `field` takes a name and a default value and defines a reader method for the name. The reader returns the instance variable if it is filled, and the default if it is empty. The two lines in the `Product` body are calls, not definitions: there is not a single `def` in the class.

#### What the Declaration Line Wrote

The observation of the lesson is the `Product.new.price` message. First what the method returns is measured, then who wrote it and what appears in the class.

```ruby
# measurement.rb — the method the declaration line gave birth to
require_relative "model"

pr = Product.new
p pr.price
p pr.name
p author(pr, :price)
p Product.instance_methods(false).sort
```
```
0
"unnamed"
["code", "Product, block body"]
[:name, :price]
```

The method returned `0`, because the instance variable was empty and the default came into play. The author came out **code**: `author` found the definition in the link of the chain that belongs to `Product`, and the label of the instruction sequence pointed to a block body. The class's own method list is `[:name, :price]`; two declaration lines gave birth to two methods. The programmer wrote only two lines, but the interface of the class is two methods. **MP30.** This measurement is for a single declaration method and two fields; had `field` been written with another body, the author could have changed.

#### A Call, but the Author Is Code

What the method really reads is the instance variable. When the variable is filled, the default should stay behind.

```ruby
# value.rb — the default and a filled value
require_relative "model"

pr = Product.new
pr.instance_variable_set(:@price, 40)
p pr.price
p Product.new.price
p author(Product, :field)
```
```
40
0
["ancestor", "#<Class:Model>"]
```

The filled variable beat the default: `40`. A new object of the same class gave `0`, because the default belongs to the class and the value belongs to the object. The last line separates the owner from the receiver: the author of the `field` method came out **ancestor**, and the place appeared as the singleton class of `Model`. The class `Product` was given as the receiver; the definition of `field` sits not in the own singleton class of `Product` but in that of `Model`. Because the owner was not the singleton class of the receiver, the rule counted it as an ancestor; so two different authors work in the same declaration line. The method of the `field` call was written by an ancestor, and the `price` method that call gave birth to was written by code. **MP31.** This distinction rests on the singleton class rules of the `author` helper; another helper could classify differently.

This must not be confused with the question in M08/K06. There the question was the cause of a behavior in the source: a call, a comment or a default. Here the cause is not asked; the author of the definition is. `field :price, 0` is a call, but the author of the method it gave birth to is code.

#### Reading Leaves No State

Whether the generated method has a side effect can be measured too. The default is computed inside the method; it is not written to the object.

```ruby
# read.rb — the trace the reader leaves on the object
require_relative "model"

pr = Product.new
p pr.price
p pr.instance_variables
p pr.method(:price).owner
p pr.method(:price).arity
```
```
0
[]
Product
0
```

`price` was read and gave `0`, but the list of the object's instance variables stayed empty: the default came back as a value and was not stored as a field. The owner of the method is `Product`, so the declaration put the definition in the subclass, not in `Model`. The arity is zero: the block given to `define_method` takes no parameter, so the generated reader takes none either. These three observations complete what the declarative interface wrote into the class: a new method, the subclass as its owner, and no state. **MP32.** This measurement is only in the reading direction; there is no method in this `field` definition that writes the value, and the value was given only with `instance_variable_set`.

#### Where a Class Method Is Looked Up

Why the `Product.field` call reaches `Model` can be read from the singleton class chain.

```ruby
# chain.rb — the chain of class methods
require_relative "model"

p Product.singleton_class.ancestors.take(3)
p Model.singleton_class.instance_methods(false)
p Product.respond_to?(:field)
p Product.new.respond_to?(:field)
```
```
[#<Class:Product>, #<Class:Model>, #<Class:Object>]
[:field]
true
false
```

The first link of the chain is the own singleton class of `Product`, and the second is that of `Model`. Class methods come by inheritance because the singleton class of a subclass descends from the singleton class of the superclass. `Product.field` was found in the second link of this chain. Writing the declaration method in the base is therefore enough: every subclass can call it in its own body. In the singleton class of `Model` there is a single method, `field`. The class `Product` answers it (`true`), but an object of `Product` does not (`false`): the declaration belongs to the interface of the class, not to the interface of the object. The object sees only the `name` and `price` methods that the declaration gave birth to. This distinction also says why a domain-specific language lives in the class body: while the declaration is being written, what is spoken to is the class, not the object. **MP33.** The output shows only the first three links; the rest of the chain was not measured in this lesson.

#### The Block Form

For taking declarations out of the class body there is another setup: a block is given, and the block is run in the context of an object.

```ruby
# form.rb — the block form with instance_eval
class Form
  attr_reader :fields

  def self.build(&block)
    f = new
    f.instance_eval(&block)
    f
  end

  def initialize
    @fields = []
  end

  def field(name)
    @fields << name
  end
end

f = Form.build do
  field :name
  field :email
end
p f.fields
```
```
[:name, :email]
```

The receiver of the `field` calls inside the block was not written; `instance_eval` ran the block in the context of the `Form` object, which means the `self` of the block became that object. The result is `[:name, :email]`. This form is readable: the user writes only the words, without writing an object name. Readability has a price, and it is measured below. **MP34.** There was a single `field` method and two calls here; no other method calls inside the block are in this measurement.

#### The Bounding Measurement

`instance_eval` changes the `self` of the block. What happens to the variables outside the block? Three are looked at: an instance variable defined outside, a local variable and `self` itself. Then the `yield` form of the same setup is tried.

```ruby
# limit.rb — instance_eval and outer variables
@title = :record
local_name = :local

class Form
  attr_reader :fields

  def self.build(&block)
    f = new
    f.instance_eval(&block)
    f
  end

  def initialize
    @fields = []
  end

  def field(name)
    @fields << name
  end
end

class Form2
  attr_reader :fields

  def self.build
    f = new
    yield f
    f
  end

  def initialize
    @fields = []
  end

  def field(name)
    @fields << name
  end
end

p Form.build { field @title }.fields
p Form.build { field local_name }.fields
p Form.build { field self.class }.fields
p Form2.build { |f| f.field @title }.fields
```
```
[nil]
[:local]
[Form]
[:record]
```

There are three results. The outer `@title` gave `nil` inside the block: instance variables are tied to `self`, and `self` changed. The local variable `local_name` gave `[:local]`: local variables are caught by the closure of the block and are not tied to `self`. `self.class` came out `Form`. The `yield` form on the last line kept `self`, and the value of `@title` was read as `[:record]`. So `instance_eval` gains vocabulary but makes the outer instance variables invisible. **MP35.** This difference was measured only for instance variables; the behavior of constants and class variables was not tested in this lesson.

The declaration also has a silent price: declaring the same field twice raises no error.

```ruby
# repeat.rb — declaring the same field twice
require_relative "model"

class Label < Model
  field :name, "unnamed"
  field :name, "new"
end

p Label.new.name
p Label.instance_methods(false)
```
```
"new"
[:name]
```

The second declaration wrote over the first: `name` now gives `"new"`, and there is still a single `name` method in the class. There is no warning and no exception. The declarative interface is therefore easy to write and easy to write wrongly; `define_method` silently redefines the same name. **MP36.** This measurement is the case where both declarations are in the same class; had they been at different inheritance levels, the subclass would have shadowed the method of the superclass.

#### Summary

- The line `field :price, 0` is a call; the author of the `price` method it gave birth to is **code**, because `define_method` produces a block body.
- The declaration method `field` is written in `Model` and, being a class method, is looked up in the singleton class chain of `Product`; the author of that method is an **ancestor**.
- `instance_eval` changes the `self` of the block; outer instance variables become `nil`, and local variables stay visible.
- An object passed with `yield` keeps `self`; for the sake of vocabulary, outer instance variables are given up.
- Declaring the same field twice raises no error; the later declaration silently replaces the earlier method.
- The author of this lesson is code: the lines of a declarative interface belong to the programmer, and the methods they give birth to belong to code.

#### Course Wrap-Up

Every lesson of the course asked the same question: who wrote this method? Nine observations answered this question nine times with a measurement. The table below collects them all by author category.

| Lesson | Measured method | Author | Bounding measurement |
| --- | --- | --- | --- |
| Class Definition and Instance Variables | `account.balance_text` | programmer | `attr_reader` is a second author in the same class |
| Inheritance and `super` | `TermDeposit.new("tom", 50).balance_text` | ancestor | a redefinition that calls `super` is programmer |
| Modules and Mixins | `account.report` | ancestor | the class's `def` shadows the mixin |
| Visibility | `fee` (from inside `account.net`) | programmer | `public :fee` moves the owner of the definition to the subclass |
| The Method Lookup Chain | `Safe.new.unlock` | ancestor | a singleton method on one object goes first |
| Reflection | `Point.new(3, 4).x` | code | core methods also look like code |
| Dynamic Method Definition | `Settings.new.color` | code | a `class_eval` string comes out programmer |
| Catching Missing Methods | `Proxy.new([3, 1, 2]).sort` | nobody | nobody turns into code on the first call |
| Domain-Specific Languages | `Product.new.price` | code | `instance_eval` changes `self` |

The distribution of the authors: **3 ancestor · 3 code · 2 programmer · 1 nobody**. The table carries one finding of the course: the author was measured per method, not per class, and the author of a method was decided not by how it looks in the source text but by how it is found in the chain. Two authors appeared in the same class, and two authors appeared in the same line.

Two inferences can be drawn. The first is that knowing the author does not require knowing the way of writing: `attr_reader` and `define_method` are different words, but both make code the author; `def` and a `class_eval` string are different words, but both make the programmer the author. The second is that reflection is not enough to tell the author: `source_location`, `owner` and `respond_to?` can each be misleading on their own; the rule read them together. The category nobody fell on a single example, and that one was not permanent. A third inference comes from the distribution itself: in only two of nine observations is the author the programmer. In the measured classes, most of the interface came from ancestors, mixins and definitions produced by code; looking at the class body is therefore not enough by itself to find the author, and the chain and the form of the definition must be measured.

**MP37.** The table is only the classification of nine measurements; the count of the categories reflects the choice of measured methods, not the general distribution of methods in Ruby. The author cells were taken from the statement in the Summary section of each lesson.

The course ends here. The next course takes up the four layers of a web application written in Ruby, in order: routing, controller, model and view. There the same rule stays in force: nothing is guessed, everything is measured.

