[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

Inheritance and meta-classes

Adam Salter

12/12/2007 11:15:00 PM

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

7 Answers

Adam Salter

12/13/2007 5:00:00 AM

0

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 13/12/2007, at 10:15 AM, Adam Salter wrote:

> 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
>

Robert Klemme

12/13/2007 11:01:00 AM

0

> > 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 Salter <adam.q.salter@gmail.com>:
> 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

Rick DeNatale

12/13/2007 2:34:00 PM

0

On 12/13/07, Robert Klemme <shortcutter@googlemail.com> 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>

> 2007/12/13, Adam Salter <adam.q.salter@gmail.com>:
> > 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"]

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-200712...

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

--
Rick DeNatale

My blog on Ruby
http://talklikeaduck.denh...

Robert Klemme

12/16/2007 12:23:00 PM

0

On 13.12.2007 15:33, Rick DeNatale wrote:
> On 12/13/07, Robert Klemme <shortcutter@googlemail.com> 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>

Ah, now I see! Should've put on my glasses. ;-)

>> 2007/12/13, Adam Salter <adam.q.salter@gmail.com>:
>>> 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"]
>
> 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-200712...
>
> It's simplified since it doesn't include the inclusion of Kernel by Object.

Nice colors. ;-) 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

Rick DeNatale

12/17/2007 3:50:00 PM

0

On 12/16/07, Robert Klemme <shortcutter@googlemail.com> wrote:
> On 13.12.2007 15:33, Rick DeNatale wrote:

> > Here's a simplified diagram
> > http://myskitch.com/rubyredrick/skitched-200712...
> >
> > It's simplified since it doesn't include the inclusion of Kernel by Object.
>
> Nice colors. ;-) 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 Thomas
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

> 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/
> => []

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.denh...

Robert Klemme

12/17/2007 4:06:00 PM

0

2007/12/17, Rick DeNatale <rick.denatale@gmail.com>:
> On 12/16/07, Robert Klemme <shortcutter@googlemail.com> wrote:
> > On 13.12.2007 15:33, Rick DeNatale wrote:
>
> > > Here's a simplified diagram
> > > http://myskitch.com/rubyredrick/skitched-200712...
> > >
> > > It's simplified since it doesn't include the inclusion of Kernel by Object.
> >
> > Nice colors. ;-) 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 Thomas
> 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.

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.

> > 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/
> > => []
>
> 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

--
use.inject do |as, often| as.you_can - without end

Rick DeNatale

12/17/2007 4:43:00 PM

0

On 12/17/07, Robert Klemme <shortcutter@googlemail.com> wrote:
> 2007/12/17, Rick DeNatale <rick.denatale@gmail.com>:

> > 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.denh...