Back-end Engineering Articles

I write and talk about backend stuff like Ruby, Ruby On Rails, Databases, Testing, Architecture / Infrastructure / System Design, Cloud, DevOps, Backgroud Jobs, some JS stuff and more...

Github:
/danielmoralesp
Twitter:
@danielmpbp

2025-02-18

Modules and Mixins in Ruby OOP

To start this post, let’s imagine we started having a lot of different classes in our program. Is quite possible that we end up with equal class names. For instance, you decide to name a class as “Figure”. Months later, you’ll start to forget the names given to all your classes, and you decide to name another different class, with different behaviors as “Figure”. This is called: name collision. That’s annoying because you have to consider all the current class names to assign a new one

Modules
Here is where modules come in help. Modules help us to avoid collision names in Ruby and at the same time it helps us to organize our code better. So a module is a class, method and variables container. Modules are defined with the Ruby keyword “module”. Let’s create our first module in a file called module.rb

module MyModule
 class ThingOne
   attr_accessor :hello
 end

 class ThingTwo
 end
end




Is easy for us to understand what we have inside the module. We have 2 methods and 2 classes, and MyModule acts as a wrapper for all of these inner components. The question now is how can we access these methods and classes outside the module?

To access classes and methods defined inside the module, we have to write the module’s name swallowed by “::” (double colon), followed by the method or classes’ name. Let do an example:
module MyModule
 class ThingOne
   attr_accessor :hello
 end

 class ThingTwo
 end
end

i = MyModule::ThingOne.new
i.hello = "Hello from ThingOne class"
puts i.hello


Let’s execute in console

$ ruby modules.rb
Hello from ThingOne class



Inside the module we have just classes, but what happens if we add methods? Let’s do it and try to execute the code as follows:

module MyModule
 def self.method_one
   "hello from method 1"
 end

 def method_two
   "hello from method 2"
 end

 class ThingOne
   attr_accessor :hello
 end

 class ThingTwo
 end
end

m = MyModule::ThingOne.new
puts m.method_one



Can you see the difference between method_one and method_two? The difference is that we added “self.” at the beginning of the method’s name. 

If we try to execute this code, we’ll see the following error:


$ ruby modules.rb
modules.rb:19:in `<main>': undefined method `method_one' for #<MyModule::ThingOne:0x000000000143ab10> (NoMethodError)
Did you mean?  method

The self.method_one is a module method, which means that we can use it without having to include (or extend) the module in any other object (we’ll see this concept below). This is very common when we are creating service objects, for example. We can call our module method like this:


module MyModule
 def self.method_one
   "hello from method 1"
 end

 def method_two
   "hello from method 2"
 end

 class ThingOne
   attr_accessor :hello
 end

 class ThingTwo
 end
end

puts MyModule.method_one


And the result is

$ ruby modules.rb
hello from method 1

This works because of “self”. Later we’ll explain the “self” method in detail. 

If we continue, we keep the same problem with method_two. This method doesnt have the “self” method, so it will throw an error if we try to instantiate it like this:

module MyModule
 def self.method_one
   "hello from method 1"
 end

 def method_two
   "hello from method 2"
 end

 class ThingOne
   attr_accessor :hello
 end

 class ThingTwo
 end
end

m = MyModule::ThingOne.new
puts m.method_two



This is because methods cannot be called directly from the module, we’ve to mix the module inside a class. 

Mixins
Modules can be included inside a class using the keyword “include”. This will include all “MyModule” methods inside the new class. Let’s see how

module MyModule
 def self.method_one
   "hello from method 1"
 end

 def method_two
   "hello from method 2"
 end

 class ThingOne
   attr_accessor :hello
 end

 class ThingTwo
 end
end

class Person
 include MyModule
end

p1 = Person.new
puts p1.method_two


Let’s execute it

$ ruby modules.rb
hello from method 2

Now, the method works! Now the question is, how can we access the classes like ThinOne and ThingTwo that are inside of the module? We'll do that inside the class Person. Let me show you how

module MyModule
 def self.method_one
   "hello from method 1"
 end

 def method_two
   "hello from method 2"
 end

 class ThingOne
   attr_accessor :hello
 end

 class ThingTwo
 end
end

class Person
 include MyModule

 def thing_one
   ThingOne.new #there is no needed MyModule:: prefix
 end
end

p1 = Person.new
t = p1.thing_one
t.hello = "Hello World!"
puts t.hello


Let’s execute the code


$ ruby modules.rb
Hello World!

And now we accessed the ThingOne class inside the module. 

At the same time it is quite possible to mix more than one module inside a class. Let’s suppose that we have 2 modules: Module1 and Module2, we can do the following:

class Person
 include Module1
 include Module2
end


So, every method inside Module1 and Module2 will be able for using inside the Person’s class.

Other use case scenario is when we nest modules, for instance:

module System
 module Currency
   class Dollar
   end
 end
end

System::Currency::Dollar.new



In this example we’re nesting the module Currency inside System. Inside the Currency module we’ve the Dollar class. If we want to create an object from Dollar we use the “::” notation per each module. 


Naming collisions
At the beginning of the post, we said that modules help us to avoid the naming collisions, but what does that mean? We already know how the modules and mixins works in a high level, now let’s see the cases when we could have name collisions.

Let’s imagine that we have the class named “Account” that refers to a bank account. But suddenly the customer ask for a new feature related to back-office administration system and we know that we need other class named Account for the login process. We can solved with the following code

class Account
 # code form bank account
end

module Admin
 class Account
   # code for login process
 end
end

bank = Account.new
login = Admin::Account.new



On this way we avoid the naming collision, because to access each of them are quite different


When to include a module or inherit from a class?
To finalize this blog post, let's try to answer this good question. You should use inheritance only when a class is a specialization of another class. Example, a Circle is a geometric Figure, but a more specialized one. It’s better to use inheritance for this case. A bus is a vehicle, so it’s a special vehicle, and we should use inheritance

On the other hand, if we need to reuse a group of methods in different classes you should use modules, instead of inheritance. Generally speaking, you should try to group modules by features or roles that you can apply to different classes. 

So, we’ve learned a lot on thi blog post

Thanks for reading
Daniel Morales