One of the great accomplishments of Ruby is that its object and class model bring about a wonderful unity and simplicity. Even things that might seem like they should be hard or obscure are actually very transparent, once you have the hang of how the model works.

Unfortunately, there’s a trend toward drawing an increasingly sharp line between regular programming and “metaprogramming” in Ruby discussions, where metaprogramming is understood to mean… well, I’m not sure what it’s understood to mean, the way it’s getting used in connection with Ruby, and that’s the problem.

There’s no natural separation between programming and metaprogramming in Ruby, and no reason to make life harder by breaking them apart. The thrust of Ruby’s design is to dissolve complexities, so that even things that appear to be “wizardly” or full of “dark magic” actually make perfect sense in terms of a relative small number of underlying language principles.

I’m not saying it’s all a snap to learn. But I do consider it counterproductive to posit some kind of special barrier or fortification surrounding a particular set of Ruby programming techniques. Learning Ruby isn’t like scaling a mountain. It’s more like exploring a plain, with interesting features and a distant but very attractive horizon. One thing leads to another, but not in a way that requires you to fight gravity to get there.

Horizontal learning: class_eval and class

You can get a feel for this horizontal, energy-efficient way of learning Ruby by looking at pairs of closely-related techniques, where one is the kind of thing you learn on the first day of studying Ruby and the other…isn’t.

A good example of such a pair is the class keyword and the class_eval method. class_eval often comes up as an example of a metaprogramming technique, and therefore as something obscure and difficult and wizardly. It isn’t. Let’s probe it systematically, and in context.

We’ll start with the class keyword, one of the bread-and-butter techniques of Ruby—the basic:

  class MyClass
    # stuff
  end

Certain things happen when you start a class definition block:

  • you start a new local scope
  • the special object self is the class object itself
  • any methods you define with the def keyword are instance methods of the class

All of this is easily visible and verifiable. Here’s a little testbed:

  # Just for demo, we'll create a local variable in the
  # outer scope.
  x = 1

  # Now the class definition block.
  class Teacher

  # Examine the value of self
    puts self

  # Try to examine the local variable x -- but it will
  # raise an exception, because we're in a new local scope
  # (so we rescue the exception).
    puts x rescue puts "x is from a different scope!" 

  # Define some instance methods.
    def initialize(name)
      @name = name
    end
    def name
      @name
    end
  end

  # Run some demo code.
  t = Teacher.new("David")
  puts t.name

The output from this little program is:

  Teacher
  x is from a different scope!
  David

Everything works as expected.

Now, let’s look at class_eval.

class_eval is a method, rather than a keyword. You don’t do this:

  class_eval Teacher   # No!

Rather, you call it as a method, and give it a code block:

  Teacher.class_eval do
    # code block!
  end

Now, rather than get all panicked about how this is “metaprogramming”, let’s look at what’s actually happening, and why.

As we’ve seen, the class keyword (class Teacher) brings about certain conditions involving self, scope, and the effect of defining methods. So let’s use the same criteria to examine what happens with class_eval:

  y = 1
  Teacher.class_eval do
    puts self
    puts y rescue "y is from a different scope!" 

    def subject
      "History" 
    end
  end

  t = Teacher.new("David")
  puts "#{t.name} teaches #{t.subject}." 

(We’ve got all teachers teaching history, but we’ll live with that for the sake of the demo.)

When you run this code, you get:

  Teacher
  1
  David teaches History.

What does this tell us? It tells us that when you use class_eval:

  • you do NOT start a new local scope; you’re in the same one
  • the special object self is the class object itself
  • any methods you define with the def keyword are instance methods of the class

Consequently, you now know that if you want to add instance methods to a class while still retaining access to local variables in the outer scope, you should use class_eval. If you don’t need those local variables, you can use the class keyword.

There’s a little more to it….

There are of course more differences between class and class_eval (including the fact that class_eval can operate on a string as well as a code block, and that it’s very handy for anonymous classes). But note that as we study class_eval, we’re not in a different universe, or higher up a mountain and running out of air. We’re actually still playing in the same ballpark we were playing in when we looked at the class keyword.

The relation between class and class_eval, from the point of view of someone interested in using them, is a horizontal, lateral relation. You hold one technique in one hand, and the other in the other hand, and you heft them and compare them and decide what you want to do. Nothing is gained by putting class_eval on a pedestal and getting all worked up about how “meta” it is. All you have to do is study it a little and then use it when it’s the correct choice for accomplishing what you’re trying to accomplish.

Sorry, comments are closed for this article.