By the late 1960s to early 1970s, computer programs had grown complex enough that the concept of the “subroutine” was insufficient for keeping them comprehensible. A new, larger-scale organization concept was needed: and thus, the concept of “modules” was born. Modules were a unit of composability and (sometimes) reusability, where multiple subroutines could be packaged up and given well-documented public interface.
In this course, we’ll broaden and deepen our understanding of modularity in Ruby from multiple perspectives. We’ll dive deep into Ruby code-loading mechanisms. We’ll learn how Ruby Modules serve as namespaces, as units of [de]composition, as inheritance mechanisms, and as the building blocks of both Ruby’s class system and its rich facilities for creating internal Domain-Specific Languages (DSLs).
What is a module?
The exact meaning of “module” differs from one programming language to another, as does the name applied to the concept: for example, Perl and Java both call them “packages”. In some languages, modules are indelibly linked to the file organization of the program: files are declared to be part of one module or another. Other languages, such as Ruby, don’t tie the “live” organization of running code to where the code is written in the filesystem.
Perhaps the most consistent feature of modules across languages is that they provide a namespace. Working within a module namespace, we don’t have to worry about accidentally clashing with, replacing, or referencing functions or variables defined in other modules.
A unit of encapsulation
And that’s the core of Ruby’s concept of modules: they are, first and foremost, namespaces. We can define methods or global constants inside a module, and not worry about the names we use clashing with any others in the program. We can also declare some of those methods private as a warning to other programmers that the internal implementation details of the module may change, and they should only rely on the documented public interface.
A unit of composition
But Ruby’s modules also go beyond naming bins. Every module also has an ancestor list, of other modules that have been included into it. These modules become extensions of the module’s namespace. Thus, Ruby modules are composable.
A namespace for namespaces
Ruby modules can also contain other modules. This enables a fractal kind of organization: when the code inside a module becomes too extensive to be easily held in a developer’s head, they can break it down still further into submodules.
A building block for DSLs
The namespacing function of Ruby modules, coupled with Ruby’s syntactical flexibility, lets them function as tiny vocabularies or “worlds”. So modules are an essential element of metaprogramming in Ruby, and of implementing internal Domain-Specific Languages (DSLs).
The basis for classes
And finally, modules are the foundation of Ruby’s class system, and thus they provide a core mechanic for Ruby’s implementation of object-oriented programming. In fact Ruby classes are simply modules, with the tiny addition that they can also act as factories for objects that link back to the class. If a module were the text of a book, a class would be that text plus a printing press: ready to churn out many discrete copies of the book, each of which can be marked up and annotated by different readers.
To sum up: modules are the fundamental building block for organizing Ruby code. Understanding them and using them well is the key to effectively decomposing Ruby programs into manageable pieces. As well as to understanding how Ruby’s OO works, and how to effectively metaprogram in Ruby.
Modules and code-loading
As we mentioned earlier, in Ruby, modules aren’t explicitly tied to file organization. But we can’t talk modules without also touching on the way those modules are organized in the filesystem, and how that code is loaded into a running program. Any discussion of Ruby code modularity is incomplete without also getting into code loading.
About this course
Graceful.Dev / RubyTapas topics have touched on Modules and code organization frequently over the years. This garden path collects some essential episodes on Ruby’s module system together in one place, and provides a suggested path through them.
Prerequisites: Modular Ruby is part of the Ruby Fluency Garden Tour, which you may want to follow in order if you are still learning Ruby.
This course is a Graceful.Dev Garden Path, meaning it is a suggested pathway through a curated selection of self-contained topics. Its status is mature: new topics may still be added, but it is a fully fleshed-out course of study.