如何在 Rails 的开发模式下针对每个请求自动重新加载 gem 代码?

ace*_*des 6 ruby-on-rails activesupport

我正在开发一个 Rails 应用程序,其中大多数非特定于该应用程序的代码都编写在各种 gem 中,包括一些 Rails 引擎和一些我正在增强或修复错误的 3rd 方 gem。

gem 'mygem', path: File.expath_path('../../mygem', __FILE__)
Run Code Online (Sandbox Code Playgroud)

由于这些 gem 中的许多代码实际上是应用程序的一部分,因此它仍然经常更改。我希望能够利用 Rails 功能,在开发时(即当config.cache_classes为 false 时)根据每个请求重新加载代码,但这默认情况下仅在正常应用程序结构中完成。

如何配置 Rails 在每个请求上重新加载 gem 代码,就像应用程序代码一样?

ace*_*des 3

我通过反复试验发现,在ActiveSupport.

  • 添加为文件activesupport中的依赖项.gemspec

    spec.add_dependency 'activesupport'
    
    Run Code Online (Sandbox Code Playgroud)
  • 在 gem 的顶级模块中包含 ActiveSupport::Dependencies (这是最难以捉摸的要求)

    require 'bundler'; Bundler.setup
    require 'active_support/dependencies'
    
    module MyGem
      unloadable
      include ActiveSupport::Dependencies
    end
    
    require 'my_gem/version.rb'
    # etc...
    
    Run Code Online (Sandbox Code Playgroud)
  • 设置您的 gem 以使用自动加载。您可以手动使用 ruby​​ 自动加载声明将符号映射到文件名,或者使用 Rails 风格的文件夹结构到模块层次结构规则(请参阅ActiveSupport #constantize

  • 在 gem 中的每个模块和类中,添加unloadable.

    module MyModule
      unloadable
    end
    
    Run Code Online (Sandbox Code Playgroud)
  • 在依赖于 gem 中的模块或类的每个文件中(包括 gem 本身),在每个文件的顶部使用require_dependency. 根据需要查找 gem 的路径以正确解析路径。

    require_dependency "#{Gem.loaded_specs['my_gem'].full_gem_path}/lib/my_gem/myclass"
    
    Run Code Online (Sandbox Code Playgroud)

如果您在修改文件并发出请求后遇到异常,请检查您是否错过了依赖项。

有关一些有趣的详细信息,请参阅这篇有关 Rails(和 ruby​​)自动加载的综合文章。