Ruby: Modules, and How They Interact
What is a Module?
A module is a collection of methods that a class in Ruby can have access to. A module is not the same as a class, so multiple classes can inherit from it while still maintaining single inheritance between classes. For instance Hash
and Array
classes both inherit from a class called Class
. Array
and Hash
have similarities that would make a lot of methods useful to both of them. Perhaps they could both inherit these methods from Class
? But Class
also has another child called String
, and yet another called Integer
. Integer
and String
are very different from Array
and Hash
, so it wouldn’t make sense to give Class
many methods that are common to Array
and Hash
, and then override all of them within String
and Integer
. It would make more sense if these methods might be collected somewhere where they can be accessed by both Hash
and Array
, but not be needed for String
or Integer
. This particular collection that is used by Hash
and Array
is called the Enumerable
module. The Enumerable
module contains many useful Hash
and Array
methods such as #each
, #reduce
, #select
and #map
.
Why is a Module Useful?
A module is useful to mix methods into classes where it doesn’t make sense for these methods to be accessed from a parent class. It is useful to share methods accross many classes that might be common. Modules are not the same as classes, and so cannot be instantiated. They act only as a collection of objects that can be used to pass messages and perform tasks on class instances.
Extending and Including
Some modules are already included within classes, as the Enumerable
module’s methods are already included within Array
and Hash
. Where they are not, the include
keyword is used to add methods to instances of the class, and the extend
keyword for adding class methods. You can extend on a class to mix in a module, that is give it more methods to use. For example:
You can see above how include
gives a teenage instance of Person, andy
, access to methods from the Adolescent
module, whereas extend
gives the Gazelle
class access to the methods of the Adolescent
module. Extend treats the module’s methods as though they are class methods, include treats them as though they are instance methods.
Namespacing
Modules use something called namespacing in order to reduce confusion about where a method is coming from. Let’s say I want to compare caterpillars that eat different types of lettuce, and also measure whether their movement might follow a cosine curve. I might have a class called Caterpillar
that includes a module called Diet
and also includes the Math
module that already exists in Ruby. But there is a problem. From the Math
module I would like to use the cos
(cosine) method, and from the Diet
module I would like to use the cos
(lettuce) method (this is a ridiculous example). But how can I tell which is which? This is where namespacing comes in.
We might nest our Caterpillar classes each within the module they are using in each case, so we can specify what methods we would like to use.