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
endA 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!
endThe 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
endMethod 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.barkThis is equivalent to
def d.bark
"Bau"
endThe 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_methodThese two things are equivalent:
class A
def my_method
puts "Hi!"
end
end
A.class_eval do
def my_method
puts "Hi!"
end
endYou would expect, given its name, to use this method to add class methods. It works the opposite way:
- with
instance_evalon a class you add class methods to that class - with
class_evalon 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
endOutside the class definition
class Dog
end
def Dog.greet
"Woof!"
endOpening the singleton internally
class Dog
class << self
def greet
"Woof!"
end
end
endOpening the singleton externally
class Dog
end
class << Dog
def greet
"Woof!"
end
endThrough class_eval on the class name and self receiver
class Dog
end
Dog.class_eval do
def self.greet
"Woof!"
end
endThrough instance_eval on the class name
class Dog
end
Dog.instance_eval do
def greet
"Woof!"
end
endinstance method
Basic syntax
class Dog
def greet
"Woof!"
end
endWith class_eval
class Dog
end
Dog.class_eval do
def greet
"Woof!"
end
endWith define_method
class Dog
define_method :greet do
"Woof!"
end
endinstance method on a single instance
Basic syntax
class Dog
end
doug = Dog.new
def doug.greet
"Woof!"
endWith instance_eval
class Dog
end
doug = Dog.new
doug.instance_eval do
def greet
"Woof!"
end
end
puts doug.greetReopening the singleton class:
class Dog
end
doug = Dog.new
class << doug
def greet
"Woof!"
end
endAnother 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/