我正在开发一个Rails应用程序,其中大部分不是特定应用程序的代码都是在各种宝石中编写的,包括一些Rails引擎和一些我正在增强或修复错误的第三方宝石。
gem 'mygem', path: File.expath_path('../../mygem', __FILE__)
由于这些宝石中的很多代码实际上都是应用程序的一部分,因此它仍然经常发生变化。我希望能够利用Rails功能,其中在开发时(即config.cache_classes
为假时)在每个请求上重新加载代码,但这仅在默认情况下在正常应用程序结构内完成。
如何配置Rails以在每个请求上重新加载gem代码,就像使用应用程序代码一样?
答案 0 :(得分:3)
在ActiveSupport
的帮助下,我通过反复试验找到了需要采取的几个步骤。
在activesupport
个文件
.gemspec
作为依赖项
spec.add_dependency 'activesupport'
在gem的顶级模块中包含ActiveSupport :: Dependencies(这是最难以捉摸的要求)
require 'bundler'; Bundler.setup
require 'active_support/dependencies'
module MyGem
unloadable
include ActiveSupport::Dependencies
end
require 'my_gem/version.rb'
# etc...
设置您的gem以使用自动加载。您可以手动使用ruby autoload声明将符号映射到文件名,也可以使用Rails样式的文件夹结构到模块层次结构规则(请参阅ActiveSupport #constantize)
在gem中的每个模块和类中,添加unloadable
。
module MyModule
unloadable
end
在每个依赖于gem中的模块或类的文件中,包括在gem本身中,使用require_dependency
在每个文件的顶部声明它们。根据需要查找gem的路径以正确解析路径。
require_dependency "#{Gem.loaded_specs['my_gem'].full_gem_path}/lib/my_gem/myclass"
如果您在修改文件和提出请求后遇到异常,请检查您是否错过了依赖项。
有关一些有趣的详细信息,请参阅this关于Rails(和ruby)自动加载的综合帖子。
答案 1 :(得分:0)
我用于 Rails 6 的解决方案,带有专用的 Zeitwerk 类加载器和文件检查器:
使用 path:
中的 Gemfile
选项将 gem 添加到 Rails 项目
gem 'mygem', path: 'TODO' # The root directory of the local gem
在 development.rb
中,设置类加载器和文件观察器
gem_path = 'TODO' # The root directory of the local gem, the same used in Gemfile
# Create a Zeitwerk class loader for each gem
gem_lib_path = gem_path.join('lib').join(gem_path.basename)
gem_loader = Zeitwerk::Registry.loader_for_gem(gem_lib_path)
gem_loader.enable_reloading
gem_loader.setup
# Create a file watcher that will reload the gem classes when a file changes
file_watcher = ActiveSupport::FileUpdateChecker.new(gem_path.glob('**/*')) do
gem_loader.reload
end
# Plug it to Rails to be executed on each request
Rails.application.reloaders << Class.new do
def initialize(file_watcher)
@file_watcher = file_watcher
end
def updated?
@file_watcher.execute_if_updated
end
end.new(file_watcher)
这样,对于每个请求,类加载器将重新加载 gem 类(如果其中一个已被修改)。
有关详细的漫游,请参阅 Embed a gem in a Rails project and enable autoreload。