Ruby: how does running this block through instance_eval work?
I have recently seen some code that I do not completely understand. There is an array named foo
that contains instances of Proc
objects. Then, an env object is开发者_高级运维 used to setup an environment to work in:
env = Object.new
foo.each do |f|
env.instance_eval &f # what happens here?
end
What exactly happens when you open the object with instance_eval and pass &f
as an argument? What happens to env at this point and the Proc itself?
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!
.
The Proc gets executed in the context of env
. It is as if you are calling a method on env
: the block has access to its instance variables and public and private methods.
env = Object.new
env.instance_variable_set :@test, "test"
class << env
private
def test
@test
end
end
env.instance_eval { @test } #=> "test"
env.instance_eval { test } #=> "test"
精彩评论