You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级至Rails 6后,Concern中无法加载Mongoid的Boolean类型

问题:Rails 6.0 + Ruby 3.0升级后Concern中Boolean常量未定义错误

我们在将应用从Ruby 2.7/Rails 5.2升级到Ruby 3.0/Rails 6.0时(因Ruby 3.0与Rails 5.2不兼容),遇到启动错误:uninitialized constant UserFields::Boolean (NameError)。错误出现在把User模型的字段拆分到UserFields concern中时,直接在User模型里定义Boolean类型字段则正常运行,原代码在旧环境下已稳定运行多年。

代码示例

User模型代码

class User
  include Mongoid::Document
  include Mongoid::Timestamps
  #field :force_password_change, type: Boolean, default: false
  include UserFields
  ....
end

UserFields Concern代码

module UserFields
  extend ActiveSupport::Concern

  included do
    ## Database authenticatable
    field :email,              :type => String, :default => ""
    field :force_password_change, type: Boolean, default: false  ### <<<< ERROR HERE
  end
end

错误追踪栈

! Unable to load application: NameError: uninitialized constant UserFields::Boolean
2024-01-10 15:22:22 bundler: failed to load command: puma (/app/bundle_cache/ruby/3.0.0/bin/puma)
2024-01-10 15:22:22 /app/app/models/concerns/user_fields.rb:8:in `block in <module:UserFields>': uninitialized constant UserFields::Boolean (NameError)
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/activesupport-6.0.6.1/lib/active_support/concern.rb:122:in `class_eval'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/activesupport-6.0.6.1/lib/active_support/concern.rb:122:in `append_features'
2024-01-10 15:22:22     from /app/app/models/user.rb:9:in `include'
2024-01-10 15:22:22     from /app/app/models/user.rb:9:in `<class:User>'
2024-01-10 15:22:22     from /app/app/models/user.rb:4:in `<main>'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/bootsnap-1.17.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/bootsnap-1.17.0/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:32:in `require'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/zeitwerk-2.6.12/lib/zeitwerk/kernel.rb:30:in `require'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/activesupport-6.0.6.1/lib/active_support/inflector/methods.rb:282:in `const_get'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/activesupport-6.0.6.1/lib/active_support/inflector/methods.rb:282:in `block in constantize'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/activesupport-6.0.6.1/lib/active_support/inflector/methods.rb:280:in `each'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/activesupport-6.0.6.1/lib/active_support/inflector/methods.rb:280:in `inject'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/activesupport-6.0.6.1/lib/active_support/inflector/methods.rb:280:in `constantize'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/activesupport-6.0.6.1/lib/active_support/dependencies/zeitwerk_integration.rb:19:in `constantize'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/devise-4.9.3/lib/devise.rb:325:in `get'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/devise-4.9.3/lib/devise/mapping.rb:83:in `to'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/devise-4.9.3/lib/devise/mapping.rb:78:in `modules'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/devise-4.9.3/lib/devise/mapping.rb:95:in `routes'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/devise-4.9.3/lib/devise/mapping.rb:162:in `default_used_route'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/devise-4.9.3/lib/devise/mapping.rb:72:in `initialize'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/devise-4.9.3/lib/devise.rb:361:in `new'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/devise-4.9.3/lib/devise.rb:361:in `add_mapping'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/devise-4.9.3/lib/devise/rails/routes.rb:243:in `block in devise_for'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/devise-4.9.3/lib/devise/rails/routes.rb:242:in `each'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/devise-4.9.3/lib/devise/rails/routes.rb:242:in `devise_for'
2024-01-10 15:22:22     from /app/config/routes.rb:264:in `block in <main>'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/actionpack-6.0.6.1/lib/action_dispatch/routing/route_set.rb:426:in `instance_exec'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/actionpack-6.0.6.1/lib/action_dispatch/routing/route_set.rb:426:in `eval_block'
2024-01-10 15:22:22     from /app/bundle_cache/ruby/3.0.0/gems/actionpack-6.0.6.1/lib/action_dispatch/routing/route_set.rb:408:in `draw'
2024-01-10 15:22:22     from /app/config/routes.rb:1:in `<main>'

解决方案

方案1:使用根命名空间常量

在Concern里把Boolean替换成::Boolean,强制从根命名空间查找常量:

field :force_password_change, type: ::Boolean, default: false

方案2:显式指定Mongoid类型

直接使用Mongoid::Boolean或者引入Mongoid的类型模块:

# 直接指定Mongoid类型
field :force_password_change, type: Mongoid::Boolean, default: false

# 或者在模块顶部引入类型模块
module UserFields
  extend ActiveSupport::Concern
  include Mongoid::Fields::Types

  included do
    field :force_password_change, type: Boolean, default: false
  end
end

方案3:使用符号替代常量

Mongoid支持用:boolean符号指定字段类型,完全避开常量查找问题:

field :force_password_change, type: :boolean, default: false

错误原因

Rails 6.0默认使用Zeitwerk加载器,常量查找规则比旧版Autoloader更严格。在Concern的included块中,常量会优先在当前模块(UserFields)的命名空间下查找,而旧环境中会自动回退到根命名空间。加上Devise在路由加载时提前触发了User模型的加载,导致常量查找时机与旧环境不同,最终触发错误。

内容的提问来源于stack exchange,提问作者timsegraves

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 08:25:57