Meta Programming in Ruby – Part One
Posted on September 10, 2007
Filed Under ruby | Leave a Comment
def warning
This is written by a Java programmer currently learning Ruby. It’s really a note to myself. I’ve found the best way learn something, is to write about it. Please, those in the know, I would appreciate comments on glaring errors and/or omissions…
end
So, there I was happily stepping through the on-line version of Programming Ruby (currently figuring out Ranges in the chapter on Standard Types). I ‘m in no particular hurry whatsoever when I come across this article:
This article changed my thinking dramatically on Ruby and upped the ante on the language capabilities. I simply couldn’t ignore it. I recommend you read this article, then read it again, and only then step through it within your favourite Ruby development enviornment. This is what a novice picked up from the article:
class Object # you can open up any class and add stuff to it
# use semi-colons to squash things onto on line
def metaclass; class << self; self; end; end
end
Semi-colons do not scan well with me. I’m trying to break thing down into blocks So lets re-write this to make it a little more readable first:
class Object # open up the Object class
def metaclass # add a method called ‘metaclass’
class << self # get access to the metaclass for this object instance
self # and then return it to the caller
end
end
With this code all you need to do to get access to an object’s metaclass is call that method. Consider the following example:
class MyShape
attr_accessor :sides, :colour
def initialize(sides,colour)
@sides, @colour = sides,colour
end
end
s1 = MyShape.new(4,’red’);
s2 = MyShape.new(3, ‘yellow’);print “\nShape1 Class: “, s1.class
print “\nShape2 Class: “, s2.class
print “\nShape1 SuperClass: “, s1.class.superclass
print “\nShape2 SuperClass: “, s2.class.superclass
print “\nShape1 MetaClass: “, s1.metaclass
print “\nShape2 MetaClass: “, s2.metaclass
print “\nShape1 MetaClass: “, s1.metaclass.class
print “\nShape2 MetaClass: “, s2.metaclass.class
print “\nShape1 MetaClass Superclass: “, s1.metaclass.superclass
print “\nShape2 MetaClass Superclass: “, s2.metaclass.superclass
The output from this code is as follows:
Shape1 Class: MyShape
Shape2 Class: MyShape
Shape1 SuperClass: Object
Shape2 SuperClass: Object
Shape1 MetaClass: #<Class:#<MyShape:0×2b75398>>
Shape2 MetaClass: #<Class:#<MyShape:0×2b7535c>>
Shape1 MetaClass: Class
Shape2 MetaClass: Class
Shape1 MetaClass Superclass: #<Class:MyShape>
Shape2 MetaClass Superclass: #<Class:MyShape>
Both s1 and s2 are of type MyShape, where MyShape inherits from Object. No surprises there.
Now see that the metaclasses are instances themselves, or virtual classes in Ruby talk, that are attached to the MyShape object instances s1 and s2.
# the metaclass instances are difference
puts s1.metaclass.object_id # 22784300
puts s2.metaclass.object_id # 22784250
All metaclasses are of type Class. Moreover, for MyShape instances, all metaclasses derive from Class:MyShape. This is where things get interesting.
In Seeing Metaclasses Clearly, the author defines metaclasses as:
“a class which an object uses to redefine itself”
So how does an object re-define itself? Well, it behaves differently. How do you get an object to behave differently? You add/change the methods that the object in question can respond to. So how do metaclasses enable you to change the methods that an object can respond to? Check this out:
class << s1
def only_available_to_s1
puts ‘this blew me away’
end
end
Now let’s run the following code:
s1.only_available_to_s1 # outputs — this blew me away
s2.only_available_to_s1 # error, no method not _available_to_s1
Some people think that the class << s1 thing is too verbose, so here is another way of doing the same thing:
def s1.only_available_to_s1
puts ‘this blew me away’
end
So what’s going on here. Well we have opened up the metaclass for the s1 object instance and added the method only_available_to_s1 to it. Yes, you read it correctly. Remember object instances store data. Classes store methods for object instances. Metaclasses are the ideal place to store methods on a per object instance basis. That is so cool.
In Ruby there is name for methods that an object finds within an attached metaclass. These are called singleton methods. Why? Because only a single metaclass can be attached to an object. Executing the following block of code:
puts s1.singleton_methods
prints out the following:
only_available_to_s1
This is the only singleton method available to the s1 object instance. This method is stored within its attached metaclass.
You can of course go meta meta, but at that put I thought things where getting a little crazy so I stopped. The article goes into more details, but they too thought that you are probably going a little too far as well if you need to go meta meta.
I’m going to stop now and pick up again later on relationship between metaclasses and the inheritance tree.
Comments
Leave a Reply

