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

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


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  endendString.class_eval do  def lowercase    self.downcase  endend

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 MyAmazingArrayExtensionsend

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  endend   confusing.lowercase=> "this is confusing""The Smiths on Charlie's Bus".lowercaseNoMethodError: 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=> StringString.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.


The other answer is correct, but allow me to go in depth a little.

Ruby has a number of different kinds of scope; six according to wikipedia, though detailed formal documentation seems to be lacking. The kinds of scope involved in this question are, not surprisingly, instance and class.

The current instance scope is defined by the value of self. All unqualified method calls are dispatched to the current instance, as are any references to instance variables (which look like @this).

However, def is not a method call. The target for methods created by def is the current class (or module), which can be found with Module.nesting[0].

Let's see how the two different eval flavors affect these scopes:

String.class_eval { [self, Module.nesting[0]] } => [String, String]String.instance_eval { [self, Module.nesting[0]] } => [String, #<Class:String>]

In both cases, the instance scope is the object on which *_eval is called.

For class_eval, the class scope also becomes the target object, so def creates instance methods for that class/module.

For instance_eval, the class scope becomes the singleton class (aka metaclass, eigenclass) of the target object. Instance methods created on the singleton class for an object become singleton methods for that object. Singleton methods for a class or module are what are commonly (and somewhat inaccurately) called class methods.

The class scope is also used to resolve constants. Class variables (@@these @@things) are resolved with class scope, but they skip over singleton classes when searching the module nesting chain. The only way I have found to access class variables in singleton classes is with class_variable_get/set.


I think you got it wrong. class_eval adds the method in the class, so all instances will have the method. instance_eval will add the method just to one specific object.

foo = Foo.newfoo.instance_eval do  def instance_bar    "instance_bar"  endendfoo.instance_bar      #=> "instance_bar"baz = Foo.newbaz.instance_bar      #=> undefined method