2012-01-02 6 views
7

Recentemente ho praticato qualche meta-programmazione in Ruby e mi sono interrogato su assigning anonymous classes to constants.C'è un gancio per quando le classi anonime sono assegnate a una costante?

In Ruby, è possibile creare una classe anonima come segue:

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

Nuove istanze di questa classe possono essere create:

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

Ora, quando la classe anonima è assegnato a una costante, la classe ora ha un nome proprio:

Foo = anonymous_class # => Foo 

E l'istanza creata in precedenza è ora anche un'istanza di quella classe:

an_instance # => #<Foo:0x007f9c5afb0330> 

La mia domanda: Esiste un metodo gancio per il momento in cui una classe anonima viene assegnato a una costante?

Ci sono molti hooks methods in Ruby, ma non ho trovato questo.

+0

Domanda molto interessante. –

+1

AFAIK non esiste ancora alcun hook per le assegnazioni di variabili, costante o altro ([vedere qui per la stessa domanda molto tempo fa] (http://www.ruby-forum.com/topic/65720)). Globale, si. –

risposta

6

Diamo un'occhiata a come l'incarico costante funziona internamente. Il codice che segue è estratto da un tarball sorgente di ruby-1.9.3-p0. In primo luogo guardiamo la definizione delle istruzioni VM setconstant (che viene utilizzato per assegnare le costanti):

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

Nessuna possibilità di inserire un gancio in vm_check_if_namespace o INC_VM_STATE_VERSION qui. Quindi guardiamo rb_const_set (variable.c 1886), la funzione che viene chiamata ogni volta che una costante viene assegnata:

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

ho rimosso tutto il codice che non è stato anche chiamato la prima volta è stato assegnato un costante all'interno di un modulo. Ho quindi esaminato tutte le funzioni chiamate da questo e non ho trovato un singolo punto in cui potremmo inserire un gancio dal codice Ruby. Ciò significa che la dura verità è, a meno che non mi sia sfuggito qualcosa, che ci sia il modo no per agganciare un incarico costante (almeno in MRI).

Aggiornamento

per chiarire: La classe anonima non non magicamente ottenere un nuovo nome, non appena viene assegnato (come indicato correttamente nella risposta di Andrea). Piuttosto, il nome costante insieme all'ID oggetto della classe è memorizzato nella tabella di ricerca costante interna di Ruby. Se, successivamente, viene richiesto il nome della classe, ora può essere risolto con un nome appropriato (non solo Class:0xXXXXXXXX...).

Quindi il meglio che puoi fare per rispondere a questo compito è controllare lo name della classe in un ciclo di un thread di lavoro in background fino a quando non è nil (che è un enorme spreco di risorse, IMHO).

+0

Grazie per la risposta molto ampia. Per completarlo, come hai trovato questo riferimento a 'setconstant'? La documentazione nel sorgente ('insns.def') non mi è molto chiara:" imposta l'id della variabile costante, se klass è Qfalse, la costante è in grado di accedere in questo ambito, se klass è Qnil, imposta la costante di primo livello. altrimenti, imposta costante sotto la classe klass o il modulo. " Questo codice è effettivamente utilizzato per assegnare costanti a istanze di classi (anonime)? – rdvdijk

+0

@rdvdijk: ho indovinato e verificato con 'printf'. –

+0

Ho esaminato il codice sorgente a cui fai riferimento qui e capisco il significato del codice che hai incollato. Tuttavia, non risponde completamente alla mia domanda. Questo codice mostra la gestione dell'assegnazione delle costanti e che questo codice non ha alcun hook disponibile. Tuttavia, questo non spiega quando e dove la classe anonima scopre il suo nuovo nome. Seguendo la risposta di Andrew Grimm, sembra che il metodo 'classname' e il codice correlato osservino la costante in' RCLASS_CONST_TBL' o 'rb_class_tbl', giusto? – rdvdijk

0

Le classi anonime in realtà non ottengono il proprio nome quando sono assegnate a una costante. In realtà lo capiscono quando vengono poi chiesti quale sia il loro nome.

Cercherò di trovare un riferimento per questo. Modifica: Impossibile trovarne uno, mi dispiace.

+0

La chiave è la funzione 'rb_class_name' definita in' variable.c: 305' (ruby-1.9.3-p0) e le funzioni chiamate da esso (in particolare 'find_class_path', che usa le mappe' RCLASS_CONST_TBL' per verificare se il la classe è legata ad un nome, almeno se la mia interpretazione è corretta). –

Problemi correlati