2010-07-02 10 views
20

Secondo la documentazione mod.const_get(sym) "Restituisce il valore della costante nominata in mod."Comportamento confuso di const_get in Ruby?

So anche che const_get per impostazione predefinita può cercare la catena di ereditarietà del destinatario. Così le seguenti opere:

class A; HELLO = :hello; end 
class B < A; end 
B.const_get(:HELLO) #=> :hello 

so anche che le classi in Ruby sottoclasse Object, in modo da poter utilizzare const_get per cercare le costanti 'globali', anche se il ricevitore è una classe normale:

class C; end 
C.const_get(:Array) #=> Array 

Tuttavia, e questo è dove sono confuso - i moduli non sottoclasse Object. Quindi, perché posso ancora cercare costanti "globali" da un modulo usando const_get? Perché funziona il seguente?

module M; end 
M.const_get(:Array) #=> Array 

Se la documentazione è corretta - const_get semplicemente cerca il costante definita sotto il ricevitore o le sue superclassi. Ma nel codice immediatamente sopra, Object non è una superclasse di M, quindi perché è possibile cercare Array?

Grazie

+3

Si noti che questo non corrisponde al comportamento di '::'. 'SomeModule :: SomeGlobalConstant' causerà un errore, mentre' SomeModule.const_get (: SomeGlobalConstant) 'funzionerà. – sepp2k

risposta

11

Si sono corretti devono essere confusi ... Il dottore non ha precisato che Ruby fa un caso speciale per la ricerca di costanti in Modules ed è stato modificato to state this explicitly. Se la costante non è stata trovata nella normale gerarchia, Ruby riavvia la ricerca da Object, come può essere found in the source.

La ricerca costante di per sé può essere un po 'confusa. Prendiamo il seguente esempio:

module M 
    Foo = :bar 
    module N 
    # Accessing Foo here is fine: 
    p Foo # => bar 
    end 
end 
module M::N 
    # Accessing Foo here isn't 
    p Foo # => uninitialized constant M::N::Foo 
end 
p M::N.const_get :Foo # => uninitialized constant M::N::Foo 

In entrambi i luoghi, però, l'accesso Object costanti di primo livello come Array va bene (grazie a Dio!). Quello che sta succedendo è che Ruby mantiene un elenco di "definizioni di moduli aperte". Se una costante ha un ambito esplicito, ad esempio LookHereOnly::Foo, quindi soloLookHereOnly ei relativi moduli inclusi verranno cercati. Se non viene specificato alcun ambito (ad esempio Foo nell'esempio precedente), Ruby esaminerà le definizioni di modulo aperte per trovare la costante Foo: M::N, quindi M e infine Object. La definizione di modulo aperta più in alto è sempre Object.

Così M::N.const_get :Foo equivale ad accedere Foo quando le classi aperte sono solo M::N e Object, come nell'ultima parte del mio esempio.

Spero ho ottenuto questo diritto, coz Sono ancora confuso da ricerche costanti me :-)

+1

Qualche idea sul perché lo fa? In quali casi sarebbe utile? – sepp2k

+0

@ sepp2k: non so se è _utile_, ma ho provato a spiegare quello che penso sia la logica dietro di esso. –

+0

Non è intuitivo, ma il problema M :: N è dovuto all'ambito lessicale. Se fai 'modulo M; modulo N; fine; end', il contenuto di M è nello scope lessicale di N (M è visibile da N a causa del nidificazione). Con 'modulo M :: N; end', M non è visibile perché l'ambito di contenimento è il livello principale. Personalmente cerco di evitare di definire classi e moduli nel modulo M :: N - anche perché è un errore se M non esiste ancora. – Kelvin

2

mi si avvicinò con il seguente script per caricare nome costanti distanziati:

def load_constant(name) 
    parts = name.split('::') 
    klass = Module.const_get(parts.shift) 
    klass = klass.const_get(parts.shift) until parts.empty? 
    klass 
end 
0

Finché poiché non controlliamo gli errori, puoi:

def load_constant(name) 
    name.split('::').inject(Module) do |mod_path, mod_to_find| 
     mod_path.const_get(mod_to_find) 
    end 
end