Modules and Mixins
How Ruby shares behavior across unrelated classes without inheritance, using include and extend.
2 min read
Ruby doesn't support inheriting from more than one class, but it has a different tool for sharing behavior across classes that aren't related by an "is-a" hierarchy: modules.
A module as a namespace
A module can simply group related methods and constants under one name, to avoid naming collisions:
module MathUtils
PI = 3.14159
def self.square(n)
n * n
end
end
MathUtils.square(4) # 16
puts MathUtils::PI # 3.14159Used this way, a module works like a namespace — MathUtils.square is called directly on the module, similar to a class method, and you'll never instantiate a module with .new (it isn't possible).
Mixins: sharing behavior with include
The more distinctive use of modules is mixing their methods into a class with include:
module Greetable
def greet
"Hello, I'm #{name}!"
end
end
class Person
include Greetable
def initialize(name)
@name = name
end
def name
@name
end
end
Person.new("Ada").greet # "Hello, I'm Ada!"include Greetable makes every method in Greetable available as an instance method on Person, as if it had been defined directly in the class. Notice greet calls name — a method the module doesn't define itself, but expects the including class to provide. This is a common mixin pattern: the module supplies shared behavior, the host class supplies the specific pieces that behavior depends on.
extend: mixing in as class methods instead
include adds instance methods; extend adds the module's methods as methods on the class (or object) itself:
module Describable
def description
"This is #{self}"
end
end
class Widget
extend Describable
end
Widget.description # "This is Widget"Why not just use inheritance?
Ruby classes can only have one superclass, but a class can include any number of modules. This matters because a lot of shared behavior doesn't fit a single "is-a" hierarchy — a Car and an Airplane aren't related by inheritance, but both might be Trackable (have a GPS position) and both might be Serializable (convert to JSON). Modules let you mix in exactly the behaviors a class needs without forcing an artificial inheritance tree.
module Trackable
def track_location(lat, lng)
"Now at #{lat}, #{lng}"
end
end
module Serializable
def to_json_string
"{ ... }"
end
end
class Car
include Trackable
include Serializable
endRuby's standard library modules
You'll encounter mixins constantly even before writing your own: Comparable (gives you <, >, between? once you define <=>) and Enumerable (gives you map, select, reduce once you define each) are both standard-library modules, and understanding include is what makes those two — covered properly in the Collections section — make sense.
Test what you just learned
4 quick questions. Get all of them right to unlock the next lesson.
You can take the quiz without an account — logging in just lets your result count toward your progress.