Is there some overall, grand unifying pattern I'm missing then? Because when
I look at Ruby method dispatch it seems a bit like an ad hoc mess
If I understand correctly (and I would welcome corrections), the pattern
for method lookup is:
Looking up "instance methods" :
* for an object that is not a class (object.class != Class)
* if there is an object.singleton_class
* search in the (instance) methods of this object.singleton_class
else
* search in the (instance) methods of the object.class
* then recursively follow the super pointer upwards from there
* for the object.singleton_class, the super pointer will point
to object.class
* for "normal" classes, the super pointer will point to the
class from which this "normal" class inherits or optionally
to an ICLASS that is a proxy for an included module
* until we reach BasicObject
Looking up "class methods":
* for an object that is a class (object.class == Class)
* there is always an object.singleton_class
* search in the (instance) methods of this object.singleton_class
* then recursively follow the super pointer upwards from there
* for the object.singleton_class and higher-up singleton classes,
the super pointer will point to object.superclass.singletonclass,
thus forming a parallel super-chain of singleton classes
(until #<Class:BasicObject> is reached, then we jump to Class)
* for "normal" classes, the super pointer will point to the
class from which this "normal" class inherits or optionally
to an ICLASS that is a proxy for an included module
* until we reach BasicObject
* Note 1: If I understand correctly, for an object that is not
a class, the singleton_class is not prepared a priori, but
is generated at the time when it is called.
* Note 2: the ruby 'superclass' method does not always show the
immediate thing pointed to by "super". In Ruby 1.9.2, I believe
it shows the next thing in the super-chain that is a Class, but
in Ruby 1.8.7 superclass on a singleton class shows Class,
while there are still intermediate singleton classes, e.g.
#<Class:Object>. ICLASS's for modules can be in the
super-chain as well, but are also not shown by the superclass
function (but these can be discovered with
self.included_modules - self.superclass.included_modules).
References:
* "Advanced Rails" (Brad Ediger), O'Reilly
*
http://www.klankboomklang.com/2007/10/05/the-metaclass/
(has a nice graphic of the parallel super-chain formed by singleton classes)
Corrections welcome,
Peter
Example for instance d of class D, without a singleton class:
=============================================================
$ irb
ruby-1.9.2-head > class D < Object; end
=> nil
ruby-1.9.2-head > d= D.new
=> #<D:0x84b05e8>
ruby-1.9.2-head > d.class
=> D ## this shows that d is _not_ a class
ruby-1.9.2-head > d.singleton_class
=> #<Class:#<D:0x84b05e8>> ## is created ad-hoc
ruby-1.9.2-head > d.singleton_class.instance_methods(false)
=> [] ## but has no methods, so search in D
ruby-1.9.2-head > D.superclass
=> Object
ruby-1.9.2-head > D.superclass.included_modules -
D.superclass.superclass.included_modules
=> [Kernel] # Object has the Kernel module included, so that will be
searched also
ruby-1.9.2-head > D.superclass.superclass
=> BasicObject
ruby-1.9.2-head > D.superclass.superclass.superclass
=> nil
ruby-1.9.2-head > D.ancestors
=> [D, Object, Kernel, BasicObject]
Example for instance e of class E, with a singleton class:
==========================================================
ruby-1.9.2-head > class E < Object; end
=> nil
ruby-1.9.2-head > e = E.new
=> #<E:0x8960d68>
ruby-1.9.2-head > e2 = E.new
=> #<E:0x896bbc8>
ruby-1.9.2-head > class << e # opening the singleton class of _object_ e
ruby-1.9.2-head ?> def my_singleton_method
ruby-1.9.2-head ?> "my_singleton_method, defined in e.singleton_class"
ruby-1.9.2-head ?> end
ruby-1.9.2-head ?> end
=> nil
ruby-1.9.2-head > e.my_singleton_method
=> "my_singleton_method, defined in e.singleton_class"
ruby-1.9.2-head > e2.my_singleton_method ## no luck, the singleton
method is only for e
NoMethodError: undefined method `my_singleton_method' for #<E:0x896bbc8>
from (irb):25
from /home/peterv/.rvm/rubies/ruby-1.9.2-head/bin/irb:16:in `<main>'
ruby-1.9.2-head > e.class
=> E
ruby-1.9.2-head > e.singleton_class
=> #<Class:#<E:0x8960d68>>
ruby-1.9.2-head > e.singleton_class.instance_methods(false)
=> [:my_singleton_method]
ruby-1.9.2-head > E.superclass
=> Object
ruby-1.9.2-head > E.superclass.included_modules -
D.superclass.superclass.included_modules
=> [Kernel]
ruby-1.9.2-head > E.superclass.superclass
=> BasicObject
ruby-1.9.2-head > E.superclass.superclass.superclass
=> nil
ruby-1.9.2-head > E.ancestors
=> [E, Object, Kernel, BasicObject]
Example with classes (Object, A, B, C) with singleton classes:
==============================================================
First this class definition code:
class Object
class << Object
def my_class_method_0
"my_class_method_0, defined in Object.singleton_class"
end
def my_class_method_1
"my_class_method_1, defined in Object.singleton_class"
end
def my_class_method_2
"my_class_method_2, defined in Object.singleton_class"
end
end
end
class A < Object
class << A
def my_class_method_0
"my_class_method_0, defined in A.singleton_class"
end
def my_class_method_1
"my_class_method_1, defined in A.singleton_class"
end
# NOT defined my_class_method_2
end
def my_instance_method_in_A
"instance method, defined in A"
end
end
class B < A
class << B
def my_class_method_0
"my_class_method_0, defined in B.singleton_class"
end
# NOT defined my_class_method_1
# NOT defined my_class_method_2
end
end
class C < B
# singleton class was not opened
end
Results in:
ruby-1.9.2-head > B.class
=> Class ## this shows that B is a class
ruby-1.9.2-head > B.my_class_method_0 ## in B.singleton_class
=> "my_class_method_0, defined in B.singleton_class"
ruby-1.9.2-head > B.my_class_method_1
=> "my_class_method_1, defined in A.singleton_class"
## in B.singleton_class.superclass == A.singleton_class
ruby-1.9.2-head > B.my_class_method_2
=> "my_class_method_2, defined in Object.singleton_class"
## in B.singleton_class.superclass.superclass == Object.singleton_class
ruby-1.9.2-head > C.my_class_method_0 ## in C.singleton_class.superclass
=> "my_class_method_0, defined in B.singleton_class"
ruby-1.9.2-head > A.my_class_method_0 ## in A.singleton_class
=> "my_class_method_0, defined in A.singleton_class"
ruby-1.9.2-head > B.singleton_class.instance_methods(false)
=> [:my_class_method_0]
ruby-1.9.2-head > A.singleton_class.instance_methods(false)
=> [:my_class_method_0, :my_class_method_1]
ruby-1.9.2-head > super_chain = [] ; sc = C.singleton_class
=> #<Class:C>
ruby-1.9.2-head > while sc
ruby-1.9.2-head ?> super_chain << sc
ruby-1.9.2-head ?> sc = sc.superclass
ruby-1.9.2-head ?> end
=> nil
ruby-1.9.2-head > super_chain
=> [#<Class:C>, #<Class:B>, #<Class:A>, #<Class:Object>,
#<Class:BasicObject>, Class, Module, Object, BasicObject]
Note: this last result does not show the modules (e.g. Kernel) that are also
included in the internal super-chain but are not shown by the ruby
superclass method