将匿名类分配给常量时是否有钩子?

时间:2012-01-02 20:34:51

标签: ruby class hook metaprogramming constants

我最近一直在练习一些Ruby元编程,并且想知道assigning anonymous classes to constants

在Ruby中,可以按如下方式创建匿名类:

anonymous_class = Class.new  # => #<Class:0x007f9c5afb21d0>

可以创建此类的新实例:

an_instance = anonymous_class.new # => #<#<Class:0x007f9c5afb21d0>:0x007f9c5afb0330>

现在,当将匿名类分配给常量时,该类现在具有正确的名称:

Foo = anonymous_class # => Foo

以前创建的实例现在也是该类的实例:

an_instance # => #<Foo:0x007f9c5afb0330>

我的问题:将匿名类分配给常量时,是否存在钩子方法?

Ruby中有很多hooks methods,但我找不到这个。

2 个答案:

答案 0 :(得分:6)

让我们来看看内部的常量赋值是如何工作的。后面的代码是从ruby-1.9.3-p0的源tarball中提取的。首先,我们看一下VM指令setconstant的定义(用于指定常量):

# /insns.def, line 239
DEFINE_INSN
setconstant
(ID id)
(VALUE val, VALUE cbase)
()
{
  vm_check_if_namespace(cbase);
  rb_const_set(cbase, id, val);
  INC_VM_STATE_VERSION();
}

此处无法在vm_check_if_namespaceINC_VM_STATE_VERSION中放置一个钩子。所以我们看一下rb_const_set variable.c:1886 ),每次分配一个常量时调用的函数:

# /variable.c, line 1886
void
rb_const_set(VALUE klass, ID id, VALUE val)
{
    rb_const_entry_t *ce;
    VALUE visibility = CONST_PUBLIC;

    # ...

    check_before_mod_set(klass, id, val, "constant");
    if (!RCLASS_CONST_TBL(klass)) {
      RCLASS_CONST_TBL(klass) = st_init_numtable();
    }
    else {
      # [snip], won't be called on first assignment
    }

    rb_vm_change_state();

    ce = ALLOC(rb_const_entry_t);
    ce->flag = (rb_const_flag_t)visibility;
    ce->value = val;

    st_insert(RCLASS_CONST_TBL(klass), (st_data_t)id, (st_data_t)ce);
}

我删除了第一次在模块中分配常量时甚至没有调用的所有代码。然后我查看了这个调用的所有函数,并没有找到一个我们可以从Ruby代码中放置一个钩子的点。这意味着,除非我遗漏了某些内容,否则就会有没有方式来挂钩常量任务(至少在MRI中)。

<强>更新

澄清一下:匿名类一旦分配就会神奇地获得一个新名称(如Andrew的回答中所述)。相反,常量名称以及类的对象ID存储在Ruby的内部常量查找表中。如果在此之后,请求了类的名称,现在可以将其解析为正确的名称(而不仅仅是Class:0xXXXXXXXX...)。

因此,您可以做的最好的事情就是在后台工作线程的循环中检查类的name,直到它不是nil(这是一个巨大的浪费)资源,恕我直言。。

答案 1 :(得分:0)

匿名类在分配给常量时实际上并没有得到它们的名字。当他们下次被问到他们的名字是什么时,他们实际上得到了它。

我会尝试为此找到参考。 编辑:找不到一个,抱歉。