2010-07-02 3 views
20

Selon la documentation mod.const_get(sym) « Renvoie la valeur de la constante nommée en mod. »Comportement déroutant de const_get dans Ruby?

Je sais aussi que const_get par défaut peut rechercher la chaîne d'héritage du récepteur. Ainsi, les travaux suivants:

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

Je sais aussi que les classes dans Ruby sous-classe Object, de sorte que vous pouvez utiliser const_get pour rechercher des constantes « globales », même si le récepteur est une classe normale:

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

Cependant, ce qui est là où je suis confus - modules ne sous-classe ne Object. Alors, pourquoi je peux toujours regarder les constantes « globales » à partir d'un module à l'aide const_get? Pourquoi ce qui suit fonctionne-t-il?

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

Si la documentation est correcte - const_get regarde simplement la constante définie dans le récepteur ou ses superclasses. Mais dans le code immédiatement ci-dessus, Object n'est pas une superclasse de M, alors pourquoi est-il possible de rechercher Array?

Merci

+3

Notez que cela ne correspond pas au comportement de '::'. 'SomeModule :: SomeGlobalConstant' provoquera une erreur, alors que SomeModule.const_get (: SomeGlobalConstant) 'fonctionnera. – sepp2k

Répondre

11

Vous avez raison d'être confus ... Le doc n'a pas précisé que Ruby fait un cas particulier pour la recherche des constantes dans Modules et a été modifié to state this explicitly. Si la constante n'a pas été trouvé dans la hiérarchie normale, Ruby redémarre la recherche de Object, comme cela peut être found in the source.

La recherche constante en elle-même peut être un peu confuse. Prenons l'exemple suivant:

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 

Dans les deux endroits, cependant, l'accès Object constantes de niveau comme Array est très bien (Dieu merci!). Qu'est-ce qui se passe, c'est que Ruby maintient une liste de "définitions de modules ouverts". Si une constante a une portée explicite, par exemple LookHereOnly::Foo, alors seulementLookHereOnly et ses modules inclus seront recherchés. Si aucune portée n'est spécifiée (comme Foo dans l'exemple ci-dessus), Ruby examinera les définitions de modules ouvertes pour trouver la constante Foo: M::N, puis M et enfin Object. La définition de module ouverte la plus haute est toujours Object.

donc M::N.const_get :Foo est équivalent à l'accès Foo lorsque les classes ouvertes ne sont M::N et Object, comme dans la dernière partie de mon exemple.

J'espère que je suis arrivé ce droit, coz je suis toujours confus par moi-même :-) constante lookups

+1

Une idée de pourquoi ça fait ça? Dans quels cas cela serait-il utile? – sepp2k

+0

@ sepp2k: je ne sais pas si c'est _useful_, mais j'ai essayé d'expliquer ce que je pense être la logique derrière. –

+0

Ce n'est pas intuitif, mais le problème M :: N est dû à la portée lexicale. Si vous faites le module M; module N; fin; end', le contenu de M est dans la portée lexicale de N (M est visible de N en raison de l'imbrication). Avec 'module M :: N; end', M n'est pas visible car la portée contenant est le niveau supérieur. Personnellement, j'essaie d'éviter de définir des classes et des modules dans la forme M :: N - aussi parce que c'est une erreur si M n'existe pas encore. – Kelvin

2

je suis venu avec le script suivant pour charger le nom des constantes espacées:

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

Tant comme nous ne vérifions pas les erreurs, vous pouvez:

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