Thursday, October 22, 2009

Recipe 9.3. Mixing in Class Methods










Recipe 9.3. Mixing in Class Methods




Credit: Phil Tomson



Problem


You want to mix class methods into a class, instead of mixing in instance methods.




Solution


The simplest way to accomplish this is to call extend on the class object, as seen in the Discussion of Recipe 9.2. Just as you can use extend to add singleton methods to an object, you can use it to add class methods to a class. But that's not always the best option. Your users may not know that your module provides or even requires some class methods, so they might not extend their class when they should. How can you make an include statement mix in class methods as well?


To begin, within your module, define a submodule called ClassMethods,[3]which contains the methods you want to mix into the class:

[3] The name ClassMethods has no special meaning within Ruby: technically, you can call your submodule whatever you want. But the Ruby community has standardized on ClassMethods as the name of this submodule, and it's used in many Ruby libraries, so you should use it too.



module MyLib
module ClassMethods
def class_method
puts "This method was first defined in MyLib::ClassMethods"
end
end
end



To make this code work, we must also define the included callback method within the MyLib module. This method is called every time a module is included in the class, and it's passed the class object in which our module is being included. Within the
callback method, we extend that class object with our
ClassMethods
module, making all of its instance methods into class methods. Continuing the example:



module MyLib
def self.included(receiver)
puts "MyLib is being included in #{receiver}!"
receiver.extend(ClassMethods)
end
end



Now we can include our MyLib module in a class, and get the contents of ClassMethods mixed in as genuine class methods:



class MyClass
include MyLib
end
# MyLib is being included in MyClass!

MyClass.class_method
# This method was first defined in MyLib::ClassMethods





Discussion



Module#included
is a callback method that is automatically called during the inclusion of a module into a class. The default included
implementation is an empty method. In the example, MyLib overrides it to extend the class that's including the MyLib module with the contents of the MyLib::ClassMethods submodule.


The Object#extend method takes a Module object as a parameter. It mixes all the methods defined in the module into the receiving object. Since classes are themselves objects, and the singleton methods of a Class object are just its class methods, calling extend on a class object fills it up with new class methods.




See Also


  • Recipe 7.11, "Coupling Systems Loosely with Callbacks," covers callbacks in general and shows how to write your own

  • Recipe 10.6, "Listening for Changes to a Class," covers Ruby's other class and module
    callback methods













No comments: