---
title: 'Modules and Mixins'
source: 'https://academia.sh/en/courses/object-orientation-and-metaprogramming/modules-and-mixins'
course: 'Object Orientation and Metaprogramming'
language: en
updated: '2026-10-21T05:03:42+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Show with ancestors and include? that a module enters the ancestor chain of a class through include'
    - 'Measure that a module has no instances and that a call to new raises NoMethodError'
    - 'Show that when two modules carry the same name, the order in the chain decides the result'
    - 'Measure that mixing a module into a single object with extend does not affect other objects'
---

# 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.
