not exactly, it's more a "becomes" relation:
"class definition" ----"becomes"----> "(class) instance"
(?) Please see (attempt to) explanation below. I got a bit enthused with the
ascii art, sorry, but it helped to my understanding of the issue, too.
"instance of" would be false, because:
* the direction fo the arrow would be wrong
Normally, the standard arrow goes from instance to class
person ----is an instance of the class---> Person
* all 'metaclasses' are instances of the class "Class"
Yes, the text says that to avoid drawing too many arrows.
Here is the explanation. Please bear with me and correct possible errors:
Objects have only data (instance variables). Their classes have methods.
When you write "person.say_hello", Ruby looks up the method "say_hello" in
the Person class, sets "self" to the object "person" and executes the
method. If you call a method not in the class Person, the method is looked
up in Person's superclass, and so on. We agree until here, yes?
[Object ]
^
|
[person ] ---- instance of ----> [Person ]
@name say_hello()
But now, you have a special person, who says hello differently. If you
change the method "say_hello()" in Person, *all* instances will use it. So
you insert a singleton class, only for the object "person":
[Object ]
^
|
[person ] ----- instance of ----> [Person ]
@name say_hello()
^
|
[mute ] ---- instance of ----> [(mute) ]
@name say_hello() (redefined only for
"mute")
Now, classes are objects, too, with the difference that they have methods
for their instances to use. So, when you call "Person.new", Ruby looks up
the method "new" in Person's class, which is "Class". Then it sets "self" to
the object "Person" and executes the method.
[Object ] [Module ]
^ ^
| |
[Person ] ---- instance of ----> [Class ]
new()
Now say that you want the class Person to have a new method, for example
"new_with_name()". You must define the method in its class, but if you
define it in "Class", *all* objects that are instances of class (that is,
all classes) will have that method. So you create a singleton class for
Person:
[Module ]
^
|
[Class ]
[Object ] new()
^ ^
| |
person -----> [Person ] ---- instance of ----> [(Person) ]
new_with_name()
We agree until now? "person.id()" will try to find "Person#id", then
"Object#id". "Person.attr_accessor" will try to find
"(Person)#attr_accessor", then "Class#attr_accessor", then
"Module#attr_accessor". First go to the right, then up until you find it.
Now let's say you create a new class "Man", inheriting from "Person". You
expect to be able to say "Man.new_with_name()", so the class of "Man"
shouldn't be "Class", but "(Person)". Well, let's assume that the singleton
class for "Man" is already created:
[Class ]
[Object ] new()
^ ^
| |
person -----> [Person ] ---- instance of ----> [(Person) ]
^ ^ new_with_name()
mute -> [(mute)] | ^
| |
man --------> [Man ] ---- instance of ----> [(Man) ]
Of course, the same logic applies to "Person". If "Object" has a singleton
class, we expect that "(Person)" inherits from it ("(Object)"). And also,
all these singleton classes (metaclasses) are instances of "Class". So let's
complete the diagram:
[Class ]
new()
^
|
[Object ] ---- instance of ----> [(Object) ] ---> [Class]
^ ^
| |
person -----> [Person ] ---- instance of ----> [(Person) ] ---> [Class]
^ ^ new_with_name()
mute -> [(mute)] | ^
| |
man --------> [Man ] ---- instance of ----> [(Man) ] ---> [Class]
Well, that's all. I can't squeeze out any more understanding of metaclasses
from my brain. In the upper stages things get a little mixed up, because
metaclasses are instances of Class, but also ultimately inherit from it, so
you need a diagram as the one in "ri Class" to sort these relationships out.
But I don't think that diagram is wrong at all.
Hope this helped. It helped me, at least.
class Person
class << self # now we are in class (Person)
def new_with_name name
o = new
o.name = name
o
end
end
def say_hello
puts "Hello"
end
attr_accessor :name
end
class Other
end
person = Person.new_with_name "Someone"
puts person.name
person.say_hello
mute = Person.new
class << mute # now we are in class (mute)
def say_hello
puts "..."
end
end
mute.say_hello
class Man < Person
end
fred = Man.new_with_name "Fred"
puts fred.name
other = Other.new_with_name "x"