Rails 6: автоматически перезагружать локальный гем при изменении
Я параллельно разрабатываю приложение Rails 6 и Gem.
Раньше я использовал require_reloader Gem, чтобы Rails перезагружал Gem при изменении любых файлов в локальном каталоге Gem.
Поскольку Zeitwerk стал новым загрузчиком в Rails 6, этот Gem, похоже, больше не работает.
Итак, мой вопрос: каков канонический способ разработки приложений Gem и Rails 6 параллельно, чтобы изменения, внесенные в файлы Gem, автоматически отображались в Rails?
1 ответ
Я также не нашел канонического решения этой проблемы, но в контексте Rails: Auto-reload gem-файлы, используемые в фиктивном приложении , нашел обходной путь:
Предположим, что папка gem
~/rails/foo_gem
и папка rails-6-app:
~/rails/bar_app
Чтобы перезагрузить код gem в приложении при изменении файловой системы, мне нужно было сделать три шага:
- Отмените регистрацию загрузчика zeitwerk, определенного в нем, который обрабатывает загрузку различных файлов драгоценных камней.
- Определите новый загрузчик zeitwerk в приложении, для которого настроено расширение .
- Настройте наблюдатель за файловой системой и инициируйте перезагрузку при изменении файла gem.
в
# ~/rails/foo_gem/lib/foo_gem.rb
# require 'foo_gem/bar` # Did not work. Instead:
# (a) use zeitwerk:
require "zeitwerk"
loader = Zeitwerk::Loader.new
loader.push_dir File.join(__dir__)
loader.tag = "foo_gem"
loader.setup
# or (b) use autoload:
module FooGem
autoload :Bar, "foo_gem/bar"
end
Примечание:
- В прошлом я просто загружал все рубиновые файлы драгоценного камня из своего рода индексного файла, который называется точно так же, как и драгоценный камень, здесь: . Здесь это не работает, потому что zeitwerk игнорирует файлы, которые ранее были загружены с помощью
require
. Вместо этого мне нужно было создать отдельный загрузчик zeitwerk для драгоценного камня. - У этого загрузчика нет, потому что в противном случае для этого драгоценного камня была бы включена перезагрузка при каждом использовании драгоценного камня, а не только во время разработки драгоценного камня.
- Я дал загрузчику
tag
, что позволяет найти этот загрузчик позже вZeitwerk::Registry
для того, чтобы снять его с регистрации. - Вместо использования zeitwerk в
foo_gem.rb
, можно также использовать там, как это делает гем devise . Это лучший способ, если вы хотите поддерживать более ранние версии rails, чем 6, потому что для zeitwerk требуется rails 6+. С использованиемautoload
здесь также делает ненужным шаг 1 в следующем разделе.
Zeitwerk::Loader
в
development.rb
приложения
# ~/rails/bar_app/config/environments/development.rb
# 1. Unregister the zeitwerk loader defined in foo_gem.rb that handles loading
# the different gem files.
#
Zeitwerk::Registry.loaders.detect { |l| l.tag == "foo_gem" }.unregister
# 2. Define a new zeitwerk loader in the development.rb of the app
# that is configured with enable_reloading.
#
gem_root_path = Pathname.new(Gem.loaded_specs["foo_gem"].full_gem_path)
gem_loader = Zeitwerk::Loader.new
gem_loader.push_dir gem_root_path.join("lib")
gem_loader.enable_reloading
gem_loader.setup
# 3. Setup a file-system watcher and trigger a reload when a gem file changes.
#
Listen.to gem_root_path.join("lib"), only: /\.rb$/ do
gem_loader.reload
end.start
Примечание:
- Zeitwerk не позволяет двум загрузчикам управлять одними и теми же файлами. Поэтому мне нужно отменить регистрацию ранее определенного загрузчика с тегом
"foo_gem"
. - Новый загрузчик, используемый в приложении, имеет
enable_reloading
. Таким образом, при использовании приложения с , или при запуске спецификаций файлы гема могут быть перезагружены. - Файлы драгоценных камней не перезагружаются автоматически zeitwerk. Нужен наблюдатель за файловой системой, чтобы запускать перезагрузку при изменении файловой системы. мне не удалось получить
ActiveSupport::FileUpdateChecker
работающий. Вместо этого я использовал гем listen в качестве наблюдателя за файловой системой.
С этой настройкой при использовании
rails console
, или спецификации bar_app, gem-файлов
foo_gem
перезагружаются после редактирования, что означает, что больше не нужно перезапускать
rails server
чтобы забрать изменения.
Однако я не нахожу этот обходной путь очень удобным.