Есть ли ловушка, когда анонимные классы присваиваются константе? - PullRequest
7 голосов
/ 03 января 2012

Недавно я практиковал некоторое метапрограммирование на Ruby, и мне было интересно узнать о назначении анонимных классов константам .

В Ruby можно создать анонимный класс следующим образом:

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

Могут быть созданы новые экземпляры этого класса:

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

Теперь, когда анонимный класс назначен константе, класс теперь имеет правильное имя:

Foo = anonymous_class # => Foo

И ранее созданный экземпляр теперь также является экземпляром этого класса:

an_instance # => #<Foo:0x007f9c5afb0330>

Мой вопрос : есть ли метод ловушки для момента, когда анонимныйкласс присвоен константе?

В Ruby существует множество методов перехвата , но я не смог найти этот.

Ответы [ 2 ]

6 голосов
/ 03 января 2012

Давайте посмотрим, как внутреннее постоянное назначение работает. Следующий код извлекается из исходного архива ruby-1.9.3-p0. Сначала мы рассмотрим определение инструкции 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_namespace или INC_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. Это означает, что, если я что-то пропустил, это жесткая правда, что нет способа перехватить постоянное назначение (по крайней мере, в МРТ).

Обновление

Для пояснения: анонимный класс не волшебным образом получает новое имя, как только оно назначено (как правильно отмечено в ответе Эндрю). Скорее, имя константы вместе с идентификатором объекта класса хранится во внутренней таблице поиска констант Ruby. Если после этого запрашивается имя класса, теперь его можно преобразовать в правильное имя (а не просто Class:0xXXXXXXXX...).

Таким образом, лучшее, что вы можете сделать, чтобы отреагировать на это назначение, это проверить name класса в цикле фонового рабочего потока, пока он не будет равен nil (что является огромной тратой ресурсов, IMHO ).

0 голосов
/ 03 января 2012

Анонимные классы не получают свое имя, когда им присваивается константа. Они действительно получают это, когда их спрашивают, как их зовут.

Я постараюсь найти ссылку для этого. Редактировать: Не могу найти, извините.

...