web-dev-qa-db-fra.com

avertissement: constante de niveau supérieur référencée

J'ai quatre modèles (Document, Question, Question::Document et Answer). Dans mon modèle Answer j'ai

validates :text,
  presence: { :unless => Proc.new{ |a| a.question.is_a? Question::Document } }

Cela me donne l'avertissement

warning: toplevel constant Document referenced by Question::Document

Comment empêcher cet avertissement de se produire (sans renommer mes classes)?

40
Kyle Decot

Votre structure de dossier/fichier devrait ressembler à ceci:

app/
  models/
    question/
      document.rb
    answer.rb
    document.rb
    question.rb

Et ensuite, Rails trouvera automatiquement les modèles corrects (le nom du modèle sera traduit en un nom de fichier et les espaces de noms seront traduits en dossiers).

Assurez-vous qu'à l'intérieur de votre question/document.rb, la définition de la classe ressemble à l'une des alternatives suivantes:

class Question::Document
end

ou

class Question
  class Document
  end
end

Si vous n’écrivez que class Document, vous redéfinissez la constante de niveau le plus élevé Document.

Notez que si la variable globale Document est définie en premier, cela déclenchera également cette erreur. Cela dépend du chemin du code. Le meilleur moyen de résoudre ce problème consiste donc à ajouter un require_dependency si nécessaire. Voir ici et ici pour plus d'informations.

Par exemple. quelque chose comme 

require_dependency 'question/document' 

class Answer < ActiveRecord::Base

end  

Si vous placez le fichier à un endroit différent, où Rails ne peut pas le trouver automatiquement, vous devrez le demander explicitement, afin que Rails sache que Question::Document existe.

Si, par exemple, vous définissez Question::Document dans le modèle Question, ce qui est un endroit raisonnable, vous devrez indiquer explicitement la dépendance au modèle Question dans votre modèle Answer

Donc, dans ce cas, dans votre answer.rb, vous écrirez

require_dependency 'question'

class Answer < ActiveRecord::Base
  # ..
end

Alors que plain require fonctionne, il est préférable d’utiliser require_dependency car cela fonctionnera avec le chargement automatique, ce qui signifie: se comporte comme prévu pendant le développement.

39
nathanvda

Dans Rails, vous n'êtes pas censé utiliser "require" car cela gâche le chargement automatique.

Une solution consiste à ajouter un require_dependency au fin du fichier qui définit la constante externe.

app/models/question.rb

class Question
  # ...
end

require_dependency 'question/document'

app/models/question/document.rb

class Question
  class Document
    # ...
  end
end

Cela force la classe Question::Document à être chargée après que la constante Question soit trouvée. Normalement, si Rails connaît déjà la constante Document de niveau supérieur, il ne tentera pas de charger Question::Document s'il n'est pas déjà connu.

Références:

18
Steve

Vous devez définir Question::Document avant de référencer la référence Document incriminée. Sinon, Ruby commencera à parcourir les espaces de noms pour trouver Document. Votre answer.rb devrait avoir 

require 'question/document'

en plus, en supposant que c’est le chemin où Question::Document est défini.

5
Tero Tilus

Vous pourriez voir l'avertissement comme ça 

/path/to/app/models/answer.rb:4: warning: toplevel constant Document referenced by Question::Document

Juste requirela classe qui a été référencée, sur le fichier supérieur qui lance Cet avertissement

Dans votre cas, la ligne ci-dessous ira dans app/model/answer.rb

require Rails.root.join('app/models/question/document.rb')

Et, après avoir redémarré le Rails server, vous ne verrez plus un tel avertissement.

Vous pouvez lire l'explication détaillée ici

1
Paritosh Piplewar

Placez les différentes définitions de classe dans l'ordre afin que Question::Document soit défini avant de le référencer. Sinon, Ruby cherche dans le niveau supérieur, comme le montre 7stud.

test.rb

class Document
end

class Question
end

class Question
  class Document
  end
end

class Answer
  puts Question::Document.class
end

Le résultat

$ Ruby test.rb
Class
0
Fred

J'ai écrit une gemme qui introduit une alternative à la solution require_dependency: heavy_control

Il résout explicitement les noms de constante donnés lors de l'initalisation via constantize (avant que d'autres constantes ne soient chargées). En outre, il se produit chaque recharge en développement.

0
mr_ffloyd