The scope is changed for the proc, which is then evaluated in that context. Internally, all procs are stored in memory as a C struct
which includes the self
of the proc (the scope in which the proc was made). When you call instance_eval
, the self
value is manually changed in memory to the object you are calling instance_eval
on. If you explore the ruby source code, you will find that it boils down to this function:
static VALUE
yield_under(VALUE under, VALUE self, VALUE values)
{
rb_thread_t *th = GET_THREAD();
rb_block_t block, *blockptr;
NODE *cref;
if ((blockptr = GC_GUARDED_PTR_REF(th->cfp->lfp[0])) != 0) {
block = *blockptr;
block.self = self; // <- This is where the scope changes!
th->cfp->lfp[0] = GC_GUARDED_PTR(&block);
}
cref = vm_cref_push(th, under, NOEX_PUBLIC, blockptr);
cref->flags |= NODE_FL_CREF_PUSHED_BY_EVAL;
if (values == Qundef) {
return vm_yield_with_cref(th, 1, &self, cref);
}
else {
return vm_yield_with_cref(th, RARRAY_LENINT(values), RARRAY_PTR(values), cref);
}
}
Note the line containing // <- This is where the scope changes!
.