升级至Rails 6.1.6后引入的路由辅助方法无法正常工作
问题根因
这是Rails 6.1路由加载逻辑变更导致的兼容性问题,和检索到的6.1版本路由异常问题属于同一类。
Rails 6.0版本中,routes.rb内直接对引擎url_helpers_module的模块混入操作,会在路由辅助方法注入控制器、视图上下文前完成,自定义方法可以被正常识别。6.1版本对路由加载流程做了性能优化,冷启动阶段挂载的引擎路由url_helpers_module会在你执行include(DynUrlHelper)操作后被框架内部重新生成替换,之前混入的自定义方法会随旧模块被丢弃,因此冷启动时会报方法不存在的错误。
触发Zeitwerk代码重载后方法恢复正常,是因为重载阶段不会走冷启动时的路由模块重建逻辑,之前混入的方法被保留了下来。
修复方案
两种方案均不需要修改Spree遗留代码,可按需选择:
方案1:调整模块混入时机(最小改动)
把自定义模块的混入逻辑移到所有路由、引擎全部加载完成的生命周期节点执行,避开框架重建路由模块的时机:
# config/routes.rb MyApp::Application.routes.draw do mount Spree::Core::Engine, at: "/" end # 应用初始化完成、所有路由加载完毕后再注入自定义路由辅助方法 Rails.application.config.after_initialize do Spree::Core::Engine.routes.named_routes.url_helpers_module.include(DynUrlHelper) end
该方案完全保留原有调用逻辑,冷启动、代码重载场景下都能正常运行。
方案2:直接注入到控制器/视图上下文(不依赖路由内部结构)
如果不想耦合Rails路由内部的模块实现逻辑,可以直接把自定义辅助方法注入到需要调用的上下文里,兼容性更强:
# config/initializers/spree_dyn_url_patch.rb Rails.application.config.to_prepare do # 注入主应用控制器、视图上下文 ActionController::Base.include(DynUrlHelper) ActionView::Base.include(DynUrlHelper) # 兼容Spree引擎独立的控制器上下文 Spree::BaseController.include(DynUrlHelper) if defined?(Spree::BaseController) end
这里用to_prepare生命周期块的原因是,不管是冷启动还是Zeitwerk触发的代码重载,块内逻辑都会重新执行,不会出现重载后方法丢失的问题。
验证方式
修改完成后重启应用,冷启动状态下直接访问对应页面调用dyn_path即可正常运行,不需要额外触发文件修改重载。
内容的提问来源于stack exchange,提问作者23tux
相关产品推荐
相关产品推荐

