Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to understand the difference between class_eval() and instance_eval()?

Foo = Class.new Foo.class_eval do   def class_bar     "class_bar"   end end Foo.instance_eval do   def instance_bar     "instance_bar"   end end Foo.class_bar       #=> undefined method ‘class_bar’ for Foo:Class Foo.new.class_bar   #=> "class_bar" Foo.instance_bar       #=> "instance_bar" Foo.new.instance_bar   #=> undefined method ‘instance_bar’ for #<Foo:0x7dce8> 

Just based on the name of the methods, I would expect class_eval to allow you to add a class method to Foo and instance_eval to allow you to add an instance method to Foo. But they seem to do the opposite.

In the example above if you call class_bar on the Foo class you get an undefined method error and if you call instance_bar on the instance returned by Foo.new you also get an undefined method error. Both errors seem to contradict an intuitive understanding of what class_eval and instance_eval should do.

What is really the difference between these methods?

Documentation for class_eval:

mod.class_eval(string [, filename [, lineno]]) => obj

Evaluates the string or block in the context of mod. This can be used to add methods to a class.

Documentation for instance_eval:

obj.instance_eval {| | block } => obj

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.

like image 562
pez_dispenser Avatar asked May 22 '09 23:05

pez_dispenser


People also ask

What is instance_eval?

The instance_eval method defines a method for one object only, whereas the class_eval method defines it for ALL objects or instances of a class.

What does class_ eval do Ruby?

class_eval(array_second) adds the method second to any instance of Array by passing a String that will be evaluated in the context of the class Array . The call to String. class_eval with a block will evaluate the content of the block in the context of the class String .

What is class_eval?

class_eval is a method of the Module class, meaning that the receiver will be a module or a class. The block you pass to class_eval is evaluated in the context of that class. Defining a method with the standard def keyword within a class defines an instance method, and that's exactly what happens here.


1 Answers

As the documentation says, class_eval evaluates the string or block in the context of the Module or Class. So the following pieces of code are equivalent:

class String   def lowercase     self.downcase   end end  String.class_eval do   def lowercase     self.downcase   end end 

In each case, the String class has been reopened and a new method defined. That method is available across all instances of the class, so:

"This Is Confusing".lowercase  => "this is confusing" "The Smiths on Charlie's Bus".lowercase => "the smiths on charlie's bus" 

class_eval has a number of advantages over simply reopening the class. Firstly, you can easily call it on a variable, and it's clear what your intent is. Another advantage is that it will fail if the class doesn't exist. So the example below will fail as Array is spelt incorrectly. If the class was simply reopened, it would succeed (and a new incorrect Aray class would be defined):

Aray.class_eval do   include MyAmazingArrayExtensions end 

Finally class_eval can take a string, which can be useful if you're doing something a little more nefarious...

instance_eval on the other hand evaluates code against a single object instance:

confusing = "This Is Confusing" confusing.instance_eval do   def lowercase     self.downcase   end end     confusing.lowercase => "this is confusing" "The Smiths on Charlie's Bus".lowercase NoMethodError: undefined method ‘lowercase’ for "The Smiths on Charlie's Bus":String 

So with instance_eval, the method is only defined for that single instance of a string.

So why does instance_eval on a Class define class methods?

Just as "This Is Confusing" and "The Smiths on Charlie's Bus" are both String instances, Array, String, Hash and all other classes are themselves instances of Class. You can check this by calling #class on them:

"This Is Confusing".class => String  String.class => Class 

So when we call instance_eval it does the same on a class as it would on any other object. If we use instance_eval to define a method on a class, it will define a method for just that instance of class, not all classes. We might call that method a class method, but it is just an instance method for that particular class.

like image 98
tomafro Avatar answered Sep 20 '22 23:09

tomafro