Книга: Metaprogramming Ruby 2
Назад: Singleton Classes
Дальше: Method Wrappers

Quiz: Module Trouble

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?

Quiz Solution

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?”

Class Methods and include()

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.

Object#extend

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.”

Назад: Singleton Classes
Дальше: Method Wrappers