Ruby:如何通过instance_eval运行这个块?

时间:2010-08-02 02:27:21

标签: ruby-on-rails ruby

我最近看到一些我不完全理解的代码。有一个名为foo的数组,其中包含Proc个对象的实例。然后,env对象用于设置要使用的环境:

env = Object.new
foo.each do |f|
    env.instance_eval &f # what happens here?
end

使用instance_eval打开对象并将&f作为参数传递时会发生什么? env在这一点和Proc本身会发生什么?

2 个答案:

答案 0 :(得分:2)

为proc更改范围,然后在该上下文中对其进行评估。在内部,所有的proc都作为C struct存储在内存中,其中包括proc的self(proc的制作范围)。当您致电instance_eval时,会在内存中手动将self值更改为您正在呼叫的对象instance_eval。如果你探索ruby源代码,你会发现它归结为这个函数:

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);
    }
}

请注意包含// <- This is where the scope changes!

的行

答案 1 :(得分:1)

Proc在env的上下文中执行。就像你在env上调用一个方法一样:该块可以访问它的实例变量以及公共和私有方法。

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"