Peter's Blog

Redefining the Impossible

Ruby Metaclasses


Sometimes software jargon gets in the way of understanding what practical use something is. For example, you have a problem and you want to know how to solve it so where do you look it up in the ruby book?

What I am going to describe is based on the concept of 'metaclasses' which are classes for creating classes. If that jargon fried your brain then lets have a problem and an example.

I want to create classes for implementing Commands. Each Command class has a command number and I want to define this Command Number in the class definition but I don't want to wear my fingers out typing them in. What nice syntactic sugar can ruby offer to help me with this?

   1  #
   2  # Class for building command classes
   3  #
   4  class Command
   5    class << self
   6      attr :nCommandNumber
   7  
   8      def CommandNumber( nNumber)
   9        @nCommandNumber = nNumber
  10      end
  11    end
  12  end

Ok, horrible syntax so what on earth is going on here? Well the effect of 'class << <object>' is to add new methods to an object so in this case we are adding a new attribute 'nCommandNumber' to hold our command number and a new method 'CommandNumber' so set its value to an object referred to by 'self'.

So what is 'self' equal to? Well it turns out that when you use this class (Command) to create another class (ACommand) then 'self' will hold the value of the 'ACommand' class object, i.e. the object that creates instances of ACommand objects.

#
# Declare a specific command
#
class ACommand < Command
  CommandNumber 123
end

Hum, that looks easy. When ACommand is being defined the line 'CommandNumber 123' will result in the CommandNumber method in Command being executed on the ACommand class object so 'ACommand' will have an attribute called nCommandNumber added to it. We can see whether this happened with:

print ACommand.nCommandNumber
=>123

and indeed it works sweetly (which is what you would expect from syntactic sugar). It is important to note that it is the ACommand class itself that has these attributes, NOT objects CREATED by ACommand:

oAC = ACommand.new
print oAC.nCommandNumber
NoMethodError: undefined method `nCommandNumber' for #<ACommand:0x345e180>
        from (irb):15

so how can an object created by ACommand find the value of this attribute?

oAC.class.nCommandNumber
=> 123

one of the attributes of the object is the class that created it.

How would this be done in a lesser object orientated language? Probably using techniques such as:

   1  class Command
   2    def GetCommandNumber
   3       raise RuntimeError, "Pure base function called"
   4    end
   5  end
   6  
   7  class ACommand < Command
   8    def GetCommandNumber
   9       return 123
  10    end
  11  end

or

   1  class Command
   2    def initialize( nCommandNumber)
   3      @nCommandNumber = nCommandNumber
   4    end
   5  end
   6  
   7  class ACommand < Command
   8    def initialize
   9      super( 123)
  10    end
  11  end

or something similar. Like all syntactic sugar, it doesn't make the impossible possible (anything is possible in hand-coded assembler) it just makes for less typing and clearer code.


Filed under: noob ruby

Jesse Says:

This is the most straightforward explanation of Ruby Metaclasses I have seen, well done, and thank you. I had pretty much gathered this already after bashing my head against a wall while reading half a dozen other explanations...keep up the good work.

Have Your Say

I welcome constructive comments or questions but I reserve the right to delete any comments that displease me.

Who are you?

(Optional) If you enter an email address here I might email you back. Your email address will not be sold to spammers or shown anywhere

What do you have to say?