Zeitwerk环境下如何缓存类列表?含目录预加载问题
Rails开发环境下Zeitwerk的类列表缓存与目录预加载方案
问题描述
我正尝试缓存一份类列表,当前实现代码如下:
def models return @models if @models Zeitwerk::Loader.eager_load_all # 开发环境下需预加载所有模型,否则部分模型未加载会不在列表中 @models = ActiveRecord::Base.descendants.select { |model| model.include?(SomeModule) } end
但Zeitwerk在开发环境会随时重载类,导致@models缓存失效。另外还想知道如何程序化预加载单个目录,而非整个应用。
补充说明:该类列表仅用于特定rake任务和测试中,不用于生产运行代码。
附相关实现代码示例:
class ModelFoo annotated ... end class ModelBar annotated ... end module Annotating extend ActiveSupport::Concern module Model extend ActiveSupport::Concern included do has_many :annotations, as: :annotated, dependent: :destroy, autosave: true end ... end class_methods do def annotated class_eval do include Model end end end class << self def models <see above the body of this method> end end end ActiveRecord::Base.include(Annotating::Model)
后续会使用Annotating.models为所有标注模型生成触发器,并测试这些模型的标准操作。
解决方案
1. 解决开发环境缓存失效问题
由于你的类列表仅用于rake任务和测试场景,可通过以下两种方式处理缓存失效问题:
方案A:临时禁用Zeitwerk自动重载
在执行特定任务时关闭Zeitwerk的自动重载,确保加载后的类不会被重置,缓存即可保持有效:
class << self def models return @models if @models # 开发环境下临时关闭自动重载 if Rails.env.development? Rails.application.config.zeitwerk.checks = false Rails.application.config.zeitwerk.enable_reloading = false end Zeitwerk::Loader.eager_load_all @models = ActiveRecord::Base.descendants.select { |model| model.include?(Annotating::Model) } end end
该操作仅作用于当前任务流程,不会影响开发环境的日常代码重载功能。
方案B:移除缓存逻辑,每次调用重新生成
若Annotating.models调用频率较低,直接去掉缓存,每次调用都重新加载筛选:
class << self def models Zeitwerk::Loader.eager_load_all ActiveRecord::Base.descendants.select { |model| model.include?(Annotating::Model) } end end
rake任务和测试场景下性能影响可忽略,还能彻底避免缓存失效问题。
2. 程序化预加载单个目录
Zeitwerk的每个loader对应一个目录,可定位目标目录的loader并单独预加载,无需全局加载整个应用:
比如要预加载app/models/annotated目录:
class << self def models return @models if @models preload_target_directory @models = ActiveRecord::Base.descendants.select { |model| model.include?(Annotating::Model) } end private def preload_target_directory target_dir = Rails.root.join("app/models/annotated").to_s loader = Zeitwerk::Loader.loaders.find { |l| l.dirs.include?(target_dir) } loader&.eager_load end end
如果所有标注模型都集中在同一目录,这种方式能精准加载所需内容,提升加载效率。
内容的提问来源于stack exchange,提问作者akostadinov
相关产品推荐
相关产品推荐

