Rails не может автоматически загрузить константу из файла, несмотря на то, что она определена в этом файле
Это сложно объяснить. У меня есть модуль в другом пространстве имен модулей, например:
# app/models/points/calculator.rb
module Points
module Calculator
def self.included(base)
base.send(:include, CommonMethods)
base.send(:include, "Points::Calculator::#{base}Methods".constantize)
end
end
end
Итак, тогда в других классах мне нужно всего лишь:
class User
include Points::Calculator
end
Я установил этот каталог в application.rb для автозагрузки... (хотя я думаю, что рельсы рекурсируют через модели...)
config.autoload_paths += Dir[ Rails.root.join('app', 'models', "points") ]
В разработке env все работает нормально. При выполнении тестов (и создания env) я получаю следующую ошибку:
Unable to autoload constant Points::Calculator, expected /Users/pete/work/recognize/app/models/points/calculator.rb to define it (LoadError)
Я действительно последовал рекомендациям здесь, чтобы устранить проблему: Остановить рельсы при выгрузке модуля в режиме разработки, явно требуя calculator.rb в application.rb.
Однако почему это происходит?
Я запустил некоторый отладочный вывод в файле dependents.rb ActiveSupport и заметил, что этот файл требуется дважды. В первый раз, когда это необходимо, я вижу, что константа действительно загружена.
Но во второй раз требуемая константа была выгружена до тех пор, пока Rails может сказать, но когда вызывается фактическое требование, ruby возвращает false, потому что Ruby знает, что его уже потребовал. Затем Rails выдает ошибку "невозможно автонастраивать константу", потому что константа все еще отсутствует, а ruby не "повторно требует" файл.
Может ли кто-нибудь пролить свет на то, почему это может произойти?
Ответы
Ответ 1
Rails увеличивает механизм поиска констант ruby.
Постоянный поиск в Ruby:
Подобно method missing
, a Module#constant-missing
вызывается, когда ссылка на константу не может быть разрешена. Когда мы ссылаемся на константу в данной лексической области, эту константу ищут в:
Each entry in Module.nesting
Each entry in Module.nesting.first.ancestors
Each entry in Object.ancestors if Module.nesting.first is nil or a module.
Когда мы ссылаемся на константу, Ruby сначала пытается найти ее в соответствии с этими встроенными правилами поиска.
Когда Ruby не удается найти... рельсы пинают в и используя its own lookup convention
и его знания о том, какие константы уже были загружены (рубином), Rails переопределяет Module#const_missing
для загрузки отсутствующих констант без необходимости явного запроса вызова программистом.
Собственное соглашение о поиске?
Контрастность автозагрузки Rubys (которая требует указания местоположения каждой автозагруженной константы, которая должна быть указана заранее) rails, следуя соглашению, которое отображает константы в имена файлов.
Points::Calculator # =>points/calculator.rb
Теперь для константы Points:: Calculator, rails ищет этот путь к файлу (то есть "points/calculator.rb" ) внутри путей автозагрузки, определенных конфигурацией autoload_paths
.
В этом случае рельсы искали путь к файлу points/calculator
в своих автозагружаемых путях, но не смогли найти файл и, следовательно, эта ошибка/предупреждение отображается.
Этот ответ является абстрактом этого блога Urbanautomation.
Ответ 2
Calculator
должен быть класс для правильной автозагрузки
module Points
class Calculator
...
end
end