A module is a container for grouping related methods, constants, and classes. It can be stored in a file and reused across multiple Ruby programs.
For example, suppose we have two .rb
files — math_utilities.rb
and main.rb
:
- In
math_utilities.rb
, we define a method to compute the square of a number. - Then, in
main.rb
, we load that module and use thesquare
method.
Here, math_utilities.rb
defines a module that can be reused elsewhere.
Don't worry if you don't understand this yet. We will learn about these in detail below.
Ruby Modules
Let's consider the two .rb
files we discussed previously: math_utilities.rb
and main.rb
.
math_utilities.rb (The Module)
This file defines a module named MathTools
. A module groups together related methods and makes it available for use by other files. The MathTools
contains the following:
module MathTools
# Method that calculates square of a number
def self.square(number)
number * number
end
end
Notice that we have defined the method using self.
to make it accessible as a module method.
main.rb (Using the Module)
This file uses the method defined in the math_utilities.rb
module. Inside the main.rb
file, we import the square
method as:
# Import the MathTools module from math_utilities.rb
require_relative 'math_utilities'
# Use the imported method to compute the square
puts MathTools.square(4) # Output: 16
Here, we used require_relative
to load the module and accessed the square
method using MathTools.square
.
Note: require_relative
loads a file relative to the current file. It's commonly used to include modules defined in other files.
Constants in Modules
Modules can also hold constants. These are accessed using the following syntax:
ModuleName::CONSTANT_NAME
Example
module AppInfo
VERSION = "1.0.0"
end
puts AppInfo::VERSION
# Output: 1.0.0
Note: Constants are useful for storing configuration, settings, or fixed values.
Defining Multiple Members in a Module
It's also possible to define multiple members (methods or constants) in a module. For example, let's see how to do that in our math_utilities.rb
file:
module MathTools
PI = 3.14159
def self.square(number)
number * number
end
end
In main.rb
file,
require_relative 'math_utilities'
puts MathTools::PI # Output: 3.14159
puts MathTools.square(6) # Output: 36
Here,
PI
is a constant.square
is a module method.- Both are accessed using the module name
MathTools
.
Using include to Add Instance Methods
You can use include
to make module methods available as instance methods in a class. For example,
module Greet
def say_hello
"Hello!"
end
end
class User
include Greet
end
user = User.new
puts user.say_hello
Output
Hello!
Here, include
mixes the module's methods into the class as if they were written directly in it.
Using extend to Add Class Methods
To use module methods as class methods, you can use extend
. For example,
module Greet
def say_hi
"Hi!"
end
end
class Admin
extend Greet
end
puts Admin.say_hi
Output
Hi!
Here, extend
adds the module's methods to the class itself, so they become class methods.
Using prepend to Override Class Methods
You can use prepend
to override class methods with module methods. For example,
module Logger
def log
"From module"
end
end
class Service
def log
"From class"
end
end
class App < Service
prepend Logger
end
puts App.new.log
Output
From module
Here, prepend
places the module before the class in the method lookup chain, so the module's log
method overrides the one in the class.
Avoiding Naming Conflicts Using Namespaces
If different modules define methods or classes with the same name, Ruby allows namespacing to avoid conflicts. For example,
module Admin
class User
def role
"admin"
end
end
end
module Guest
class User
def role
"guest"
end
end
end
puts Admin::User.new.role
puts Guest::User.new.role
Output
admin guest
Here, the User
class exists inside both Admin
and Guest
modules, but there's no conflict because of namespacing.
More on Ruby Module
Some of the benefits of using modules are:
- Improved Maintainability: Code is organized into separate files based on functionality, making it easier to manage and update.
- Enhanced Reusability: Modules are designed to be reusable, allowing you to define functionality once and use it across multiple parts of your application or in different projects.
- Clear Dependencies: By using imports and exports, modules clearly outline their dependencies, thus simplifying debugging and testing.
You can include as many modules as needed in a class. For example,
module A
def greet
"Hi from A"
end
end
module B
def bye
"Bye from B"
end
end
class Person
include A
include B
end
p = Person.new
puts p.greet
puts p.bye
Output
Hi from A Bye from B
This allows the Person
class to use methods from both modules as if they were defined inside it.
Modules can include other modules to share behavior. For example,
module A
def greet
"Hello"
end
end
module B
include A
end
class MyClass
include B
end
puts MyClass.new.greet
# Output: Hello
This allows module B
(and anything that includes B
) to also gain methods from module A
.