Modules are a very powerful feature of Ruby. Primarily, they are used for two reasons: namespacing your code and sharing common methods across different classes (as mix-ins).
Using modules, you can create classes (or modules) with the same name without interfering with other classes. This allows you to design modular programs, breaking large components into smaller ones and also mixing and matching object behaviors.
# this module holds all the code
# related to tagging items.
module Taggable
end
# module as namespace
module Blog
class Post
include Taggable # module as mix-in
end
class Comment
include Taggable # module as mix-in
end
end
You can also nest multiple modules to create nested namespaces. There are two ways to define nested modules (or classes) in Ruby.
- Define the second module inside the first one. If module A doesn't exist, Ruby will create it first, then define module B. If module A exists, then it will re-open it and define module B inside it.
module A
module B
end
end
- Use the
::
operator to define the second module on the same line.
module A::B
end
There are three ways in which the second version (A::B
) differs from the first:
- It assumes that the outer module, i.e.
A
is already defined. If it's not, Ruby will throw aNameError: uninitialized constant A
error. So use this syntax only if you're sure that module A exists. - It allows you to reduce the amount of indentation. Instead of 3 levels of indentation, only one is necessary.
- You can not access the constants defined inside module A inside module B without the scope resolution operator. This is a significant difference; the next section covers it in detail.
Accessing Constants in Nested Modules
The scope of constant access is different depending on how the nested module is defined. Let's consider the first version.
module A
Z = 10
module B
puts Z
end
end
# output
10
You can define both modules at two separate locations, and it should still work.
module A
Z = 10
end
# somewhere else, after the above module is defined
module A
module B
puts Z
end
end
# output
10
Let's try accessing the constant in a module defined using the second syntax. It throws a NameError
.
module A
Z = 10
end
module A::B
puts Z
end
# output
uninitialized constant A::B::Z (NameError)
However, you can access it using the scope resolution operator (::
) as follows:
module A
Z = 10
end
module A::B
puts A::Z
end
# output
10
Trying to access a constant defined in the parent module without ::
operator fails with the second syntax.
The following example explains this point using classes and methods.
module A
PI = 3.14
module B
def get_pi = PI # method shorthand for one-liner methods
end
end
class C
include A::B
end
assert_equal 3.14, C.new.get_pi # Passes!
However, if you use ::
to define A::B
without nesting it inside A
, a NameError
exception will be raised.
module A
PI = 3.14
end
module A::B
def get_pi = PI
end
class C
include A::B
end
assert_raises(NameError) { C.new.get_pi } # uninitialized constant A::B::PI (NameError)
The 'Module.nesting' Method
The nesting
method defined on the Module
returns the list of modules nested at the time of call.
The following example shows how the two versions differ in their module nesting.
module A
p Module.nesting # [A]
module B
p Module.nesting # [A::B, A]
end
module B::C
p Module.nesting # [A::B::C, A]
end
end
module A::D
p Module.nesting # [A::D]
end
When module D is defined, the nesting does not include module A. Hence, module D cannot access the constants defined in module A, without explicitly using the scope resolution (::
) operator.
I hope you found this article useful and that you learned something new.
If you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. I look forward to hearing from you.
Please subscribe to my blog if you'd like to receive future articles directly in your email. If you're already a subscriber, thank you.