Modules & Mixins
Reviewed & published by Brayan K
Ruby is a dynamic, beginner-friendly programming language, and modules let you both namespace your code and share behaviour across classes through mixins — Ruby's elegant alternative to multiple inheritance.
Part of the free Ruby course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.
By the end of this lesson you'll create modules, use them as namespaces, and mix shared methods into classes with include.
What You'll Learn in This Lesson
- Defining a module with methods and constants
- Using modules as namespaces with ::
- Mixing in instance methods with include
- Composing several mixins into one class
- Built-in mixins: Comparable and Enumerable
- Mixins vs inheritance
1️⃣ Modules as Namespaces and Toolboxes
A module groups related constants and methods. You can call its self. methods directly and reach inside with the :: operator. As a namespace, a module lets two libraries each define a Track without colliding.
# A module groups related methods and constants.
# Used this way, it's a NAMESPACE and a toolbox of helper methods.
module MathTools
PI = 3.14159
# module_function methods are called on the module itself.
def self.circle_area(r)
PI * r * r
end
def self.double(n)
n * 2
end
end
puts MathTools::PI # -> 3.14159 (:: reaches into the module)
puts MathTools.circle_area(2) # -> 12.56636
puts MathTools.double(21) # -> 42
# Namespacing also prevents name clashes between libraries:
module Audio
class Track; end
end
module Video
class Track; end # a different Track — no conflict!
end
puts Audio::Track.new.class # -> Audio::Track2️⃣ Mixins: Sharing Behaviour with include
The real power is the mixin. Define instance methods in a module, then include it in any class to give every instance those methods. A class can include many modules. Ruby's own Comparable mixin turns a single <=> method into a full set of comparison operators.
# A MIXIN shares instance methods with any class via "include".
module Greetable
def greet
"Hi, I'm #{name}" # relies on the including class having a name method
end
end
module Farewell
def bye
"Goodbye from #{name}"
end
end
class Person
include Greetable # mix in greet
include Farewell # mix in bye (multiple mixins are fine!)
attr_reader :name
def initialize(name)
@name = name
end
end
p = Person.new("Ada")
puts p.greet # -> Hi, I'm Ada (method came from the module)
puts p.bye # -> Goodbye from Ada
puts Person.ancestors.include?(Greetable) # -> true
# Ruby's built-in Comparable mixin gives <, >, == from one <=> method:
class Version
include Comparable
attr_reader :num
def initialize(num); @num = num; end
def <=>(other); num <=> other.num; end
end
puts (Version.new(2) > Version.new(1)) # -> trueYour turn. Give a Duck both walk and swim by mixing in two modules. Complete the TODOs, then run it.
# 🎯 YOUR TURN — replace each TODO, then run with: ruby walkable.rb
# 1) Define a module "Walkable" with a method "walk"
module Walkable
def walk
"#{name} is walking" # 👉 TODO: keep using name
end
end
# 2) Define a module "Swimmable" with a method "swim"
module Swimmable
def swim
"#{name} is swimming"
end
end
# 3) Mix BOTH into a Duck class
class Duck
include Walkable # 👉 TODO: include Walkable
include Swimmable # 👉 TODO: include Swimmable
attr_reader :name
def initialize(name)
@name = name
end
end
donald = Duck.new("Donald")
puts donald.walk
puts donald.swim
# ✅ Expected output:
# Donald is walking
# Donald is swimmingMini-Challenge: A Describable Mixin
Write one Describable module and share it between two unrelated classes — the essence of a mixin. Run with ruby describable.rb.
# 🎯 MINI-CHALLENGE: a Describable mixin.
# Save as describable.rb and run with: ruby describable.rb
#
# 1. Define a module Describable with a method "describe" that returns
# "<class name>: <name>" (hint: self.class gives the class).
# 2. Make TWO classes (e.g. Car and Bike) that each include Describable
# and have an attr_reader :name set in initialize.
# 3. Create one of each and call describe on both.
#
# ✅ Example output:
# Car: Tesla
# Bike: BMX
# your code hereCommon Errors (and the fix)
- Calling .new on a module — Modules can't be instantiated. Use a class, or call the module's self. methods.
- include vs extend — include adds instance methods; extend adds class methods. Pick by where you need them.
- Mixin method relies on a missing method — greet needs name; the including class must provide it.
- Wrong constant access — Reach module constants with MathTools::PI, not a dot.
- Comparable without <=> — Including Comparable requires you to define <=>, or comparisons raise.
Pro Tips
- 💡 Lean on built-ins: Comparable and Enumerable give huge functionality from one method.
- 💡 Check ancestors: Klass.ancestors shows the lookup order including mixins.
- 💡 Prefer composition: small focused mixins beat tall inheritance trees for sharing behaviour.
📋 Quick Reference — Modules & Mixins
| Concept | Ruby Syntax |
|---|---|
| Define module | module Name ... end |
| Module method | def self.helper |
| Reach inside | Name::PI |
| Mix in (instance) | include Module |
| Mix in (class) | extend Module |
| Lookup order | Klass.ancestors |
🎉 Lesson Complete!
- ✅ Modules group methods/constants and can't be instantiated
- ✅ As namespaces, they prevent name clashes (::)
- ✅ include mixes instance methods into a class
- ✅ A class can compose many mixins
- ✅ Comparable/Enumerable are powerful built-in mixins
Practice quiz
What is a key difference between a module and a class?
- modules are faster
- a class cannot have methods
- a module cannot be instantiated with .new
- modules cannot hold constants
Answer: a module cannot be instantiated with .new. You cannot call .new on a module; modules namespace or mix in behaviour.
Which operator reaches a constant inside a module, like PI?
- double colon, MathTools::PI
- a dot, MathTools.PI
- an arrow, MathTools->PI
- a hash, MathTools#PI
Answer: double colon, MathTools::PI. Use :: to reach module constants, e.g. MathTools::PI.
What does include do?
- adds class methods
- creates a subclass
- deletes methods
- adds the module's instance methods to the class
Answer: adds the module's instance methods to the class. include mixes a module's instance methods into the class.
How many modules can a single class include?
- zero
- as many as you like
- exactly one
- exactly two
Answer: as many as you like. A class can compose many mixins via multiple include statements.
What does extend add to a class instead of include?
- class methods
- instance methods
- constants
- nothing
Answer: class methods. extend adds the module's methods as class (singleton) methods.
Including Comparable requires you to define which method?
- each
- to_s
- <=>
- compare
Answer: <=>. Comparable builds <, >, == and more on top of your <=> method.
Including Comparable and defining <=> gives you which methods for free?
- map and select
- <, >, ==, between?, clamp
- push and pop
- each and count
Answer: <, >, ==, between?, clamp. Comparable derives the full comparison set from <=>.
How does a mixin differ from inheritance?
- mixins are single, inheritance is multiple
- they are identical
- mixins require a parent class
- inheritance is single (one superclass); mixins can be many
Answer: inheritance is single (one superclass); mixins can be many. A class has one superclass but can include many modules.
A mixin method like greet that uses name requires that:
- name is a global variable
- the including class provides a name method
- name is a constant
- you call extend instead
Answer: the including class provides a name method. The mixin relies on the host class supplying the name method.
Which call shows the method-lookup order including mixins?
- Klass.methods
- Klass.parents
- Klass.ancestors
- Klass.lookup
Answer: Klass.ancestors. ancestors lists the lookup chain, including included modules.
Continue this course
- Previous: Attributes (attr_accessor)
- Next: Exception Handling — Rescue errors gracefully with begin/rescue/ensure
- Quick reference: Ruby cheat sheet › Classes & Modules
Frequently asked questions
What's the difference between a module and a class?
A class is a blueprint you instantiate with .new to create objects. A module is not instantiated — you can't call SomeModule.new. Modules serve two purposes: as a namespace that groups related classes and constants to avoid name clashes, and as a mixin that packages methods to share across multiple classes. Think of a class as 'a kind of thing' and a module as 'a bundle of behaviour or a container of names'.
What is a mixin and how does include work?
A mixin is a module whose methods you 'mix into' a class with include. Once included, every instance of that class gains the module's instance methods as if they were defined right in the class. include Greetable means any Person can call greet. This is Ruby's answer to sharing behaviour without deep inheritance chains: a class can include many modules, composing capabilities from several sources.
How is a mixin different from inheritance?
Inheritance models an 'is-a' relationship and is single — a class has exactly one superclass. Mixins model a 'can-do' capability and are multiple — a class can include any number of modules. So a Duck might inherit from Bird (it is a bird) while mixing in Walkable and Swimmable (it can walk and swim). When you find yourself wanting to share the same behaviour across unrelated classes, a mixin is usually the cleaner tool than forcing them under a common parent.
What are Comparable and Enumerable, and why include them?
They are built-in modules that give you a lot of functionality for a little work. Include Comparable and define a single <=> method, and you instantly get <, >, ==, between?, and clamp. Include Enumerable and define each, and you get map, select, reduce, sort, and dozens more. They're the textbook example of mixins: define the one method that varies, and the module supplies all the methods built on top of it.