Rails 6.1应用初始化后FeatureFlags配置object_id变更问题
问题原因说明
Rails 6默认使用Zeitwerk作为自动加载器,你的问题本质是开发环境下的自动重载机制导致的:
- Rails开发环境默认
config.cache_classes = false,所有Zeitwerk托管的自动加载常量,在每次请求结束后都会被卸载,下次引用时会重新加载类文件。 - 你的
FeatureFlags类放在app/lib下,属于Zeitwerk的自动加载路径。初始化器是Rails启动阶段执行的,这时候引用FeatureFlags会触发Zeitwerk第一次加载这个类,你在初始化器里给@configuration赋值完成,得到第一个object_id的实例。 - 当第一次请求到来时,之前加载的
FeatureFlags常量已经被Zeitwerk卸载,调用FeatureFlags.enabled?时会重新加载整个feature_flags.rb文件,类级别的@configuration实例变量会被重置,自然就得到了新的object_id,之前的配置也全部丢失。 - 你手动加
require 'feature_flags'之后,这个类是通过Ruby原生的require机制加载的,Zeitwerk不会把它标记为自动重载的托管常量,所以请求阶段不会被卸载,实例就可以保留。
疑问解答
为什么Zeitwerk自动加载了还需要手动require?
不是Zeitwerk没加载,而是它默认会在开发环境重载自动加载路径下的常量。你初始化阶段的类实例和请求阶段的类已经不是同一个了,自然配置会丢失。手动require相当于告诉Zeitwerk这个类不要管重载的事,自然就不会出问题。手动require是不是正确的实现方式?
对于不需要热重载的工具类来说,这个做法完全可用,也不会有什么副作用。如果想要更规范的处理,也可以选择下面的方案:- 把
FeatureFlags类移到lib目录下,不要放在自动重载的app/lib里,在config/application.rb中把lib加入全局加载路径,这样默认就不会被重载 - 如果一定要放在
app/lib下,可以配置Zeitwerk忽略这个文件的重载:Rails.autoloaders.main.ignore(Rails.root.join('app/lib/feature_flags.rb')) - 生产环境默认
config.cache_classes = true,不会触发常量重载,就算不加手动require也不会出现配置丢失的问题
- 把
内容的提问来源于stack exchange,提问作者yoones
相关产品推荐
相关产品推荐

