Modules in Ruby

The one that cannot be instantiated like the Class!

Juzer Shakir
CodeX
5 min readJul 6, 2021

--

Prerequisite

To better understand this topic, I would recommend that you have a solid knowledge of Classes in Ruby.

Definition

Module is a collection of constants, methods, classes, and variables in a container.

Topics covered in this article

It's similar to a Class but the key difference is that modules cannot be instantiated like the Classes.

A module is initiated by module keyword followed by ‘module_name’, Testwith an initial letter as an uppercase, because they’re constants. And finally ending its scope by end keyword.

Nested modules

Yes! Modules can be nested too, just as nested classes or nested methods.

We can directly access the inner module Inner, by calling its parent module Outer followed by a double colon operator :: followed by name of the inner module. (Line #9)

We can also reopen the module just like we can reopen Rubys’ built-in classes or our own defined classes, and directly access the inner module, as shown in line #12. This style benefits the programmer to reduce the number of indentations from 2 lines to 1.

Constants in a Module

We access constants of a module by giving module_name, Test, followed by double colon operator, ::, followed by constant_name, Module_constant.

The Module_constant is a constant object which is available to be accessed by the methods, classes, and modules defined inside the module Test. However, the constant is not accessible outside the scope of the module Test.

Methods in a Module

Defining & accessing both instance and module methods is similar to how we do for the Classes.

Since we cannot instantiate a Module as a Class, accessing instance methods will be later discussed in this article in section Mixin.

Class in a Module

Just as we can access constants from the Module, we can access classes outside the Module's scope.

Why use a Module instead of a Class?

For these 2 reasons :

  1. Namespace
  2. Mixin

Let’s discuss it:

Namespace

Namespace is a way of bundling logically related objects together. It allows classes or modules with conflicting names to co-exist while avoiding collisions.

An example of this is the Rails module.

Here Application class is defined within the scope of a Rails Module.

As the Application class is a pretty common class name that we could encounter in another gem, so to avoid clashing of similar names, the Application class is encapsulated in the Rails module.

This means that class Rails::Application will never clash with an Application class or Other_gem::Application class defined anywhere else.

Mixin

Mixin is a facility in which a module can be called in another module or class by using include, prepend and extend keywords.

Include:

Here, we create a class Klass outside the scope of the module Mod, and we make Mod modules’ instance methods available as an instance to the class Klass.

When we call .ancestors method to the Class Klass, it gives a list of classes and modules that Class Klass descends from.

Before we call include, the class Klass directly descends from Object, but after include call, the class Klass now directly descends from Module Mod.

The include keyword adds methods of the declared module to the class it's declared in as instance methods.

Or in other words:

The include keyword makes the module a parent of the class it's declared in an ancestor chain.

Therefore, when we call an instance method to the class Klass, they find an instance method in their parent Module Mod and gives output.

The module methods, however, are only available to module Mod itself.

Now what if we include 2 different modules with the same ‘method_name’ in a single class?

The 2nd module imported directly descends from 1st module.

So when we call method_1 it will first look if it’s available in class Klass, then it will go 1 step up the hierarchy to module Mod_2 to find the method, it finds it there and gives output.

If we remove the method_1 from Mod_1 this wouldn’t give any different output because it never goes there to find the method, however, if we remove method_1 from Mod_2 then it will find method_1 in Mod_1 and give its output.

prepend

The prepend keyword makes the module a child of the class it's declared in an ancestor chain.

Or,

The class where prepend keyword is declared is a parent of the declared module.

Therefore, when I give a method that is both available in module and class, it will first look in the module as its first member in the ancestor chain.

extend

The extend keyword adds instance methods of the declared module to the class it's declared in as class methods.

Or,

The extend keyword makes instance methods of the module an instance method to the singleton class of the class where extend is declared in.

In other words,

The extend keyword brings instance methods of a Module, Mod, as a singleton method of class Klass.

The ancestor chain of the class Klass isn’t affected, but of the singleton class...

The module Mod is a parent of a singleton class Class:Klass. So when we call Klass.method_1, we are accessing method_1 from singleton class, Class:Klass.

Access module from different file

I have explained this in detail in this article.

Any suggestions or edits in this article are always welcome! Thank you!

My previous article: A Class in Ruby
My next article: An Enumerable Module in Ruby

--

--