4 Modules and program organization
Modules are Ruby’s lightweight way to package related methods and constants so they can be reused without creating instances. The chapter opens by showing how a module can encapsulate a behavior such as “stacklikeness,” then be mixed into one or more classes so their objects gain that behavior. This makes modules useful for organizing code into smaller, sharable pieces, especially when several classes need the same functionality but do not belong in a single inheritance chain.
The chapter then explains how Ruby finds methods when an object receives a message. An object searches through its own class, included modules, superclasses, and prepended modules in a precise order, with the first matching method winning. The text demonstrates how include, prepend, and extend affect that search path, how super jumps to the next method in line, and how method and super_method can be used to inspect what method will run and what method comes next.
When no matching method exists, Ruby falls back to method_missing, which can be overridden to handle dynamic or pattern-based messages. The chapter uses this to build a Person class that answers queries like all_with_friends and all_with_hobbies by interpreting method names at runtime. It closes by discussing design choices: when to use modules versus inheritance, how naming often reflects whether something is a behavior or an entity, and how nesting classes and modules creates namespaces that keep similarly named constants from conflicting.
An instance of class D looks for method x in its method search path.
An instance of class D looks for method x in its method search path across both included and prepended modules.
Summary
- Both modules and classes can bundle methods and constants together, but modules can’t be instantiated.
- Classes and modules have method definitions; objects have the ability to traverse classes and modules in search of methods.
- Upon receiving a message, an object goes on a search along a well-defined lookup path consisting of classes and included modules, looking for a method that matches the message.
- Objects don’t themselves have methods, even though phrasing it that way is sometimes a handy shortcut; rather, they have the ability to search for methods.
- You can use modules to express the design of a program.
- You can handle failure with method_missing in cases where the message doesn’t match a method.
- Classes and modules can nest inside each other, with the benefit of keeping namespaces separate and clear.
FAQ
What is a Ruby module, and how is it different from a class?
A module is a bundle of methods and constants used to organize and share behavior. Unlike classes, modules do not have instances. Instead of being instantiated, modules are mixed into classes or objects to provide functionality.How do you add a module’s behavior to a class?
Useinclude inside the class definition. This makes the module’s instance methods available to instances of that class.Why would you use a module instead of putting everything in a class?
Modules help separate reusable behavior from class-specific behavior. They make code more modular, let multiple classes share the same methods, and avoid forcing unrelated features into one inheritance chain.What does it mean to “mix in” a module?
Mixing in a module means adding the module’s methods to a class or object so they become available through the method lookup path. A mixed-in module is often called a mixin.How does Ruby decide which method to call when a class and a module define the same method?
Ruby searches the method lookup path in order. The first matching method it finds wins. If a class defines the method, that version is used before a method with the same name in an included module. For multiple included modules, Ruby checks them in reverse order of inclusion.What is the difference between include, extend, and prepend?
include adds module methods as instance methods. extend adds module methods to a specific object or class object as singleton/class methods. prepend places the module before the class in the lookup path, so the module’s methods are checked first.What is the purpose of Ruby’s ancestors method?
ancestors shows the lookup path for a class or module. Reading it from left to right tells you the order Ruby will use when searching for methods.How does the super keyword work in method lookup?
super calls the next method with the same name higher up in the method lookup path. With no arguments, it forwards the current arguments automatically. You can also use super() to pass none, or super(a, b) to pass explicit arguments.What happens when Ruby can’t find a method for a message?
Ruby callsmethod_missing as a last resort. If you override method_missing, you can handle undefined messages yourself, or call super to let Ruby continue with the default behavior, which raises a NoMethodError.Why are nested modules useful for program organization?
Nested modules create namespaces, helping prevent name collisions. For example,Tools::Hammer and Piano::Hammer can coexist because each is defined in a different namespace.
The Well-Grounded Rubyist, Fourth Edition ebook for free