Ruby Metaprogramming: Part I

If you’re working with Ruby, chances are by now you’ve heard the word “metaprogramming” thrown around quite a lot. You may have even used metaprogramming, but not fully understood the true power or usefulness of what it can do. By the end of this article, you should have a firm grasp not only of what it is, but also what it capable of, and how you can harness one of Ruby’s “killer features” in your projects.

What is “metaprogramming”?

Metaprogramming is best explained as programming of programming. Don’t let this abstract definition scare you away though, because Ruby makes metaprogramming as easy to understand as it is to work with.

Metaprogramming can be used a way to add, edit or modify the code of your program while it’s running. Using it, you can make new or delete existing methods on objects, reopen or modify existing classes, catch methods that don’t exist, and avoid repetitious coding to keep your program DRY.

Understanding how Ruby calls methods

Before you can understand the full scope of metaprogramming, it’s imperative that you grasp how Ruby finds a method when it is called. When you call a method in Ruby, it must locate that method (if it exists) within all the code that is within the inheritance chain.

class Person
  def say
    "hello"
  end
end

john_smith = Person.new
john_smith.say # => "hello"

When the say() method is called in the example above, Ruby first looks for the parent of the john_smith object; because this object is an instance of the Person class, and it has available a method called say(), this method is called.

Things get more complicated however when the object is an instance of a class which has inherited from another class:

class Animal
  def eats
    "food"
  end

  def lives_in
    "the wild"
  end
end

class Pig < Animal
  def lives_in
    "farm"
  end
end

babe = Pig.new
babe.lives_in # => "farm"
babe.eats # => "food"
babe.thisdoesnotexist # => NoMethodError: undefined method `thisdoesnotexist' for #<Pig:0x16a53c8>

When we introduce inheritance into the mix, Ruby needs to consider methods defined higher in the inheritance chain. When we call babe.lives_in(), Ruby first checks the Pig class for the method lives_in(); because it exists, it’s called.

It’s a slightly different story when we call the babe.eats() method, though. Ruby checks for that method by asking the Pig class if it can respond to eats(), and in the absence of that method existing on Pig, it will continue by asking the parent class Animal if it can respond; it can in our case, so it will be called.

When we call babe.thisdoesnotexist(), because the method does not exist, we get a NoMethodError exception. You can think of this as a sort of cascade: whatever method is defined lowest in the inheritance chain will be the method that ends up being called; if the method doesn’t exist at all, an exception is raised.

Based on what we have discovered so far, we can summarise the way Ruby looks up each method as a pattern something like the following:

  • Ask the object’s parent class if it can respond to the method, calling it if found.
  • Ask the next parent class up if it can respond to the method and call it if found, continuing this step towards the top of the inheritance chain for as long as necessary.
  • If nothing in the inheritance chain can respond to the method being called, the method does not exist and an Exception should be raised.

Note that because every object inherits from Object (or BasicObject in Ruby 1.9) at the very top level, that class will always be the last to be asked, but only if it makes it that far up the inheritance chain without finding a method that can respond.

Introducing the Singleton class

Ruby gives you the full power of Object Oriented programming and allows you to create objects that inherit from other classes and call their methods; but what if only a single object requires an addition, alteration, or deletion?

The “singleton class” (sometimes known as the “Eigenclass”), is designed exactly for this and allows you to do all that and more. A simple example is in order:

greeting = "Hello World"

def greeting.greet
  self
end

greeting.greet # => "Hello World"

Let’s digest what’s just happened here line by line. On the first line, we create a new variable called greeting that represents a simple String value. On the second line, we create a new method called greeting.greet, and give it a very simple content. Ruby allows you to choose what object to attach a method definition to by using the format some_object.method_name, which you may recognize as the same syntax for adding class methods to classes (ie. def self.something). In this case, as we had greeting first, the method has been attached to our greeting variable. On the final line, we call the new greeting.greet method we just defined.

The greeting.greet method has access to the entire object it has been attached to; in Ruby, we always refer to that object as self. In this case, self refers to the String value we attached it to. If we had attached it to a Array, self would have returned that Array object.

As you’re about to see, adding methods using some_object.method_name is not always the best way to do such tasks. That’s why Ruby provides another far more useful way of working with objects and their methods in a dynamic way. Meet the Singleton class:

greeting = "i like cheese"

class << greeting
  def greet
    "hello! " + self
  end
end

greeting.greet # => "hello! i like cheese"

The syntax is very strange, but the outcome is the same as our some_object.method_name way of adding methods. This singleton class method allows you to add many methods at once without having to prefix all of your method names. This syntax also allows you to add anything you would normally add during the declaration of a class, including attr_writer, attr_reader, and attr_accessor methods.

How does it work?

So how does it actually work? The name “singleton class” might have given this away slightly, but Ruby is sneaky and adds another class into our inheritance chain. When you try to operate on the singleton class, Ruby needs a way to add methods to the object we’re adding to, something that the language does not allow. To get around this it creates a secret new class, which we call the “singleton class”. This class is given the methods and changes instead, is made the parent of the object we’re working on. This singleton class is also made an instance of the previous parent of our object so that the inheritance chain remains mostly unchanged:

