Where you learn that singleton classes and modules mix well with each other.
Bill decides it’s time for a story: “Every single day, somewhere in the world, a Ruby programmer tries to define a class method by including a module. I tried it myself, but it didn’t work.”
| module MyModule |
| def self.my_method; 'hello'; end |
| end |
| |
| class MyClass |
| include MyModule |
| end |
| |
| MyClass.my_method # NoMethodError! |
“You see,” Bill continues, “when a class includes a module, it gets the module’s instance methods—not the class methods. Class methods stay out of reach, in the module’s singleton class.”
“So, how did you find a solution?” you ask. “Oh, I didn’t,” Bill replies, blushing. “I just asked for the solution on a mailing list, like everybody else does. But maybe you can find a solution.” Think about the object model and singleton classes. How would you modify the code that you just looked at so that it works as expected?
The solution to this quiz is simple and subtle at the same time. First, define my_method as a regular instance method of MyModule. Then include the module in the singleton class of MyClass.
| module MyModule |
* | def my_method; 'hello'; end |
| end |
| |
| class MyClass |
* | class << self |
* | include MyModule |
* | end |
| end |
| |
| MyClass.my_method # => "hello" |
my_method is an instance method of the singleton class of MyClass. As such, my_method is also a class method of MyClass. This technique is called a Spell: .
“That’s brilliant,” Bill says. “What about trying the same trick on a regular object instead of a class?”
Reviewing Class Extensions, you can define class methods by mixing them into the class’s singleton class. Class methods are just a special case of Singleton Methods, so you can generalize this trick to any object. In the general case, this is called an Spell: . In the following example, obj is extended with the instance methods of MyModule:
| module MyModule |
| def my_method; 'hello'; end |
| end |
| |
| obj = Object.new |
| |
| class << obj |
| include MyModule |
| end |
| |
| obj.my_method # => "hello" |
| obj.singleton_methods # => [:my_method] |
In case you think that opening the singleton class is a clumsy way to extend a class or an object, let’s also look at an alternative technique.
Class Extensions () and Object Extensions () are common enough that Ruby provides a method just for them, named Object#extend:
| module MyModule |
| def my_method; 'hello'; end |
| end |
| |
| obj = Object.new |
| obj.extend MyModule |
| obj.my_method # => "hello" |
| |
| class MyClass |
| extend MyModule |
| end |
| |
| MyClass.my_method # => "hello" |
Object#extend is simply a shortcut that includes a module in the receiver’s singleton class. You can always do that yourself, if you so choose.
“Enough talking about singleton classes today,” Bill announces. “I don’t want to get a meta-headache. For now, let’s go back to refactoring Bookworm.”