如何让Gem的Railtie在所有config/initializers加载完成后运行?
解决Gem Railtie提前读取宿主配置的问题
你遇到的核心问题是Railtie初始化器的执行时机早于宿主应用的config/initializers配置代码,导致读取到默认值而非用户设置的值。以下是Gem内部即可实现的解决方案,不需要用户修改application.rb:
调整初始化器执行时机
Rails内部有一个:build_middleware_stack钩子,负责最终构建应用的中间件栈,这个钩子的执行时机晚于所有config/initializers文件的加载,同时又早于中间件栈的最终定型(不会像after_initialize那样添加中间件太晚)。
只需给你的initializer加上before: :build_middleware_stack选项,就能确保它在宿主配置加载完成后、中间件栈构建前执行:
module Mygem class Railtie < Rails::Railtie # 指定在构建中间件栈前执行这个初始化器 initializer "mygem_engine.initialize_warden", before: :build_middleware_stack do |app| if Mygem.use_warden_middleware? app.config.middleware.use RailsWarden::Manager do |manager| # Config for RailsWarden::Manager end end end end end
为什么这个方案可行?
Rails的初始化流程大致是:
- 加载框架与Gem
- 执行
:before_configuration钩子 - 加载环境配置文件
- 执行
:before_initialize钩子 - 加载所有config/initializers下的文件(用户的配置代码在这里执行)
- 执行默认组的初始化器
- 执行
:build_middleware_stack钩子前的初始化器(你的代码在这里执行) - 构建中间件栈
- 执行
:after_initialize钩子
通过指定before: :build_middleware_stack,你的初始化器会卡在步骤7,此时用户的配置已经生效,同时中间件还没开始最终构建,添加的RailsWarden::Manager能正常加入中间件栈。
为什么之前的尝试有问题?
- 默认的initializer属于
:initialize组,在某些应用的加载顺序中,这个组的执行可能早于config/initializers的加载(比如Gem提前被Bundler.require加载时) config.after_initialize是在中间件栈构建完成后才执行,此时添加中间件不会被应用采纳,自然无法生效
内容的提问来源于stack exchange,提问作者KNejad
相关产品推荐
相关产品推荐

