Ruby objects have methods called
class_eval. They both execute a block with
self referencing the object.
For a long time I thought these methods were basically synonymous, that for some reason I was simply supposed to use
class_eval for classes (and
module_eval for modules) and
instance_eval for everything else. Just accept it and don’t ask questions, I told myself.
But there is a difference, one that points to an important but seldom understood aspect of Ruby. In this article, we’ll explore the distinction and what it means for our Ruby code.
The “current object”
Let’s start with what many Ruby programmers already know.
Like many object-oriented languages, Ruby has the notion of a “current object”. This is the object that receives method calls by default, and it is the object that owns any instance variables you reference. Ruby sets the current object inside every method call, so the method can access the object’s instance variables and easily call other methods of the object.
You can get a reference to the current object using the
self keyword, but for the most part, it’s used implicitly when resolving methods or instance variables.
There is always a current object, even when you’re not in a method. Within a class definition, the current object is the class. And Ruby even provides a “main” object that is the current object at the top level of a Ruby script.
Changing the current object with instance_eval
You can also change the current object using the
instance_eval method (or the closely related
instance_eval method is commonly used for building domain-specific-languages (DSLs) because it lets us control how methods are looked up. When you write Rails routes, for example, Rails is using
instance_eval to give you a simple syntax for declaring paths and resources.
The current object has a strong effect on looking up method names, but what about defining a method? This is where the story gets a bit more complicated.
def keyword is typically used to define a new method. Normally,
def appears within a class or module definition, so it’s clear where the method is defined. But Ruby is very flexible. You can put a
def almost anywhere: outside any class, in a block, even within another method.
What do you think happens when a method is defined inside another method?
No, Ruby doesn’t have any weird notion of “nested” methods. All that’s happening here is that defining the
dismissal method is part of the functionality of the
dismissal is defined when you call
So what class is
dismissal defined on? One might guess that it’s also defined on the
Greeter class, and in this case you’d be right. But why is that the case? It may seem “obvious” or “intuitive,” but it’s very important to understand what’s actually going on, because things won’t always be obvious. Take this example:
instance_eval to set the object context to the
Greeter class when we define the method. So where is
dismissal defined? You might guess, on the
Greeter class. But you’d be wrong. It gets defined on the
So what’s really going on? What actually governs on which class a method is defined?
The “current class”
The answer is that “self” isn’t the only piece of context that Ruby maintains. The “current object” governs lookup of method names (and instance variables), but method definitions are governed by a separate piece of context that I’ll call the “current class”.
Note: other terms have been used elsewhere for the “current class”. For example, Yugui used the term “default definee” when she wrote about the Ruby contexts in an earlier article.
When you define a class using the
class keyword, it creates a class and sets the current class context so that methods are attached to it. Similarly, when a method is called, the current class context is set to
instance_eval sets the current object but not the current class.
And this is where
class_eval is different. Whereas
instance_eval sets only the current object,
class_eval sets both the current object and current class.
So we’ve seen that Ruby maintains separate, independent class and object contexts. And that brings up an interesting question: Why?
Why are “current class” and “current object” distinct?
Why maintain two separate pieces of state? Isn’t that needlessly complicated?
It turns out there’s a good reason for it, and it has to do with Ruby’s goal of making programming intuitive. Let’s look again at the two places we’ve seen method definitions.
When you use a
class declaration, Ruby sets both the current object and the current class to the same thing, the class. This lets you both define methods and call class methods such as
include, and even
But when you define a method in another method, the current object and the current class are not the same. An arbitrary object doesn’t have methods; only classes do. So the current class is set to the class of the object.
In order to support reasonable behavior in both of these two cases, Ruby needs the flexibility to be able to set up the two values, current object and current class, differently.
Why does this matter?
So Ruby has more context than just
self. Why does it matter?
Well, it might help you avoid some bugs, and it’s always useful to understand the details of the language you are using. But it’s particularly important when you are designing interfaces for other developers to use, especially if you’re designing a domain-specific language.
Ruby is a flexible language, and Ruby programmers expect to be able to use that flexibility, calling code, writing helper methods, and generally doing things you might not expect. If the Rails router had set the current class to some strange value and made it difficult for users to write helper methods, Rails would have been much more brittle and difficult to use, and ultimately less successful.
So it’s important for Ruby library writers to make sure you choose the correct method when using
instance_eval. And in general, library designers need to pay attention to the current class in order to avoid unexpected behavior.
If you’re interested in learning some tips for designing interfaces that pay attention to these issues, see my talk “Ruby Ate My DSL!” from RubyConf 2019.
It turns out, the rabbit hole goes even deeper. A third independent piece of Ruby context governs constants, where they are defined and how they are looked up. This piece of context, known internally as the
cref, represents the lexical nesting of classes and modules, basically what you get from calling
Module.nesting. However, unlike the current object and current class, the cref can’t be changed programmatically, at least not without dropping into C.
Because cref can’t be modified from Ruby, it’s difficult to control how constants are defined and managed in DSLs. This is generally why many DSLs eschew constants or provide alternatives.
And indeed, there are several other elements to the Ruby “context”, controlling such functionality as what
super calls, how iteration keywords like
break behave, and so forth. If you’re interested in exploring this further, the old Ruby hacking guide has a chapter dedicated to the context, but it’s from the Ruby 1.7 era and might be out of date. Pat Shaughnessy’s excellent book Ruby Under a Microsocope is somewhat newer and also covers some of these topics. (If anyone knows of other resources, leave a note in the comments!)