(c) John Mair (banisterfiend) 2010 MIT license
Removes the shackles from Module#include
Module#include_complete to bring in singleton classes from
modules. No more ugly ClassMethods and included() hook hacks.
include_complete the class methods of the
module will be mixed in along with the instance methods:
module M # class method def self.hello puts "hello!" end # instance method def bye puts "bye!" end end class A include_complete M end # invoke class method A.hello #=> hello! # invoke instance method A.new.bye #=> bye!
When a class inherits from another class it inherits both the instance methods and class methods from its superclass.
Module inclusion does not work this way, only the module's instance methods
are mixed into the receiver's ancestor chain. This shortcoming
In my opinion this behaviour of modules violates the principle of least surprise, though I'm aware not everyone agrees with this.
include_complete was written to make module inclusion work more like
For completeness the
extend_complete method has also been
implemented. Like traditional
extend it mixes the module's instance
methods into the singleton class of the receiver. But where do the
singleton methods on the module end up? On the singleton class of the
singleton class of the receiver ;)
module M def self.hello :hello end end class C extend_complete M class << self hello #=> :hello end end
As a result of this, it is unlikely
extend_complete will be of much
use to anyone :)
include_complete is a C extension that implements a highly modified
rb_include_module() function. Traditional module inclusion uses the
class pointer of the Included Module to point to the original module;
include_complete instead uses the class pointer to point to a
wrapped version of the singleton class of the module and stores the original module in a
__module__ instance variable. This wrapped singleton class is
then injected into the ancestor chain of the receiver's singleton
include_complete uses a recursive function to generate the
Included Modules, and the base case of this recursion is reached when the singleton class of Module is
encountered. In the case where the module has a meta-meta class the recursive
function will not terminate and the program will hang.
It is highly unlikely and, as far as I know, next to useless for a module to possess any higher order metaclasses so this limitation is unlikely to be a problem in practice.
It may be argued that the current behaviour of modules is desirable
and that you do not in fact want module singleton classes to be mixed in
during inclusion. There are reasonably good arguments to
support this case which range from the obvious: That you do not want
your class to access hook methods on the module such as
extended. To the more subtle arguments: That a singleton
class is not a module and so cannot be mixed into an ancestor chain
Nonetheless, in my opinion, the advantages of
behaviour outweigh these considerations - It brings a nice symmetry
and consistency in behaviour to module inclusion and class
inheritance, it obviates the need for the included hook hack, and it
(in my opinion) correlates more closely with expectation and satisfies
the principle of least surprise.
Have a play, and decide for yourself :)
include_complete is one of a series of experimental libraries that mess with the internals of Ruby to bring new and interesting functionality to the language, see also:
- Remix - Makes ancestor chains fully read/write.
- Object2module - Convert Classes and Objects to Modules so they can be extended/included
- Prepend - Prepends modules in front of a class; so method lookup starts with the module
- GenEval - A strange new breed of instance_eval
Problems or questions contact me at github
This project was previously called