Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

class_eval vs instance_eval

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.

like image 812
Alexey Avatar asked Apr 24 '12 16:04

Alexey


People also ask

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.

What is Instance_eval?

instance_eval(array_second) adds the method second to the variable a — which is an instance of Array — by passing a String that will be evaluated in the context of the instance a . The call to str. instance_eval with a block will evaluate the content of the block in the context of str — which is an instance of String .

What is class_eval 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 .


1 Answers

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:

  1. block.self = self;

    This sets the self of the block to the receiver.

  2. 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.

like image 86
Matheus Moreira Avatar answered Oct 05 '22 09:10

Matheus Moreira