2010-07-22 5 views
0

Pourquoi une erreur est survenue dans l'exemple suivant?Pourquoi j'obtiens une erreur en essayant de référencer une classe imbriquée dans Ruby?

class ClassA 
    class ClassB 
    end 
    class ClassC 
    def test 
     ClassB.new 
    end 
    end 
end 

p ClassA::ClassC.new.test # => #<ClassA::ClassB:0x0000010103f860> 

class ClassA 
    class ClassD 
    def test 
     ClassB.new 
    end 
    end 
end 

p ClassA::ClassD.new.test # => #<ClassA::ClassB:0x0000010103f010> 

class ClassA::ClassE 
    def test 
    ClassB.new 
    end 
end 

p ClassA::ClassE.new.test # => NameError: uninitialized constant ClassA::ClassE::ClassB 

Y at-il une autre façon de créer ClassE, pas en tapant class ClassA; class ClassE?

Répondre

1

Eh bien, oui, si vous définissez votre méthode de test pour revenir ClassA::ClassB.new :-)

Vous pouvez aussi jouer avec const_missing afin qu'il appelle ClassA.const_get.

Sinon, ClassB n'est pas dans la portée actuelle, qui à ce stade est seulement ClassA::ClassE et Object. Lorsque vous ouvrez ClassA pour la première fois, puis ClassE, la recherche pour ClassB est effectuée d'abord dans ClassA::ClassE, puis dans ClassA (où il se trouve) et également dans Object.

+0

Existe-t-il un moyen d'obtenir le chemin de l'espace de noms? Quelque chose comme '[: ClassA,: ClassE]'? – Andrei

+0

Bonne question. Je ne pense pas que la langue nous donne accès à cela. –

+0

Il existe une solution de contournement - regardez ma réponse ci-dessous. – Andrei

0

La méthode personnalisée Object#const_missing, suggérée par Marc-André Lafortune, serait alors

def Object.const_missing(name) 
    @looked_for ||= {} 
    key = self.to_s + '~' + name.to_s 
    raise "Class not found: #{name}" if @looked_for[key] == key 
    return @looked_for[key] if @looked_for[key] 
    @looked_for[key] = key 
    if self.to_s.include? '::' 
    klass = Object 
    self.to_s.split('::')[0..-2].each do |klass_string| 
     klass = klass.const_get klass_string 
    end 
    return @looked_for[key] = klass.const_get(name) if klass # klass.is_a?(Class) 
    end 
    raise "Class not found: #{name}" 
end 

Quelques questions connexes:

  1. Does Ruby provide the namespace path, e.g. something like [:A,:B] for class A::B::C?
  2. If I define a class-method in Ruby Object class, how do I get the name of a child class calling this method?
  3. How do I get class-object from string “A::B::C” in Ruby?
Questions connexes