2012-01-02 21 views
7

He estado practicando algunos meta-programación de Ruby recientemente, y me preguntaba acerca de assigning anonymous classes to constants.¿Hay un gancho para cuando se asignan clases anónimas a una constante?

En Rubí, es posible crear una clase anónima de la siguiente manera:

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

nuevas instancias de esta clase se pueden crear:

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

Ahora, cuando la clase anónima se asigna a una constante, la clase ahora tiene un nombre propio:

Foo = anonymous_class # => Foo 

Y la instancia creada anteriormente ahora es también una instancia de esa clase:

an_instance # => #<Foo:0x007f9c5afb0330> 

Mi pregunta: ¿Existe un método gancho para el momento en que se le asigna una clase anónima a una constante?

Hay muchos hooks methods en Ruby, pero no pude encontrar este.

+0

Pregunta muy interesante. –

+1

AFAIK todavía no hay enganche para asignaciones variables, constantes o de otra manera ([ver aquí la misma pregunta hace mucho tiempo] (http://www.ruby-forum.com/topic/65720)). Globales, sí. –

Respuesta

6

Echemos un vistazo a cómo funciona la asignación constante internamente. El código que sigue se extrae de una fuente tarball de ruby-1.9.3-p0. En primer lugar nos fijamos en la definición de la instrucción VM setconstant (que se utiliza para asignar constantes):

# /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(); 
} 

No hay posibilidad de colocar un gancho en vm_check_if_namespace o INC_VM_STATE_VERSION aquí. Así que nos fijamos en rb_const_set (variable.c: 1886), la función que se llama cada vez que se asigna una constante:

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

me quita todo el código que ni siquiera se llama la primera vez que se asigna una constante dentro de un modulo Luego busqué en todas las funciones llamadas por este y no encontré un solo punto donde podríamos colocar un gancho del código de Ruby. Esto significa que la verdad es que, a menos que me haya perdido algo, hay no forma de enganchar una asignación constante (al menos en MRI).

actualización

Para aclarar: La clase anónima hace no mágicamente obtener un nuevo nombre tan pronto como se asigna (como se indica correctamente en la respuesta de Andrew). Por el contrario, el nombre de la constante junto con el ID del objeto de la clase se almacena en la tabla de búsqueda constante interna de Ruby. Si, después de eso, se solicita el nombre de la clase, ahora se puede resolver con un nombre propio (y no solo con Class:0xXXXXXXXX...).

Así que lo mejor que puede hacer para reaccionar a esta asignación es comprobar la name de la clase en un bucle de un subproceso de trabajo de fondo hasta que no es nil (que es un enorme desperdicio de recursos, en mi humilde opinión).

+0

Gracias por la respuesta muy extensa. Para completarlo, ¿cómo encontraste esta referencia a 'setconstant'? La documentación en la fuente ('insns.def') no es muy clara para mí:" establece la variable constante id. Si klass es Qfalse, la constante puede acceder en este ámbito. Si klass es Qnil, establezca el nivel máximo constante. de lo contrario, establezca constante bajo klass class o module ". ¿Este código se usa para asignar constantes a instancias de clase (anónimas)? – rdvdijk

+0

@rdvdijk: Adiviné y lo verifiqué con 'printf'. –

+0

He examinado el código fuente al que hace referencia aquí y entiendo el significado del código que ha pegado. Sin embargo, no responde completamente mi pregunta. Este código muestra el manejo de la asignación de constantes, y que este código no tiene gancho disponible. Sin embargo, esto no explica cuándo y dónde la clase anónima descubre su nuevo nombre. Siguiendo la respuesta de Andrew Grimm, parece que el método 'classname' y el código relacionado buscan la constante en' RCLASS_CONST_TBL' o 'rb_class_tbl', ¿verdad? – rdvdijk

0

Las clases anónimas en realidad no reciben su nombre cuando se asignan a una constante. De hecho lo obtienen cuando se les pregunta cuál es su nombre.

Voy a tratar de encontrar una referencia para esto. Edit: No se puede encontrar uno, lo siento.

+0

La clave es la función 'rb_class_name' definida en' variable.c: 305' (ruby-1.9.3-p0) y las funciones llamadas por ella (especialmente 'find_class_path', que utiliza los mapas' RCLASS_CONST_TBL' para comprobar si el la clase está ligada a un nombre, al menos si mi interpretación es correcta). –

Cuestiones relacionadas