class_eval vs instance_eval
class-eval, instance-eval, metaprogramming, ruby
Solution
Long story short:
- `Object.instance_eval &block` sets:
- `self` to `Object`
- The "current class" to `Object.singleton_class`
- `Object.class_eval &block` sets:
- `self` to `Object`
- The "current class" to `Object`
The "current class" is used for `def`, `undef` and `alias`, as well as constant and class variable lookups.
Now, let's have a look at the implementation details.
Here's how `module_eval` and `instance_eval` are implemented in C:
VALUE rb_mod_module_eval(int argc, VALUE *argv, VALUE mod) {
return specific_eval(argc, argv, mod, mod);
}
VALUE rb_obj_instance_eval(int argc, VALUE *argv, VALUE self) {
VALUE klass;
if (SPECIAL_CONST_P(self)) { klass = Qnil; }
else { klass = rb_singleton_class(self); }
return specific_eval(argc, argv, klass, self);
}
Both call `specific_eval`, which takes the following arguments: `int argc`, `VALUE *argv`, `VALUE klass` and `VALUE self`.
Note that:
- `module_eval` passes the `Module` or `Class` instance as both `klass` and `self`
- `instance_eval` passes the object's singleton class as `klass`
If given a block, `specific_eval` will call `yield_under`, which takes the following arguments: `VALUE under`, `VALUE self` and `VALUE values`.
if (rb_block_given_p()) {
rb_check_arity(argc, 0, 0);
return yield_under(klass, self, Qundef);
}
There are two important lines in `yield_under`:
`block.self = self;`
This sets the `self` of the block to the receiver.
`cref = vm_cref_push(th, under, NOEX_PUBLIC, blockptr);`
The `cref` is a linked list which specifies the "current class", which is used for `def`, `undef` and `alias`, as well as constant and class variable lookups.
That line basically sets the `cref` to `under`.
Finally:
When called from `module_eval`, `under` will be the `Class` or `Module` instance.
When called from `instance_eval`, `under` will be the singleton class of `self`.
Problem
Is there any difference in how `class_eval` & `instance_eval` work except `def`? Inside `class_eval` block `def` defines method to class itself (i.e. instance method) and inside `instance_eval` `def` defines method to the eigenclass of the class (i.e. class method). AFAIK all other features work identically in both cases (e.g. `define_method`, `attr_accessor`, `class << self; end`, defining constants). Is it true? Answer is: `def`, `undef` and `alias` have different contexts for `class_eval` and `instance_eval`.