Inheritance and meta-classes

Hi all,

OK. I’m trying to work out where a method is defined in Rails.
It’s in a test, and the specific function that’s being called is
‘post’, but my question is not a Rails question, it’s Ruby.

How do I find where a function is defined in the inheritance tree?
Because of metaclass inheritance being cyclical I don’t know where to
stop looking.

I made the following as a test but I still can’t find the function
being called so I’m guessing I’m missing some of the inheritance tree…

module Debugs
def find_respond_to_in_class_heirarchy(obj, method)
if obj.respond_to?(:superclass)
arr = find_respond_to_in_class_heirarchy(obj.superclass, method)
arr.unshift([obj.class.respond_to?(method),
obj.metaclass.respond_to?(method), obj.superclass.respond_to?(method)])
elsif obj.nil?
arr = [[nil.class.respond_to?(method), nil.metaclass.respond_to?
(method), NoMethodError]]
else
arr = find_respond_to_in_class_heirarchy(obj.class, method)
arr.unshift([obj.class.respond_to?(method),
obj.metaclass.respond_to?(method), NoMethodError])
end
arr
end
end

class Object
include Debugs
def metaclass
class << self
self
end
end
end

Wow this whole metaclass, class, object, module relationship is pretty
mindbending…

Cheers,
-Adam

Any suggestions?
(or has the subject been done to death…)

I still don’t get how you can query and see where in the chain a
method got added to an object…

I’ve now found that you can do:
obj.class.ancestors # => Array

But that still doesn’t include metaclasses…

How does Ruby know??

On 12/13/07, Robert K. [email protected] wrote:

How do I find where a function is defined in the inheritance tree?
Because of metaclass inheritance being cyclical I don’t know where
to stop looking.

Where exactly do you see cycles? I am not aware of any.

shadowfax:~/ssanta rick$ irb
irb(main):001:0> def meta
irb(main):002:1> class << self
irb(main):003:2> self
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> meta
=> #<Class:#Object:0x349a4>
irb(main):007:0> Array.meta
=> #Class:Array
irb(main):008:0> Array.meta.superclass
=> #Class:Class
irb(main):009:0> Array.meta.superclass.superclass
=> #Class:Class

