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