Модели Rails усиливают нагрузку
Я хотел бы иметь возможность загружать все приложение, чтобы я мог найти потомков данного класса.
Например, у меня есть следующий класс:
# app/models/foo_class.rb
class FooClass < MySpecialBaseClass
# pasta code
end
Он не будет найден с помощью:
irb> ObjectSpace.each_object.select { |obj| obj.is_a?(Class) && obj <= MySpecialBaseClass }
=> []
Пока я не назову const:
irb> FooClass
Затем возвращается:
irb> ObjectSpace.each_object.select { |obj| obj.is_a?(Class) && obj <= MySpecialBaseClass }
=> [FooClass]
Как мне это сделать?
Ответы
Ответ 1
После большого количества проб и ошибок я недавно узнал, что ответ Джейсона Вальдрипа несколько неполный. Начиная с Rails 5, Rails.application.eager_load!
действительно загрузит все каталоги, указанные в config/application.rb
. Но это не соответствует фактическому поведению Rails в производстве. Для этого нужно вместо этого зеркально отразить то, что делает Rails:
Rails.configuration.eager_load_namespaces.each(&:eager_load!)
Существенная разница между подходами заключается в том, что ответ OPs не будет загружать файлы в каталогах приложений Engine
которые живут в папках с gems
или vendor
. Принимая во внимание, что Rails сам определяет, где существуют подклассы Engine
и будет следить за тем, чтобы соответствующие подкаталоги app
загружались с нетерпением.
За кулисами
Rails 5 добавляет загружаемые каталоги в railties-5.0.2/lib/rails/engine/configuration.rb:39
, где он запускается
paths.add "app", eager_load: true, glob: "{*,*/concerns}"
paths.add "app/assets", glob: "*"
paths.add "app/controllers", eager_load: true
paths.add "app/channels", eager_load: true, glob: "**/*_channel.rb"
paths.add "app/helpers", eager_load: true
paths.add "app/models", eager_load: true
paths.add "app/mailers", eager_load: true
Эти каталоги в настоящее время не включены в Rails.application.eager_load!
умолчанию Rails.application.eager_load!
Ответ 2
Ну, после некоторого копания, на самом деле это действительно просто. Просто нужно запустить следующее.
Rails.application.eager_load!
Ответ 3
Из Настройка Rails-приложений
- config.eager_load при true Eager загружает все зарегистрированные
config.eager_load_namespaces. Это включает в себя ваше приложение,
движки, фреймворки Rails и любое другое зарегистрированное пространство имен.
- config.eager_load_namespaces регистрирует пространства имен, которые стремятся
загружается, когда config.eager_load имеет значение true. Все пространства имен в списке
должен ответить на eager_load! Метод.
- config.eager_load_paths принимает массив путей, с которых Rails
будет загружаться при загрузке, если включены классы кэша. По умолчанию
каждая папка в каталоге приложения.
EDIT:
Для ручной загрузки вы должны сделать что-то вроде:
matcher = /\A#{Regexp.escape(load_path)}\/(.*)\.rb\Z/
Dir.glob("#{load_path}/**/*.rb").sort.each do |file|
require_dependency file.sub(matcher, '\1')
end
Ответ 4
With Rails 6, Zetiwerk стал загрузчиком кода по умолчанию.
Попробуйте это для быстрой загрузки:
Zeitwerk::Loader.eager_load_all