在开发gem时,我经常使用一个虚拟的Rails应用程序,需要引用这个gem以便在进行修改时尝试它。同时,我也会将同一个虚拟应用程序用于集成测试。
通常情况下,我将gem放置在
~/rails/foo_gem
以及相关的虚拟应用程序在:
~/rails/foo_gem/spec/dummy_app
使用 zeitwerk 代码加载器,我该如何配置虚拟应用程序(dummy app) 才能在更改时不仅重新加载虚拟应用程序的 Ruby 文件,还能够更新 gem 文件中的更改?否则,在开发 gem 过程中,每次更改 gem 文件都需要重新加载虚拟应用程序的 Rails 服务器。
# ~/rails/foo_gem/spec/dummy_app/config/environments/development.rb
config.cache_classes = false
config.eager_load = false
# TODO: Add ~/rails/foo_gem/lib to the list
# of watched and auto-reloaded directories.
没有生效:config.autoload_paths
# ~/rails/foo_gem/spec/dummy_app/config/environments/development.rb
gem_root_path = Pathname.new(File.expand_path(Rails.root.join("../..")))
config.autoload_paths << gem_root_path.join("lib")
我尝试将宝石添加到自动加载路径中,但是代码没有在文件系统更改时重新加载。
无效:具有enable_reloading
的Zeitwerk::Loader
# ~/rails/foo_gem/spec/dummy_app/config/environments/development.rb
gem_root_path = Pathname.new(File.expand_path(Rails.root.join("../..")))
gem_loader = Zeitwerk::Loader.new
gem_loader.push_dir gem_root_path.join("lib")
gem_loader.enable_reloading
gem_loader.log!
gem_loader.setup
添加一个单独的Zeitwerk加载器是没有用的;据我所了解,该加载器不会带有文件系统监听器;因此需要调用gem_loader.reload
来重新加载宝石类。
Zeitwerk::Loader#reload
与require
一起使用无效
如果在宝石中需要引入宝石自身的文件,例如:
# ~/rails/foo_gem/lib/foo_gem.rb
require 'foo_gem/bar`
若使用Zeitwerk::Loader
,则文件~/rails/foo_gem/lib/foo_gem/bar.rb
将被忽略。调用gem_loader.reload
不会重载此文件。
Zeitwerk::Loader#reload
在使用Zeitwerk加载器加载gem文件时无效
如果不手动引用gem文件,而是使用不同的Zeitwerk加载器,例如:
# ~/rails/foo_gem/lib/foo_gem.rb
require "zeitwerk"
loader = Zeitwerk::Loader.new
loader.push_dir(__dir__)
loader.setup
如果目录 ~/rails/foo_gem/lib
同时被 foo_gem.rb
中的 loader
和 development.rb
中的 gem_loader
两个独立的 zeitwerk 加载器管理,则会导致问题。这显然不符合 zeitwerk 的规则,会抛出 Zeitwerk::Error
异常:
loader ... 希望管理目录 ~/rails/foo_gem/lib,但该目录已经被 ... 所管理。
无效: ActiveSupport::FileUpdateChecker
# ~/rails/foo_gem/spec/dummy_app/config/environments/development.rb
gem_root_path = Pathname.new(File.expand_path(Rails.root.join("../..")))
gem_loader = Zeitwerk::Loader.new
gem_loader.push_dir gem_root_path.join("lib")
gem_loader.enable_reloading
gem_loader.log!
gem_loader.setup
gem_files = gem_root_path.glob("lib/**/*.rb")
gem_update_checker = ActiveSupport::FileUpdateChecker.new(gem_files) do
gem_loader.reload # This line is never executed
end
ActiveSupport::Reloader.to_prepare do
gem_update_checker.execute_if_updated
end
我曾尝试使用ActiveSupport::FileUpdateChecker
来监视文件变化,但是,至少在我的Docker环境中,重载代码的块永远不会被执行。