Rails:自动重新加载虚拟应用中使用的 gem 个文件
Rails: Auto-reload gem files used in dummy app
在开发 gem 时,我经常使用需要 gem 的虚拟 rails 应用程序,以便在我进行时尝试 gem 更改。此外,我使用相同的虚拟应用程序进行集成测试。
通常,我在
中有gem
~/rails/foo_gem
和关联的虚拟应用程序位于:
~/rails/foo_gem/spec/dummy_app
使用 zeitwerk 代码加载器,如何配置虚拟应用程序 不仅在更改时重新加载虚拟应用程序 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")
我尝试将 gem 添加到自动加载路径,但代码不会在文件系统更改时重新加载。
无效:Zeitwerk::Loader
与 enable_reloading
# ~/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
才能重新加载 gem 类.
无效:Zeitwerk::Loader#reload
与 require
如果 gem 的文件在 gem 中是必需的,例如
# ~/rails/foo_gem/lib/foo_gem.rb
require 'foo_gem/bar`
然后文件 ~/rails/foo_gem/lib/foo_gem/bar.rb
被 Zeitwerk::Loader
忽略。调用 gem_loader.reload
不会重新加载此文件。
没有工作:Zeitwerk::Loader#reload
用于 gem 个文件的 zeitwerk 加载程序
如果不需要手动 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
由两个独立的 zeitwerk 加载器管理:foo_gem.rb
中的 loader
和 development.rb
中的 gem_loader
。这显然是 zeitwerk 不允许的,它抱怨 Zeitwerk::Error
:
loader ... wants to manage directory ~/rails/foo_gem/lib, which is already managed by ...
无效: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
来观察变化,但是,至少在我的 dockerized 设置中,重新加载代码的块从未被执行。
要在 file-system 更改时重新加载 gem 代码,我需要执行三个步骤:
- 取消注册在
foo_gem.rb
中定义的处理不同 gem 文件加载的 zeitwerk 加载程序。
- 在虚拟应用程序的
development.rb
中定义一个新的 zeitwerk 加载器,该应用程序配置有 enable_reloading
。
- 设置 file-system 观察器并在 gem 文件更改时触发重新加载。
我确信有更简单、更清洁的解决方案。随意 post 它作为一个单独的答案。我会 post 我的解决方案在这里,但请考虑它是一种解决方法。
Zeitwerk::Loader
在 foo_gem.rb
# ~/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
注:
- 过去,我只是从一种名为 gem 的索引文件中用
require
加载了 gem 的所有 ruby 个文件,这里:foo_gem.rb
。这在这里不起作用,因为 zeitwerk 似乎忽略了以前用 require
加载的文件。相反,我需要为 gem. 创建一个单独的 zeitwerk 加载程序
- 这个加载器没有
enable_reloading
因为否则,只要使用 gem,就会为这个 gem 启用重新加载,而不仅仅是在开发 gem 时。
- 我已经给加载器一个
tag
,它允许稍后在 Zeitwerk::Registry
中找到这个加载器,以便 un-register 它。
- 除了在
foo_gem.rb
中使用 zeitwerk,还可以在 like the devise gem does 中使用 autoload
。如果想要支持早于 6 的 rails 版本,这是最好的方法,因为 zeitwerk 需要 rails 6+。在这里使用 autoload
也使得下一节中的步骤 1 变得不必要。
虚拟应用 development.rb
中的 Zeitwerk::Loader
# ~/rails/foo_gem/spec/dummy_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 dummy app
# that is configured with enable_reloading.
#
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.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
。因此,当使用具有 rails server
、rails console
的虚拟应用程序时,或者当 运行 规格时,可以重新加载 gem 文件。
- zeitwerk 不会自动重新加载 gem 文件。需要一个 file-system 观察者来触发对 file-system 更改的重新加载。我没有设法让
ActiveSupport::FileUpdateChecker
working. Instead, I've used the listen gem 作为 file-system 观察者。
使用此设置,当使用虚拟应用程序的 rails server
、rails console
或使用虚拟应用程序进行集成测试时,gem 文件会在编辑后重新加载,这意味着那一个不再需要重新启动 rails server
来获取更改。
参考资料
在开发 gem 时,我经常使用需要 gem 的虚拟 rails 应用程序,以便在我进行时尝试 gem 更改。此外,我使用相同的虚拟应用程序进行集成测试。
通常,我在
中有gem~/rails/foo_gem
和关联的虚拟应用程序位于:
~/rails/foo_gem/spec/dummy_app
使用 zeitwerk 代码加载器,如何配置虚拟应用程序 不仅在更改时重新加载虚拟应用程序 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")
我尝试将 gem 添加到自动加载路径,但代码不会在文件系统更改时重新加载。
无效:Zeitwerk::Loader
与 enable_reloading
# ~/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
才能重新加载 gem 类.
无效:Zeitwerk::Loader#reload
与 require
如果 gem 的文件在 gem 中是必需的,例如
# ~/rails/foo_gem/lib/foo_gem.rb
require 'foo_gem/bar`
然后文件 ~/rails/foo_gem/lib/foo_gem/bar.rb
被 Zeitwerk::Loader
忽略。调用 gem_loader.reload
不会重新加载此文件。
没有工作:Zeitwerk::Loader#reload
用于 gem 个文件的 zeitwerk 加载程序
如果不需要手动 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
由两个独立的 zeitwerk 加载器管理:foo_gem.rb
中的 loader
和 development.rb
中的 gem_loader
。这显然是 zeitwerk 不允许的,它抱怨 Zeitwerk::Error
:
loader ... wants to manage directory ~/rails/foo_gem/lib, which is already managed by ...
无效: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
来观察变化,但是,至少在我的 dockerized 设置中,重新加载代码的块从未被执行。
要在 file-system 更改时重新加载 gem 代码,我需要执行三个步骤:
- 取消注册在
foo_gem.rb
中定义的处理不同 gem 文件加载的 zeitwerk 加载程序。 - 在虚拟应用程序的
development.rb
中定义一个新的 zeitwerk 加载器,该应用程序配置有enable_reloading
。 - 设置 file-system 观察器并在 gem 文件更改时触发重新加载。
我确信有更简单、更清洁的解决方案。随意 post 它作为一个单独的答案。我会 post 我的解决方案在这里,但请考虑它是一种解决方法。
Zeitwerk::Loader
在 foo_gem.rb
# ~/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
注:
- 过去,我只是从一种名为 gem 的索引文件中用
require
加载了 gem 的所有 ruby 个文件,这里:foo_gem.rb
。这在这里不起作用,因为 zeitwerk 似乎忽略了以前用require
加载的文件。相反,我需要为 gem. 创建一个单独的 zeitwerk 加载程序
- 这个加载器没有
enable_reloading
因为否则,只要使用 gem,就会为这个 gem 启用重新加载,而不仅仅是在开发 gem 时。 - 我已经给加载器一个
tag
,它允许稍后在Zeitwerk::Registry
中找到这个加载器,以便 un-register 它。 - 除了在
foo_gem.rb
中使用 zeitwerk,还可以在 like the devise gem does 中使用autoload
。如果想要支持早于 6 的 rails 版本,这是最好的方法,因为 zeitwerk 需要 rails 6+。在这里使用autoload
也使得下一节中的步骤 1 变得不必要。
虚拟应用 development.rb
中的 Zeitwerk::Loader
# ~/rails/foo_gem/spec/dummy_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 dummy app
# that is configured with enable_reloading.
#
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.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
。因此,当使用具有rails server
、rails console
的虚拟应用程序时,或者当 运行 规格时,可以重新加载 gem 文件。 - zeitwerk 不会自动重新加载 gem 文件。需要一个 file-system 观察者来触发对 file-system 更改的重新加载。我没有设法让
ActiveSupport::FileUpdateChecker
working. Instead, I've used the listen gem 作为 file-system 观察者。
使用此设置,当使用虚拟应用程序的 rails server
、rails console
或使用虚拟应用程序进行集成测试时,gem 文件会在编辑后重新加载,这意味着那一个不再需要重新启动 rails server
来获取更改。