предупреждение: указана константа верхнего уровня

У меня четыре модели (Document, Question, Question::Document и Answer). В моей модели Answer есть

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

Это дает мне предупреждение

warning: toplevel constant Document referenced by Question::Document

Как мне предотвратить появление этого предупреждения (без переименования моих классов)?


person Kyle Decot    schedule 29.08.2013    source источник
comment
что, если ты сделаешь ::Question::Document   -  person apneadiving    schedule 29.08.2013
comment
Неа. Предупреждение по-прежнему сохраняется   -  person Kyle Decot    schedule 29.08.2013
comment
Я предполагаю, что это только проблема разработки, чтобы быть уверенным, явно требуется файл, определяющий класс   -  person apneadiving    schedule 29.08.2013
comment
@KyleDecot, мой ответ работает?   -  person Paritosh Piplewar    schedule 08.09.2013
comment
Есть отзывы здесь? Было дано много ответов, и все они должны были приблизить вас к решению, imho.   -  person nathanvda    schedule 12.09.2013
comment
Извините, @nathanvda. Отчасти отложил эту проблему на задний план, пока я работал над некоторыми другими вещами. Ваше решение, похоже, подавило предупреждение. Спасибо!   -  person Kyle Decot    schedule 12.09.2013


Ответы (7)


Структура вашей папки / файла должна выглядеть следующим образом:

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

И тогда rails автоматически найдет правильные модели (он переведет имя модели в имя файла, а пространства имен будут переведены в папки).

Убедитесь, что внутри вашего question/document.rb определение класса выглядит как одна из следующих альтернатив:

class Question::Document
end

or

class Question
  class Document
  end
end

Если вы пишете просто class Document, вы переопределяете константу верхнего уровня Document.

Обратите внимание, что если глобальный Document определен первым, это также вызовет эту ошибку. Это зависит от пути кода, поэтому лучший способ решить эту проблему - добавить require_dependency там, где это необходимо. См. здесь и здесь для получения дополнительной информации.

Например. что-то вроде

require_dependency 'question/document' 

class Answer < ActiveRecord::Base

end  

Если вы поместите файл в другое место, где rails не смогут его найти автоматически, вам придется явно потребовать его, чтобы rails знал Question::Document о существовании.

Если, например, вы определяете Question::Document внутри модели Question, что является разумным местом, вам нужно будет явно указать зависимость от модели Question в вашей Answer модели.

Итак, в таком случае в вашем answer.rb вы напишете

require_dependency 'question'

class Answer < ActiveRecord::Base
  # ..
end

Хотя обычный require работает, вместо него предпочтительнее использовать require_dependency, поскольку он будет работать с автоматической загрузкой, что означает: ведет себя так, как ожидалось, во время разработки.

person nathanvda    schedule 11.09.2013
comment
Что касается самой последней версии rails, это фактически не работает, если что-то сначала загружает документ верхнего уровня. Если это произойдет, Rails просто выдаст ошибку и откажется искать правильный файл, если вы попытаетесь получить доступ к Question :: Document после нее. - person GlyphGryph; 15.07.2015
comment
С какой версией рельсов вы тестировали? Это было бы действительно странно, и имхо ошибка. Я хочу это проверить. - person nathanvda; 15.07.2015
comment
На самом деле я только что проверил репозиторий Rails на github, и похоже, что это известная проблема, но она была закрыта, так как это очевидно, как Rails работает с пространствами имен из-за использования const_missing. Не знаю деталей, но на самом деле это работает так для всех Rails, когда-либо существовавших. Похоже, единственное решение - вручную require_dependency файлы в правильном порядке всякий раз, когда они могут быть вызваны. Может, это вообще рубиновая штука? github.com/rails/rails/issues/6931 Это одна из проблем, из-за которых это вверх. - person GlyphGryph; 16.07.2015
comment
Тем не менее, хорошее время для ответа на вопрос, на который вы ответили два года назад! - person GlyphGryph; 16.07.2015
comment
Зачем нам нужно require 'question' в answer.rb - person hiveer; 11.08.2015
comment
Я не уверен, что непонятно, потому что сейчас я бы просто повторил строчку из своего ответа. Позвольте мне попробовать: если класс Question::Document определен внутри класса Question, то есть внутри файла question.rb, у rails нет способа найти этот файл (и, следовательно, определение), поскольку он будет искать файл question/document.rb, поэтому у вас будет чтобы явно потребовать файл. Это яснее? - person nathanvda; 11.08.2015
comment
Обновил свой ответ, чтобы отдать предпочтение require_dependency, а не require, и на самом деле лучше всегда добавлять явное require_depency там, где это необходимо, как это предлагается в документации. - person nathanvda; 08.07.2016
comment
лучший ответ. должен быть зеленым - person Tim Kretschmer; 26.12.2016
comment
Я столкнулся с той же проблемой прямо сейчас, и мне было интересно, можете ли вы также просто поместить require_dependency 'question/document' в начало файла question.rb? Потому что, когда вы позже в коде будете ссылаться на Question::Answer, Rails сначала загрузит Question из question.rb, что также загрузит Question::Answer из-за require_dependency. - person schneikai; 14.11.2018