=> “X”
irb(main):004:0> c2 = Class.new c1
=> #Class:0x7ff8f4b0
irb(main):005:0> c2.x
=> “X”
irb(main):006:0> c2.ancestors
=> [#Class:0x7ff8f4b0, #Class:0x7ff9f70c, Object, Kernel]
irb(main):007:0> c1.class.instance_methods.grep /x/
=> [“extend”]
irb(main):008:0> class <<c2;self end.instance_methods.grep /x/
=> [“x”, “extend”]

I don’t see any metaclasses here. c1 and c2 are simply anonymous
classes.

A metaclass doesn’t sit between an instance and its class. A singleton
class of an instance would, but that’s not a metaclass (although the
way the two terms get conflated I can see where that’s a common
misconception). A metaclass is a singleton ‘class’ which holds the
behavior of a class.

Here’s a simplified diagram
http://myskitch.com/rubyredrick/skitched-20071213-093049/

It’s simplified since it doesn’t include the inclusion of Kernel by
Object.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

How do I find where a function is defined in the inheritance tree?
Because of metaclass inheritance being cyclical I don’t know where
to stop looking.

Where exactly do you see cycles? I am not aware of any.

2007/12/13, Adam S. [email protected]:

I still don’t get how you can query and see where in the chain a
method got added to an object…

I’ve now found that you can do:
obj.class.ancestors # => Array

But that still doesn’t include metaclasses…

Correct.

How does Ruby know??

The meta class is there but it’s hidden from #ancestors as you found
out. Inheritance wise it sits between an object and its class.

irb(main):001:0> c1 = Class.new
=> #Class:0x7ff9f70c
irb(main):002:0> def c1.x; “X” end
=> nil
irb(main):003:0> c1.x
=> “X”
irb(main):004:0> c2 = Class.new c1
=> #Class:0x7ff8f4b0
irb(main):005:0> c2.x
=> “X”
irb(main):006:0> c2.ancestors
=> [#Class:0x7ff8f4b0, #Class:0x7ff9f70c, Object, Kernel]
irb(main):007:0> c1.class.instance_methods.grep /x/
=> [“extend”]
irb(main):008:0> class <<c2;self end.instance_methods.grep /x/
=> [“x”, “extend”]

Note also that #respond_to? is a weak method to determine whether an
instance will respond to a method, because of #method_missing which I
believe is used a lot by Rails. Also, it is not called on the class
but on the object.

You can use #instance_method(s) to check whether a class implements a
certain instance method but as I said, you might not be lucky.

Kind regards

robert

On 13.12.2007 15:33, Rick DeNatale wrote:

irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> meta
=> #<Class:#Object:0x349a4>
irb(main):007:0> Array.meta
=> #Class:Array
irb(main):008:0> Array.meta.superclass
=> #Class:Class
irb(main):009:0> Array.meta.superclass.superclass
=> #Class:Class

Ah, now I see! Should’ve put on my glasses. :wink:

How does Ruby know??
=> #Class:0x7ff8f4b0
irb(main):005:0> c2.x
=> “X”
irb(main):006:0> c2.ancestors
=> [#Class:0x7ff8f4b0, #Class:0x7ff9f70c, Object, Kernel]
irb(main):007:0> c1.class.instance_methods.grep /x/
=> [“extend”]
irb(main):008:0> class <<c2;self end.instance_methods.grep /x/
=> [“x”, “extend”]

I don’t see any metaclasses here. c1 and c2 are simply anonymous classes.

According to the definition given by you the class that’s used in
statement 8 is a meta class.

A metaclass doesn’t sit between an instance and its class. A singleton
class of an instance would, but that’s not a metaclass (although the

Since a metaclass - according to your definition - is a class’s
singleton class it sits between the instance (the class object, e.g.
Array) and its class (Class) in the same way an ordinary’s object’s
singleton class does - which is also shown by the image you referred to
in your posting.

way the two terms get conflated I can see where that’s a common
misconception). A metaclass is a singleton ‘class’ which holds the
behavior of a class.

You mean “class<<Array;self;end”, do you?

Here’s a simplified diagram
http://myskitch.com/rubyredrick/skitched-20071213-093049/

It’s simplified since it doesn’t include the inclusion of Kernel by Object.

Nice colors. :wink: I reckon Array’ is intended to mean the singleton
class of object Array. I am not sure I get your point where you wanted
to correct me. As far as I can see we are completely in sync.

Generally singleton classes of class instances and of ordinary instances
behave the same. The only difference I can see so far is that, since
classes can inherit from each other, a subclass also shares instance
methods defined for its superclass (see my irb session). Btw, something
similar happens with instances when cloned:

irb(main):001:0> o = Object.new
=> #Object:0x7ff9e474
irb(main):002:0> def o.xxx;1;end
=> nil
irb(main):003:0> o.xxx
=> 1
irb(main):004:0> class <<o;self;end.instance_methods.grep /xx/
=> [“xxx”]
irb(main):005:0> o1 = o.clone
=> #Object:0x7ff80eec
irb(main):006:0> o1.xxx
=> 1
irb(main):007:0> class <<o1;self;end.instance_methods.grep /xx/
=> [“xxx”]
irb(main):008:0> o2 = o.dup
=> #Object:0x7ff73cec
irb(main):009:0> o2.xxx
NoMethodError: undefined method `xxx’ for #Object:0x7ff73cec
from (irb):9
from :0
irb(main):010:0> class <<o2;self;end.instance_methods.grep /xx/
=> []

Kind regards

robert

On 12/16/07, Robert K. [email protected] wrote:

On 13.12.2007 15:33, Rick DeNatale wrote:

Here’s a simplified diagram
http://myskitch.com/rubyredrick/skitched-20071213-093049/

It’s simplified since it doesn’t include the inclusion of Kernel by Object.

Nice colors. :wink: I reckon Array’ is intended to mean the singleton
class of object Array. I am not sure I get your point where you wanted
to correct me. As far as I can see we are completely in sync.

Yes I copied the classname’ notation from the diagrams Dave T.
uses in the pickaxe.

The statement I wanted to correct, maybe I misunderstood it was:

The meta class is there but it’s hidden from #ancestors as you found
out. Inheritance wise it sits between an object and its class.

I read object as an instance of “its class.” The above statement is
wrong in two regards.

First, the metaclass isn’t on the inheritance chain of the instance,
it’s on the inheritance chain of the class, but that’s a horse of a
different color.

Second, in the klass chain it’s on the other side of the class, it goes:

instance - klass-> class -klass-> class’

NOT

instance -klass-> class’ -klass-> class

irb(main):003:0> o.xxx
=> #Object:0x7ff73cec
irb(main):009:0> o2.xxx
NoMethodError: undefined method `xxx’ for #Object:0x7ff73cec
from (irb):9
from :0
irb(main):010:0> class <<o2;self;end.instance_methods.grep /xx/
=> []

Not quite the same, what happens here is that cloning an object with a
singleton class gives the clone a singleton class which looks like the
original object’s singleton class:

k$ irb
irb(main):001:0> def meta
irb(main):002:1> class << self;self;end
irb(main):003:1> end
=> nil
irb(main):004:0> o = Object.new
=> #Object:0x89724
irb(main):005:0> o.meta
=> #<Class:#Object:0x89724>
irb(main):006:0> o1 = o.clone
=> #Object:0x82c44
irb(main):007:0> o1.meta
=> #<Class:#Object:0x82c44>
irb(main):008:0> o.meta == o1.meta
=> false

One last point, you have to be careful in looking at the results from
experiments like this in irb/ruby programs because some of the
reflection methods in ruby deliberately lie about what’s really
happening under the covers.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

On 12/17/07, Robert K. [email protected] wrote:

2007/12/17, Rick DeNatale [email protected]:

The statement I wanted to correct, maybe I misunderstood it was:

The meta class is there but it’s hidden from #ancestors as you found
out. Inheritance wise it sits between an object and its class.

I read object as an instance of “its class.” The above statement is
wrong in two regards.

But note that any class is also an instance.

But that doesn’t seem to be relevant to the 'slightly restated assertion
that.

Inheritance wise the metaclass sits between an object and its class.

Let’s say that the object is an array, we have

array -klass-> Array ->klass-> Array’ (i.e. Arrays metaclass)

which isn’t an inheritance chain anyway, but it illustrates that the
metaclass doesn’t sit between an instance and its class. Now lets’
say the object was a class, say Array

we have two parallel inheritance chains:

Array Array’
| |
super super
V V
Object Object’

Again, since a class is also an instance I choose to use the more
general term. But the statement holds true for classes as well because
with regard to this there is no difference between an ordinary
instance and an instance of Class (or Module for that matter). There
is in fact some difference in behavior that results from this (see my
posting) but the singleton class of a class sits between the instance
(the class) and its class (Class).

Yes, but not all singleton classes are metaclasses, I maintain that
only singleton classes of classes are properly called metaclasses.
The meaning of metaclass is “class of a class”, so calling the
singleton class of a non-class object a metaclass is a misnomer,
albeit, sadly, a common one.

Given this definition, it only holds true if the object in the general
statement is a class which makes the statement incorrect in general.

Generally singleton classes of class instances and of ordinary instances
behave the same. The only difference I can see so far is that, since
classes can inherit from each other, a subclass also shares instance
methods defined for its superclass (see my irb session). Btw, something
similar happens with instances when cloned:

Not quite the same, what happens here is that cloning an object with a
singleton class gives the clone a singleton class which looks like the
original object’s singleton class:

That’s why I wrote “similar” and not “same”…

The point is that there’s no inheritance involved, just copying.

One last point, you have to be careful in looking at the results from
experiments like this in irb/ruby programs because some of the
reflection methods in ruby deliberately lie about what’s really
happening under the covers.

Can you elaborate which methods do actually lie?

Off the top of my head, ancestors for one.


Rick DeNatale

My blog on Ruby
http://talklikeaduck.denhaven2.com/

2007/12/17, Rick DeNatale [email protected]:

to correct me. As far as I can see we are completely in sync.
wrong in two regards.
But note that any class is also an instance.

First, the metaclass isn’t on the inheritance chain of the instance,
it’s on the inheritance chain of the class, but that’s a horse of a
different color.

Again, since a class is also an instance I choose to use the more
general term. But the statement holds true for classes as well because
with regard to this there is no difference between an ordinary
instance and an instance of Class (or Module for that matter). There
is in fact some difference in behavior that results from this (see my
posting) but the singleton class of a class sits between the instance
(the class) and its class (Class).

Second, in the klass chain it’s on the other side of the class, it goes:

instance - klass-> class -klass-> class’

NOT

instance -klass-> class’ -klass-> class

Do I understand you properly that you claim the chain is

Array → Class → Array’

If so, I strongly believe you are wrong. It’s

Array → Array’ → Class

Or did you mean

anArray → Array → Array’

Where Array’ is the singleton class of Array like in

irb(main):001:0> c=class <<Array;self;end
=> #Class:Array

This is correct.

irb(main):003:0> o.xxx
=> #Object:0x7ff73cec
irb(main):009:0> o2.xxx
NoMethodError: undefined method `xxx’ for #Object:0x7ff73cec
from (irb):9
from :0
irb(main):010:0> class <<o2;self;end.instance_methods.grep /xx/
=> []

Not quite the same, what happens here is that cloning an object with a

That’s why I wrote “similar” and not “same”…

singleton class gives the clone a singleton class which looks like the
original object’s singleton class:

One last point, you have to be careful in looking at the results from
experiments like this in irb/ruby programs because some of the
reflection methods in ruby deliberately lie about what’s really
happening under the covers.

Can you elaborate which methods do actually lie?

Cheers

robert