Ruby instance_eval and friends

The instance_eval and class_eval methods have always been slightly obscure (at least to me). Let’s try understanding better what they are and what is their purpose.

instance_eval

Let’s see what the documentation says:

Evaluates a string containing Ruby source code, or the given block, within the context of the receiver (obj). In order to set the context, the variable self is set to obj while the code is executing, giving the code access to obj‘s instance variables.

class Dog
  def initialize
    @name = 'Brutus'
  end
end

d = Dog.new

d.instance_eval do
  puts @name # => Brutus
end

A common scenario to use instance_eval is when for some reason we’re calling many methods on the same object:

x = "pippo"
x.upcase!
x.reverse!

This can be rewritten this way:

x.instance_eval do
  upcase!
  reverse!
end

The example is silly but you get the point. For the same reason we can use it to call private methods (that, you might remember, can be called only through an implicit receiver):

class Foo
  
  private
  
  def bar
    puts "Hi"
  end
end

x = Foo.new
x.instance_eval do
  bar
end

Method creation

You’re free to create some methods:

class Dog
end

d = Dog.new

d.instance_eval do
  def bark
    "Bau"
  end
end

puts d.bark

This is equivalent to

def d.bark
	"Bau"
end

The first interesting use of this feature is when we want to add class methods to a class. Remember that in Ruby a class name is a constant assigned to an instance of the Class object.

A = Class.new

def A.my_method
  puts "Hi!"
end

A.my_method # => 'Hi!'

A.instance_eval do
  def other_method
    puts "Hey!"
  end
end

A.other_method # => 'Hey!'

Therefore adding a class method consists in adding an instance method to an object that happens to be an instance of the Class class. This is one of the things that has been puzzling me for a while. The name instance_eval makes you think you will be creating instance methods but in this case you’re actually creating class methods. That’s because the receiver is an instance of Class!

class_eval

This is what the documentation says:

mod.class_eval(string [, filename [, lineno]]) Evaluates the string or block in the context of mod. This can be used to add methods to a class. module_eval returns the result of evaluating its argument. The optional filename and lineno parameters set the text for error messages.

The module_eval method is an alias. These methods, unlike instance_eval, can only be called on classes and modules. As instance_eval it changes self, but it changes the current class as well. Basicly what it does is adding instance methods to an existing class:

class A
end

A.class_eval do
  def my_method
    puts "Hi!"
  end
end

A.new.my_method

These two things are equivalent:

class A
  def my_method
    puts "Hi!"
  end
end

A.class_eval do
  def my_method
    puts "Hi!"
  end
end

You would expect, given its name, to use this method to add class methods. It works the opposite way:

But if you do something like this:

class A
end

A.class_eval do
  def self.greet
    puts "Hi!"
  end
end

A.greet # => Hi!

You’ve just added a class method!

Sum up

There are many ways to define or add a method to a class or to an instance. Let’s try to list them all:

class method

In the class definition

class Dog
  def self.greet
    "Woof!"
  end
end

Outside the class definition

class Dog
end

def Dog.greet
  "Woof!"
end

Opening the singleton internally

class Dog
  class << self
    def greet
      "Woof!"
    end
  end
end

Opening the singleton externally

class Dog
end

class << Dog
  def greet
    "Woof!"
  end
end

Through class_eval on the class name and self receiver

class Dog
end

Dog.class_eval do
  def self.greet
    "Woof!"
  end
end

Through instance_eval on the class name

class Dog
end

Dog.instance_eval do
  def greet
    "Woof!"
  end
end

instance method

Basic syntax

class Dog
  def greet
    "Woof!"
  end  
end

With class_eval

class Dog
end

Dog.class_eval do
  def greet
    "Woof!"
  end
end

With define_method

class Dog
  define_method :greet do
    "Woof!"
  end  
end

instance method on a single instance

Basic syntax

class Dog
end

doug = Dog.new

def doug.greet
  "Woof!"
end

With instance_eval

class Dog
end

doug = Dog.new

doug.instance_eval do
  def greet
    "Woof!"
  end
end

puts doug.greet

Reopening the singleton class:

class Dog
end

doug = Dog.new

class << doug
  def greet
    "Woof!"
  end
end

Another comparison between the two methods

instance_eval

class_eval

Sources