В Rails вы не должны использовать "require", поскольку это мешает автозагрузке.

Одно из решений - добавить require_dependency в конец файла, который определяет внешнюю константу.

app / models / question.rb

class Question
  # ...
end

require_dependency 'question/document'

app / models / question / document.rb

class Question
  class Document
    # ...
  end
end

Это заставляет загрузку класса Question::Document после нахождения константы Question. Обычно, если Rails уже знает о Document константе верхнего уровня, он не будет пытаться загрузить Question::Document, если она еще не известна.

Ссылки:

person Steve    schedule 21.11.2015

Вам необходимо определить Question::Document, прежде чем ссылаться на недопустимую Document ссылку. В противном случае Ruby начнет обход пространств имен вверх, чтобы найти Document. Ваш answer.rb должен иметь

require 'question/document'

поверх него, предполагая, что это путь, по которому определено Question::Document.

person Tero Tilus    schedule 06.09.2013

Вы могли видеть такое предупреждение

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

Просто require класс, на который была сделана ссылка в верхнем файле, который выдает это предупреждение.

В вашем случае строка ниже войдет в app/model/answer.rb

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

И после перезапуска rails server вы не увидите такого уродливого предупреждения.

Вы можете прочитать подробное объяснение здесь

person Paritosh Piplewar    schedule 07.09.2013
comment
Почему простого require 'question/document' недостаточно? Путь загрузки будет содержать Rails.root.join('app/models'), верно? - person Tero Tilus; 08.09.2013
comment
Это тоже нормально, но я был обеспокоен этим предупреждением, поэтому предпочитаю использовать полный путь. - person Paritosh Piplewar; 08.09.2013
comment
OK. Было бы неплохо указать на это, поскольку использование require с абсолютными путями в высшей степени неидиоматично. - person Tero Tilus; 08.09.2013

Расположите различные определения классов по порядку так, чтобы Question::Document был определен до того, как на него ссылаться. В противном случае рубин будет искать верхний уровень, как показал 7stud.

test.rb

class Document
end

class Question
end

class Question
  class Document
  end
end

class Answer
  puts Question::Document.class
end

Результат

$ ruby test.rb
Class
person Fred    schedule 06.09.2013
comment
Теоретически это правильно. Но в рельсах вы обычно не помещаете модели в один и тот же файл (потому что тогда рельсы не могут автоматически определять имена файлов на основе имени класса). Ваш ответ показывает, что требование правильного файла с правильным классом должно работать. - person nathanvda; 12.09.2013
comment
но он не работает (три уровня) с Rails (4.1.5) должным образом, долгосрочным решением является переименование одного из классов, если вы не хотите возиться с требованием порядка - особенно в Minitest. - person Ivan Stana; 25.08.2014

Я написал жемчужину, которая представляет альтернативу require_dependency решению: heavy_control

Он явно разрешает заданные имена констант при инициализации через constantize (до загрузки других констант). Также это происходит при каждой перезагрузке в разработке.

person mr_ffloyd    schedule 27.09.2016

Вы имеете в виду вот что:

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

Похоже, что ruby ​​ищет константу, охватывающую области, когда она не найдена в указанной области. Я думаю, что предупреждение - это наказание за невозможность придумать более двух имен переменных.

person 7stud    schedule 29.08.2013
comment
Это не значит, что я не могу назвать это как-то иначе, но имеет смысл назвать их аналогичным образом, потому что они связаны друг с другом в том Question::Document вопросе, к которому в качестве ответа прикреплен Документ. Я бы предпочел не называть иначе, если это вообще возможно, потому что легче определить отношения / связь между двумя - person Kyle Decot; 29.08.2013
comment
Этот код недействителен. puts Question::Document выполняется при синтаксическом анализе файла, и в то время класс Question::Document просто еще не определен. Так что измените порядок или поместите puts в функцию, и она будет работать. Также: ваш вывод неверен: штраф за невозможность придумать более двух имен переменных ???? Какого черта? Вполне возможно использовать одно и то же имя класса в другой области. - person nathanvda; 12.09.2013
comment
@nathanvda, Так что измените порядок или поместите вставки в функцию, и она будет работать. Смысл примера состоял в том, чтобы использовать все имена, упомянутые операцией, в некотором коде, который производит то же самое сообщение об ошибке - не производит рабочий код. Какого черта? Вполне возможно использовать одно и то же имя класса в другой области. И вполне возможно использовать имена переменных xxxyxx, xxyxxx, xyxxx, xxxyx в той же области. И что? Цель состоит не только в том, чтобы написать юридический синтаксис. Некоторые из нас пытаются поставить планку немного выше. - person 7stud; 12.09.2013