Tuesday, January 19, 2010

Section 8.5. Hooks








8.5. Hooks


Module, Class, and
Objectimplement several callback methods, or hooks. These methods are
not defined by default, but if you define them for a module, class, or
object, then they will be invoked when certain events occur. This gives
you an opportunity to extend Ruby's behavior when classes are subclassed,
when modules are included, or when methods are defined. Hook methods
(except for some deprecated ones not described here) have names that end in
"ed."


When a new class is defined, Ruby invokes the class method inherited on the superclass of the new
class, passing the new class object as the argument. This allows classes
to add behavior to or enforce constraints on their descendants. Recall
that class methods are inherited, so that the an
inherited method will be invoked if it is defined by
any of the ancestors of the new class. Define
Object.inherited to receive notification of all new
classes that are defined:


def Object.inherited(c)
puts "class #{c} < #{self}"
end



When a module is included into a class or into another module, the
included class method of the included module is invoked
with the class or module object into which it was included as an argument.
This gives the included module an opportunity to augment or alter the
class in whatever way it wants—it effectively allows a module to define
its own meaning for include. In addition to adding
methods to the class into which it is included, a module with an
included method might also alter the existing methods
of that class, for example:


module Final             # A class that includes Final can't be subclassed
def self.included(c) # When included in class c
c.instance_eval do # Define a class method of c
def inherited(sub) # To detect subclasses
raise Exception, # And abort with an exception
"Attempt to create subclass #{sub} of Final class #{self}"
end
end
end
end



Similarly, if a module defines a class method named
extended, that method will be invoked any time the
module is used to extend an object (with
Object.extend). The argument to the
extended method will be the object that was extended,
of course, and the extended method can take whatever
actions it wants on that object.


In addition to hooks for tracking classes and the modules they
include, there are also hooks for tracking the methods of classes and
modules and the singleton methods of arbitrary objects. Define a class
method named method_added for any class or module and
it will be invoked when an instance method is defined for that class or
module:


def String.method_added(name) 
puts "New instance method #{name} added to String"
end



Note that the method_added class method is
inherited by subclasses of the class on which it is defined. But no class
argument is passed to the hook, so there is no way to tell whether the
named method was added to the class that defines
method_added or whether it was added to a subclass of
that class. A workaround for this problem is to define an
inherited hook on any class that defines a
method_added hook. The inherited
method can then define a method_added method for each
subclass.


When a singleton method is defined for any object, the method
singleton_method_added is invoked
on that object, passing the name of the new method. Remember that for classes, singleton methods
are class methods:


def String.singleton_method_added(name)
puts "New class method #{name} added to String"
end



Interestingly, Ruby invokes this
singleton_method_added hook when the hook method itself
is first defined. Here is another use of the hook. In this case, singleton_method_added is defined as an
instance method of any class that includes a module. It is notified of any
singleton methods added to instances of that class:


# Including this module in a class prevents instances of that class
# from having singleton methods added to them. Any singleton methods added
# are immediately removed again.
module Strict
def singleton_method_added(name)
STDERR.puts "Warning: singleton #{name} added to a Strict object"
eigenclass = class << self; self; end
eigenclass.class_eval { remove_method name }
end
end



In addition to method_added and
singleton_method_added, there are hooks for tracking
when instance methods and singleton methods are removed or undefined. When
an instance method is removed or undefined on a class or module, the class
methods method_removed and
method_undefined are invoked on that module. When a
singleton method is removed or undefined on an object, the methods
singleton_method_removed and
singleton_method_undefined are invoked on that
object.


Finally, note that the method_missing and
const_missing methods documented elsewhere in this chapter also behave like
hook methods.









No comments: