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