J’ai quatre modèles ( Document
, Question
, Question::Document
et Answer
). Dans mon modèle de 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 puis-je empêcher cet avertissement (sans renommer mes classes)?
Votre structure de dossier / fichier doit ressembler à ceci:
app/ models/ question/ document.rb answer.rb document.rb question.rb
Et puis, les rails trouveront automatiquement les modèles corrects (cela traduira le nom du modèle en un nom de fichier et les espaces de noms seront traduits en dossiers).
Assurez-vous que dans votre question/document.rb
la définition de classe ressemble à l’une des alternatives suivantes:
class Question::Document end
ou
class Question class Document end end
Si vous écrivez simplement class Document
vous redéfinissez le Document
toplevel constant.
Notez que si le Document
global est défini en premier, cela déclenchera également cette erreur. Cela dépend du chemin de code, donc le meilleur moyen de résoudre ce problème est d’append une require_dependency
requirejse. Voir ici et ici pour plus de fond.
Par exemple, quelque chose comme
require_dependency 'question/document' class Answer < ActiveRecord::Base end
Si vous placez le fichier dans un endroit différent, où les rails ne peuvent pas le trouver automatiquement, vous devrez le demander explicitement, de sorte que Rails connaît 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 de Answer
.
Donc, dans ce cas, dans votre answer.rb
vous écrirez
require_dependency 'question' class Answer < ActiveRecord::Base # .. end
Bien que plain
require
travaux, il est préférable d'utiliserrequire_dependency
car il fonctionnera avec le chargement automatique, ce qui signifie: se comporte comme prévu pendant le développement.
Dans Rails, vous n’êtes pas censé utiliser “require” car cela gêne le chargement automatique.
Une solution à ce problème consiste à append une require_dependency
à la 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 la découverte de la constante Question
. Normalement, si Rails connaît déjà la constante Document
niveau supérieur, il ne tentera pas de charger Question::Document
s’il n’est pas déjà connu.
Les références:
Vous devez avoir une Question::Document
définie avant de référencer une référence de Document
offensante. Sinon, Ruby commencera à parcourir les espaces de noms pour trouver Document
. Votre answer.rb
devrait avoir
require 'question/document'
en plus de cela, en supposant que c’est le chemin où Question::Document
est défini.
Vous pourriez voir l’avertissement comme ça
/path/to/app/models/answer.rb:4: warning: toplevel constant Document referenced by Question::Document
Exigez simplement la classe qui a été référencée , dans 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 d’avertissement aussi moche.
Mettez 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 regarde dans le niveau supérieur, comme 7stud l’a montré.
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
J’ai écrit un joyau qui présente une alternative à la solution require_dependency
: heavy_control
Il résout explicitement les noms de constantes données lors de l’initialisation via la constantize
(avant que d’autres constantes ne soient chargées). Il arrive aussi chaque rechargement en développement.
Tu veux dire comme cela:
Document = 'hello' class Question end class Animal puts Question::Document end class Question class Document end end --output:-- 1.rb:7: warning: toplevel constant Document referenced by Question::Document hello
Il semble que les recherches de ruby contenant des étendues pour une constante ne se trouvent pas dans la scope spécifiée. Je pense que l’avertissement est une pénalité pour avoir été incapable de concevoir plus de deux noms de variables.