some object instance > singleton class > parent class > ... > Object/BasicObject

Returning to what we know about Ruby method lookup, we previously decided that there was three simple steps that the Ruby interpreter followed when looking up a method. Singleton classes add one extra step to this lookup process:

  • Ask the object if its singleton class can respond to the method, calling it if found.
  • Ask the object’s parent class if it can respond to the method, calling it if found.
  • Ask the next parent class up if it can respond to the method and call it if found, continuing this step towards the top of the inheritance chain for as long as necessary.
  • If nothing in the inheritance chain can respond to the method being called, the method does not exist and an Exception should be raised.

As we previously discussed, you can think of this as a cascade, and that has a very important impact on our idea of objects in Ruby: not only can Objects gain methods from their inherited classes, but now they can gain individually unique methods as the program is running.

Putting metaprogramming to work with instance_eval and class_eval

Having singleton classes is all good and well, but to truly work with objects dynamically you need to be able to re-open them at runtime within other functions. Unfortunately, Ruby does not syntactically allow you to have any class statements within a function. This is where instance_eval comes into the picture.

The instance_eval method is defined in Ruby’s standard Kernel module and allows you to add instance methods to an object just like our singleton class syntax.

foo = "bar"
foo.instance_eval do
  def hi
    "you smell"
  end
end

foo.hi # => "you smell"

The instance_eval method can take a block (which has self set to that of the object you’re operating on), or a string of code to be evaluated. Inside the block, you can define new methods as if you were writing a class, and these will be added to the singleton class of the object.

Methods defined by instance_eval will be instance methods. It’s important to note that the scope is that of instance methods, because it means you can’t do things like attr_accessor as a result. If you find yourself needing this, you’ll want to operate on the Class of the object instead using class_eval:

bar = "foo"
bar.class.class_eval do
  def hello
    "i can smell you from here"
  end
end

bar.hello # => "i can smell you from here"

As you can see, instance_eval and class_eval are very similar, but their scope and application differs ever so slightly. You can remember what to use in each situation by remembering that you use instance_eval to make instance methods, and use class_eval to make class methods.

Why do I care?

As this point you’re probably thinking “that’s great, but why should I care? What real world value does any of this have?” The simple answer is “yes you should” and “a lot”.

Metaprograming allows you to create more flexible code, be it through beautiful APIs or easily testable code. More than that though, it allows you to do that using powerful and elegant programming techniques and syntax. Metaprogramming allows you to create code that is DRY, highly reusable, and extremely concise.

In part two of this series we’ll look at how to apply Metaprogramming to everyday problems, and see how the elegance and power of it can change the way you develop solutions forever.

Win an Annual Membership to Learnable,

SitePoint's Learning Platform

  • Debbie

    Great article. Well done.

  • https://twitter.com/bugant Matteo

    The discussion on instance_eval and class_eval seems particularly wrong to me.

    Even in the example you show after saying (incorrectly) that instance_eval adds a class method, you are invoking an instance method: if it was a class method it would have been called on the class of foo, i.e., String.hi.

    instance_eval evaluates ruby code in the context of the receiver while class_eval evaluates a given block in the context of an existing class.

    Am I missing something? Could you please better explain your point?

    • http://unfinitydesign.com/ Nathan Kleyn

      Matteo – You are absolutely correct, and I’ve amended the article appropriately; I’m not sure what I was thinking when I wrote that…if I was thinking at all. ;]

      • https://twitter.com/bugant Matteo

        No problem :) very nice article and really interesting topic!

        • http://unfinitydesign.com/ Nathan Kleyn

          Thanks Matteo!

  • J. Merrill

    I think that you made the “method lookup” concept more difficult than it really is. (You did not point out that it is really “name lookup” because variables work the same way.)

    Ruby asks the object “do you know what this is?” and it does one of two things. If there is a definition in the object itself, that definition is used. If not, the object asks its parent — in the case of an instance variable, that is its class; if the object is a class, it’s the class that the class inherits from. That same logic takes place there, possibly moving up multiple levels — and if we ever get to the top (Object / BasicObject), then (simplifying) an exception is raised.

    (The simplification is that we’re not playing the “method missing” game. But that is for another article.)

    All objects (and as you’ve pointed out, classes are also objects) do this. It is not the case that the initial “target” does all the work — it looks in itself, then asks its parent if it doesn’t know. An object only needs to know its parent, not the entire chain.

    • http://unfinitydesign.com/ Nathan Kleyn

      You are, of course, fundamentally correct. However, I chose to explain it this way because I wanted to place particular importance on how the Singleton class slots in and behaves as part of this process, not necessarily how the actual process works in full.

      That said, your explanation of the method lookup process on it’s own is indeed a lot better than mine.

  • Bluebells

    Wonderful explanation about metaprogramming!