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"
加载中,请稍侯......
精彩评论