I've been keeping these methods in a file called metaid.rb and it's a start toward building a little library that can simplify use of metaclasses. Let's talk about metaclasses and I advise you to keep metaid.rb at your side. Take time to run some code from this article and you'll understand much better.

Okay, so the @driver instance variable has an accessor. When Ruby sees attr_accessor :driver in the MailTruck class definition, you get reader and writer methods. The methods driver and driver=.

These methods are stored in the class. So the instance variable is in the object and the accessor methods are in the class. They're in two completely different spots.

It's an important lesson: objects do not store methods, only classes can.

Classes Are Objects

Okay, but classes are objects, right? I mean if everything is an object in Ruby, then classes and objects should both be objects. Which makes them the same?

Sure, classes are objects. You can run all the same methods on classes that you can run on object. Look, they each have their own ID in the symbol table.

>> m.object_id
=> 68058570
>> MailTruck.object_id
=> 68069450

But I've already told you: classes store methods. They're different. Now I know you're probably a bit confused wondering, "If a class is an object, but objects are built on classes, isn't there a big confusing infinite cycle here that you're not explaining?"

No, there's not. I hate to break it to you, but a class isn't really an object. From Ruby's source code:

Look! A class has an m_tbl (a symbol table for storing methods) and a superclass (pointer to a superclass).

But let me reassure you. To a Ruby programmer, a class is an object. Because it meets the two big criteria: you can store instance variables in a class and it is descended from the Object class. That's it.

This is handy if you want to dump one specific object with a certain style of YAML without effecting every object from that class. In the above example, only the object in the m variable will be output with its properties in order. All other MailTruck objects will be output in whatever way the YAML library chooses. Sometimes we may want to display a certain string one way without needing to modify the String class (which affects every string in your code).

So the object in the m variable has its own special toyamlproperties method. It's stored in a metaclass. The metaclass stores methods for the object and sits right in the inheritance chain.

We could also add the toyamlproperties method with this convenient syntax:

def m.to_yaml_properties
['@driver', '@route']
end

If you have the metaid.rb methods from the top of this article loaded, try this:

When you use the class << m syntax, you're opening up a metaclass. Ruby calls these virtual classes. Notice the result of m.metaclass. A class attached to an object:

#<Class:#<MailTruck:0x81cfb94>>.

When an object finds methods in an attached metaclass, these methods are referred to as the object's singleton methods rather than the object's metaclass' instance methods (if you get my drift.) And since there can only be a single metaclass attached to an object, it's called a singleton.

It's much easier to see metaclasses when you're using the metaclass method. Normally, you would need to use class << self; self; end ) wherever you wanted to root out a metaclass. But this makes it much simpler.
Do Metaclasses Need Metaclasses?

Check out those frivolous metaclasses we're creating. So what can we do with a metaclass of a metaclass?

Well, the same thing we do with a normal metaclass. A normal metaclass holds methods for an object. So a metaclass of a metaclass holds methods for that metaclass ? which is just an object, of course!

The problem with a metaclass of a metaclass is that there's not much practical use for them. You can only use the methods if you're deep inside the chain and we don't really want to spend much time down there.

Metaclasses are only really useful one level deep. You want to have give methods to an object. Or, as you will see, you might want a specific class to have a metaclass. Beyond that, you're just storing methods in these obscure metaclasses that no one can really get at. Which you might need to do sometime. Who knows.

The important thing to know at this point is: metaclasses don't go up, they go out. Yes, when you create a metaclass for an object, it happens to intercept method calls before the object's inheritance chain. But that doesn't mean inheritance is affected by further metaclasses. When you create a metaclass of a metaclass, it has no affect on the object referred to by the original metaclass.
Metaclasses Have One More Funky Trick For Classes and It's The Crucial Trick In The Metaprogrammer's Handbook

One more point and I believe this one is the juiciest. If you read the rest of this essay and quit before this section, you've come away without the most important lesson. You may know some nice things about objects and metaclasses, but it all pales.

I'm going to reiterate two previous statements about classes and build on them.

Class are objects. This means they can hold instance variables.

Metaclasses hold instance methods. When attached to an object, these methods become singleton methods. These methods intercept calls before they trickle up the chain of inheritance.

Have you ever used instance variables in a class before? I don't mean in a class method. I mean in the class itself.

Class instance variables and metaclass instance methods are really pretty pointless in a plain old class. But when inheritance enters the mix, the party comes alive. Writhing bodies and drunken madness, believe me.

class MailTruck
def self.company( name )
meta_def :company do; name; end
end
end

The above method is remarkably simple, but excavates a beachhead worth of possibilities. A new company class method is added to MailTruck that can be used in a class definition.

class HappyTruck < MailTruck
company "Happy's -- We Bring the Mail, and That's It!"
end

Okay, so the company class method gets executed with the Happy's company name and slogan. What does meta_def do with it??

Well, the meat of meta arrives here. The meta_def adds a new method called company to the HappyTruck metaclass. The beauty of this is that the method is not added to the MailTruck metaclass, but to the derived class HappyTruck.

This may seem simple, but it's very powerful. You can write simple class methods which will add class methods to a derived class. This is the secret to Rails and Ruby/X11 and so many other examples of metaprogramming in Ruby.
Dwemthy's Array

I discovered most of this while building Dwemthy's Array for my cartoon Ruby book. I was able to simplify the Creature code (which gives a readability to the RPG) down to this fragment:

The metadef and classdef help make the metaprogramming a bit more clear. Pay close attention to the use of instance variables in the meta_def. If you want to understand just why class variables won't work in this situation, then try changing the instance variables to class variables above.

Then, start creating monsters as described on the Dwemthy's Array page and you'll watch them step all over each other.

11:51 PM
disoriented?

why the lucky stiff
is a fledgling freelance professor, one who will die young and make no lasting impression.