Modules
Learn about modules in Ruby and how they relate with classes.
We'll cover the following
It now makes sense to introduce another language feature in Ruby: modules.
Modules are somewhat similar to classes in Ruby. They hold methods just like classes do. However, modules can’t be instantiated. It’s not possible to create objects from a module. Modules, unlike classes, don’t have a new
method.
What are modules useful for?
We can share methods between classes with modules. Modules can be included into classes, and this makes their methods available in the class, just as if we’d copied and pasted these methods over to the class definition.
This is useful if we have methods that we want to reuse in certain classes but also want to keep in a central place so that we don’t have to repeat them everywhere.
A toy example
Let’s have a look at this pretty contrived code:
module Creamdef cream?trueendendclass Cookieinclude Creamendcookie = Cookie.newp cookie.cream?
This minimal code example is rather strange. That said, it’s a perfect quick example.
This code outputs true
. Why is that?
The cream? method is defined on the
Creammodule, and all it does is always return the value
true. This module is included in the
Cookieclass. So, if we now instantiate a cookie, we can call the method
cream?on it, and it returns the value
true`.
Remember: We use the
module
keyword to create a module, and we use theinclude
keyword to include it in another class.
Let’s move on and use this for our Person
class, which will hopefully then make more sense.
Another example
Let’s assume that our application has other classes that need to encrypt things. We want to keep the exact way of how we encrypt things, the implementation, in one single place.
Why would we want to do that? One reason could be that when we want to switch to a different way of encrypting things (maybe use a stronger encryption algorithm), we’d only need to change it in this one place, in our module, and be done with it.
Another reason could be that we want the encryption algorithm to be somehow configurable, for example, in a configuration file that our application reads. This would then require additional logic that we wouldn’t want to duplicate across all the places where we need to encrypt something. We’d want all this to be kept in a central place.
Another reason could be that we want to move some clutter out of sight and hide it away in another file. So, we can focus on what our Person
class does instead of having to look at the details of how exactly we encrypt things.
Here’s how we can create a meaningful module for our application and use it in the Person
class:
require 'digest'module Encryptiondef encrypt(string)Digest::SHA2.hexdigest(string)endendclass Personinclude Encryptiondef initialize(name)@name = nameenddef name@nameenddef password=(password)@password = passwordenddef encrypted_passwordencrypt(@password)endendperson = Person.new("Ada")person.password = "super secret"puts person.encrypted_password
This code prints out the same encrypted version of the password. That’s what we want!
We’ve moved the noisy details of the encryption algorithm to a module and then included the module in the Person
class. This, at the very least, makes the encrypted_password
method much easier to read.
We refer to the process of moving some logic (code) from one method to another as extracting a method. In our case, we’ve extracted the encrypt
method from the encrypt_password
method. Methods usually become shorter and more readable when we do this.