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:
- with
instance_eval
on a class you add class methods to that class - with
class_eval
on a class you’re adding instance methods
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
- can be called on any object
- method definitions will be added to the receiver singleton class, thus if it is an instance it will be an instance method, if it is a class it will be a class method: current class is set to the singleton
class_eval
- can be called only on classes and modules
- method definitions will be added to the object itself, not to its singleton: current class is set to the class/module itself
Sources
- http://railstalk.com/2010/1/8/instance_eval-vs-class_eval-in-ruby
- http://stackoverflow.com/questions/900419/how-to-understand-the-difference-between-class-eval-and-instance-eval
- http://briancarper.net/blog/196/