views:

60

answers:

2

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?

+2  A: 

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

Adrian
+1  A: 

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"
yjerem