After watching the Pragmatic Screencasts of The Ruby Object Model and Metaprogramming with Dave Thomas a few of us at the office decided to investigate further.
Developers in the Ruby on Rails community are familiar with the concept of constructing custom finders for models.
class User
class << self
def find_by_geography(options)
# ... custom geographic finder logic ...
end
end
end
One invokes method name find_by_geography directly from the User class.
@users = User.find_by_geography(options)
A programmer can alternately prepend self. to the method name.
class User
def self.find_by_geography(options)
# ... custom geographic finder logic ...
end
end
And the method is called in exactly the same fashion.
@users = User.find_by_geography(options)
QUESTION: What is the nature of a method that employs both the self. and the class << self constructs?
The terms metaclass, eigenclass, shadow class, virtual class, or ghost class have been used to refer to the anonymous singleton class in between every object and its class in Ruby. This article will use the term “metaclass.”
How does one access the metaclass? The following defines a method metaclass for every object.
class Object
def metaclass
class << self
self
end
end
end
>> cat = 'miaow'
=> "miaow"
>> cat.metaclass
=> #<Class:#<String:0x525670>>
>> cat.metaclass == cat.class
=> false
Another useful method definition for this article follows.
class Object
def metaclass?(object)
self.metaclass == object
end
end
>> x = cat.metaclass
=> #<Class:#<String:0x525670>>
>> y = 'not cat'.metaclass
=> #<Class:#<String:0x586970>>
>> z = cat.metaclass.metaclass
=> #<Class:#<Class:#<String:0x525670>>>
>> cat.metaclass?(x)
=> true
>> cat.metaclass?(y)
=> false
>> cat.metaclass?(z)
=> false
Let us see an example of a class-level method definition and a self. method definition also declared at the class-level.
class A
class << self
def b
self
end
def self.c
self
end
end
end
Two questions come to mind: “How do I call those methods?” “What do they return?”
The answers to method b are easy enough.
>> A.b
=> A
>> A.b == A
=> true
The method b is called from class A. It returns class A.
>> A.b.b.b.b.b.b
=> A
How does one invoke the other method?
>> A.c
NoMethodError: undefined method `c' for A:Class
>> A.metaclass.c
=> #<Class:A>
>> A.metaclass?(A.metaclass.c)
=> true
The method c is called from the metaclass of A. It returns the metaclass of A.
>> A.metaclass.c.c.c.c.c.c
=> #<Class:A>
Let us observe inheritance and if method self.c is inherited.
class B < A
end
>> B.b
=> B
>> B.b.b.b.b.b.b
=> B
>> B.c
NoMethodError: undefined method `c' for B:Class
>> B.metaclass.c
NoMethodError: undefined method `c' for #<Class:B>
Nope.
ONE MORE META
What is the nature of a method that employs both the self. and the class << self constructs and returns self within another class << self construct?
Perhaps a code example is better
class A
class << self
def self.meta_metaclass
class << self
self
end
end
end
end
Hmmm… that’s a little hard to read. The following should be a little better.
class A
class << self
def b
self
end
def self.c
self
end
def self.meta_metaclass
class << self
self
end
end
end
end
Since is has similar method defintion to self.c it is most probable that self.meta_metaclass is called the same way.
>> A.metaclass.meta_metaclass
=> #<Class:#<Class:A>>
Yes!
Now for the other question: What is returned?
>> mm = A.metaclass.meta_metaclass
=> #<Class:#<Class:A>>
>> A.metaclass?(mm)
=> false
>> A.metaclass.metaclass?(mm)
=> true
The method self.meta_metaclass returns the metaclass of the metaclass of A
CONUNDRUMS
~ irb -r meta.rb
>> String.metaclass.superclass
=> #<Class:Class>
>> Class.metaclass.superclass
=> #<Class:Class>
>> Object.metaclass.superclass
=> Class
What?
~ irb -r meta.rb
>> Class.metaclass.superclass == Class.metaclass
=> true
>> Class.metaclass.superclass
=> #<Class:Class>
>> Class.metaclass.metaclass.superclass
=> #<Class:#<Class:Class>>
>> Class.metaclass.superclass
=> #<Class:#<Class:Class>>
>> Class.metaclass.superclass == Class.metaclass
=> false
What? What?
~ irb -r meta.rb
>> A.metaclass.c.meta_metaclass.c.meta_metaclass
=> #<Class:#<Class:#<Class:A>>>
>> A.metaclass.c.meta_metaclass.c.meta_metaclass
NoMethodError: undefined method `c' for #<Class:#<Class:A>>
from (irb):2
What? What? What?
If you answer, truly you have a dizzying intellect.