A deeper look into Ruby Modules
By: Nathen Bowes
Tags: Programming, Ruby, Launch School

What is a Ruby Module?

A module is a way for a Ruby programmer to encapsulate logically similar classes, methods, constants, and even other modules. A module is included in other Ruby classes by using the include method, which can also be referred to as a mixin . Here is an example of a module:

module Towable

def tow

"I can tow something!"

end

end class Truck

include Towable

end truck = Truck.new

truck.tow # => "I can tow something!"

We define a module by using the module keyword and using a capital letter as the initial character. If a module had multiple words, then camel case syntax is preferred (ex: module TowTruck ). It is also a common naming convention to use an “able” suffix on the verb that the module is modeling.

Inside a module, we can define methods (class and instance methods) similar to how we would define methods inside of a class. In the example above we have defined an instance method tow within the Towable module. There is also a Truck class that has been defined, and we are using the include method to include the Towable module and all of the functionality that has been defined within it. Finally, we are initializing a local variable truck and assigning it to a new Truck object (or a new instance of the Truck class). Since we have access to the tow instance method, we invoke it on truck , and the program returns "I can tow something!" .

Why not just use a Class?

Now I know what you’re thinking — why wouldn't I just create a class and inherit the instance methods that way. I’ll provide another example to show you why.

class HockeyPlayer

def slap_shot

"Top shelf! The goalie never had a chance."

end

end class SoccerPlayer

def penalty_kick

"The ball was faster than the goalie!"

end

end class Person

end

Here we have an example with three different classes — HockeyPlayer , SoccerPlayer , and Person . As a person who loves sports (not a Person object, a real person) I don’t want to have to choose between playing hockey or playing soccer. In Ruby, we are only able to inherit from one superclass (either the HockeyPlayer class or the SoccerPlayer class) which isn’t great. Let’s fix that by using modules!

module Hockeyable

def slap_shot

"Top shelf! The goalie never had a chance."

end

end module Soccerable

def penalty_kick

"The ball was faster than the goalie!"

end

end class Person

include Hockeyable

include Soccerable

end nathen = Person.new

nathen.slap_shot # => "Top shelf! The goalie never had a chance."

nathen.penalty_kick # => "The ball was faster than the goalie!"

You can see that we are able to include multiple modules in the Person class, which means the Person object nathen has access to both slap_shot and penelty_kick instance methods. This is how Ruby addresses the inability to inherit from multiple classes — by way of multiple module mixins .

What is the role of a Module?

Now that you understand what a module is, and how you can use it, you are ready to understand what the role of a module is. When we define classes in a Ruby file, we are defining them in the global namespace . This is fine for simple programs, but as soon as you start to add more code and complexity, there is more room for clashing names of classes and methods. Take this example for instance:

# backend.rb

class User

def write_code

"You write awesome Ruby code!"

end

end # more code in between #frontend.rb

class User

def write_code

"Finally, the box is centered."

end

end nathen = User.new

nathen.write_code # => "Finally, the box is centered."

Well, that is confusing. What if we wanted to have a backend developer and a frontend developer? They both write code, but we don’t want one write_code instance method to overwrite the other. Once again, let’s bring a module to the rescue.

module BackEnd

class User

def write_code

"You write awesome Ruby code!"

end

end

end module FrontEnd

class User

def write_code

"Finally, the box is centered."

end

end

end nathen = BackEnd::User.new

nathen.write_code # => "You write awesome Ruby code!"

Here we have defined two modules that describe two different namespaces — BackEnd and FrontEnd namespaces if you will. Now we are not only able to be much more clear about which User object we are creating, but we are also able to identify much faster what we expect the program to output. You can see we are using the :: operator to instantiate the User class from within the BackEnd module, which is conveniently named the namespace resolution operator .

From this, we can tell that nathen is assigned to the User object inside the BackEnd module, which is why invoking the write_code instance method on nathen returns "You write awesome Ruby code!" .

Note: The :: operator is not limited to only modules. It can be used on classes, modules, and constants.

This is a very simple example, but when we start to work with larger programs that include multiple files and many classes, you can see how class names and even instance methods can easily collide and be overwritten when you put all of them together. This is where the power of modules and creating those ‘safe places’ (namespaces) for you to define classes and methods come into play, making for a much nicer and less conflicted coding experience.

Other considerations

Now I just told you all of these awesome things about modules and how powerful they are, but when designing Object-Oriented programs there are a few things to consider before just putting modules everywhere.

Modules cannot be instantiated. If you are planning on creating instances that have access to all of the functionality within a module, just know that the module itself cannot be instantiated. You are able to instantiate a class within a module (like the developer example above), but the module itself cannot have instances of itself.

Modules modify the method lookup path. If we use the Person class from above I can demonstrate what I mean.

# modules omitted for brevity.. :( class Person

include Hockeyable

include Soccerable

end

In order to see the method lookup path, we need to use the .ancestors class method like this: Person.ancestors , which would return the following Array of objects:

[Person, Soccerable, Hockeyable, Object, Kernel, BasicObject]

We can see that our Soccerable and Hockeyable modules are included in the method lookup path (sometimes referred to as a hierarchy). One thing that is interesting is modules are ordered in the hierarchy from bottom to top, as opposed to top to bottom like they are included in the Person class. This is important when calling methods and knowing which classes and modules would be ‘looked in’ first. If Hockeyable and Soccerable each had an instance method with the same name, the one in Soccerable would be called first since it appears first in the method lookup path.

Modules can be included in multiple classes. I showed an example above on how multiple modules can be included in a single class, but it’s good to know that a single module can be added to multiple classes. This can be useful when you want functionality in one class, but you don’t want to create another class hierarchy, or if different classes do not inherit from the same superclass.

module Swimmable

def swim

"I can swim!"

end

end class Pet

end class Fish < Pet

include Swimmable

end class Dog < Pet

include Swimmable

end class Cat < Pet

end