2010-07-02 11 views
20

De acuerdo con la documentación mod.const_get(sym) "Devuelve el valor de la constante con nombre en mod."Comportamiento confuso de const_get en Ruby?

También sé que const_get de forma predeterminada puede buscar la cadena de herencia del receptor. Por lo que las siguientes obras:

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

también sé que las clases en Ruby subclase Object, de modo que usted puede utilizar const_get para buscar constantes 'global' a pesar de que el receptor es una clase normal:

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

Sin embargo, y aquí es donde estoy confundido: los módulos no incluyen la subclase Object. Entonces, ¿por qué todavía puedo buscar constantes 'globales' desde un módulo usando const_get? ¿Por qué funciona lo siguiente?

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

Si la documentación es correcta - const_get simplemente busca la constante se define en el receptor o sus superclases. Pero en el código inmediatamente anterior, Object no es una superclase de M, entonces ¿por qué es posible buscar Array?

Gracias

+3

Tenga en cuenta que esto no coincide con el comportamiento de '::'. 'SomeModule :: SomeGlobalConstant' causará un error, mientras que' SomeModule.const_get (: SomeGlobalConstant) 'funcionará. – sepp2k

Respuesta

11

estás en lo correcto debe ser confundido ... El documento no declararon que Ruby hace un caso especial para la búsqueda de constantes en Modules y se ha modificado to state this explicitly. Si la constante no se ha encontrado en la jerarquía normal, Ruby reinicia la búsqueda de Object, como puede ser found in the source.

La búsqueda constante en sí misma puede ser un poco confusa. Tomemos el siguiente ejemplo:

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 

En ambos lugares, sin embargo, el acceso a Object constantes de nivel como Array está muy bien (gracias a Dios!). Lo que sucede es que Ruby mantiene una lista de "definiciones abiertas de Módulo". Si una constante tiene un alcance explícito, digamos LookHereOnly::Foo, entonces soloLookHereOnly y se buscarán sus módulos incluidos. Si no se especifica ningún ámbito (como Foo en el ejemplo anterior), Ruby buscará en las definiciones de módulo abierto la constante Foo: M::N, luego M y finalmente Object. La definición de módulo abierto más alta siempre es Object.

Así M::N.const_get :Foo es equivalente a acceder a Foo cuando las clases abiertas sólo son M::N y Object, al igual que en la última parte de mi ejemplo.

espero que me dieron ese derecho, coz Todavía estoy confundido por las búsquedas constantes a mí mismo :-)

+1

¿Alguna idea de por qué hace eso? ¿En qué casos sería útil? – sepp2k

+0

@ sepp2k: no estoy seguro si es _useful_, pero traté de explicar lo que creo que es la lógica detrás de esto. –

+0

No es intuitivo, pero el problema M :: N se debe al alcance léxico. Si haces 'módulo M; módulo N; fin; end', los contenidos de M están en el alcance léxico de N (M es visible desde N debido a la anidación). Con 'módulo M :: N; end', M no es visible porque el alcance que lo contiene es el nivel superior. Personalmente trato de evitar definir clases y módulos en el formulario M :: N, también porque es un error si M aún no existe. – Kelvin

2

me ocurrió con la siguiente secuencia de comandos para cargar nombres de constantes separadas:

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

Mientras como no estamos buscando errores, puede:

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