将 Rails 从 6.1.1 升级到 7.0.3 后,我的 Devise 邮件程序无法初始化

After upgrading Rails from 6.1.1 to 7.0.3 my Devise mailers can't be initialized

我开始将我的应用程序从 Rails 6.1.1 升级到 7.0.3。在这样做的同时,我还必须将 Devise 从 4.7.3 升级到 4.8.1

使用旧设置一切正常。现在升级后我无法启动以下 none:

  1. rails 服务器
  2. rails 控制台
  3. rspec 规格
  4. 耙子rails:update

它们都失败并显示以下错误消息:

/home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/activesupport-7.0.3/lib/active_support/inflector/methods.rb:280:in `constantize': uninitialized constant AccountMailer
Did you mean?  ActionMailer (NameError)
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/activesupport-7.0.3/lib/active_support/core_ext/string/inflections.rb:74:in `constantize'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/devise-4.8.1/lib/devise.rb:320:in `get'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/devise-4.8.1/lib/devise.rb:343:in `mailer'
    from /home/andres/apps/my_app/config/initializers/devise.rb:25:in `block in <main>'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/devise-4.8.1/lib/devise.rb:307:in `setup'
    from /home/andres/apps/my_app/config/initializers/devise.rb:5:in `<main>'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/bootsnap-1.11.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:39:in `load'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/bootsnap-1.11.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:39:in `load'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/railties-7.0.3/lib/rails/engine.rb:667:in `block in load_config_initializer'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/activesupport-7.0.3/lib/active_support/notifications.rb:208:in `instrument'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/railties-7.0.3/lib/rails/engine.rb:666:in `load_config_initializer'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/railties-7.0.3/lib/rails/engine.rb:620:in `block (2 levels) in <class:Engine>'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/railties-7.0.3/lib/rails/engine.rb:619:in `each'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/railties-7.0.3/lib/rails/engine.rb:619:in `block in <class:Engine>'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/railties-7.0.3/lib/rails/initializable.rb:32:in `instance_exec'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/railties-7.0.3/lib/rails/initializable.rb:32:in `run'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/railties-7.0.3/lib/rails/initializable.rb:61:in `block in run_initializers'
    from /home/andres/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:228:in `block in tsort_each'
    from /home/andres/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:350:in `block (2 levels) in each_strongly_connected_component'
    from /home/andres/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:422:in `block (2 levels) in each_strongly_connected_component_from'
    from /home/andres/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:431:in `each_strongly_connected_component_from'
    from /home/andres/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:421:in `block in each_strongly_connected_component_from'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/railties-7.0.3/lib/rails/initializable.rb:50:in `each'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/railties-7.0.3/lib/rails/initializable.rb:50:in `tsort_each_child'
    from /home/andres/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:415:in `call'
    from /home/andres/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:415:in `each_strongly_connected_component_from'
    from /home/andres/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:349:in `block in each_strongly_connected_component'
    from /home/andres/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:347:in `each'
    from /home/andres/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:347:in `call'
    from /home/andres/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:347:in `each_strongly_connected_component'
    from /home/andres/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:226:in `tsort_each'
    from /home/andres/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/tsort.rb:205:in `tsort_each'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/railties-7.0.3/lib/rails/initializable.rb:60:in `run_initializers'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/railties-7.0.3/lib/rails/application.rb:372:in `initialize!'
    from /home/andres/apps/my_app/config/environment.rb:7:in `<main>'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/bootsnap-1.11.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/bootsnap-1.11.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/zeitwerk-2.5.4/lib/zeitwerk/kernel.rb:35:in `require'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/spring-2.1.1/lib/spring/application.rb:106:in `preload'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/spring-2.1.1/lib/spring/application.rb:157:in `serve'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/spring-2.1.1/lib/spring/application.rb:145:in `block in run'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/spring-2.1.1/lib/spring/application.rb:139:in `loop'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/spring-2.1.1/lib/spring/application.rb:139:in `run'
    from /home/andres/.rvm/gems/ruby-3.0.0@my_app/gems/spring-2.1.1/lib/spring/application/boot.rb:19:in `<top (required)>'
    from <internal:/home/andres/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
    from <internal:/home/andres/.rvm/rubies/ruby-3.0.0/lib/ruby/3.0.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
    from -e:1:in `<main>'

邮件文件

我有以下邮件文件夹结构:

app/mailers:

account_mailer.rb
application_mailer.rb

我的 account_mailer.rb 有以下声明:

# frozen_string_literal: true

# using SendGrid's Ruby Library
# https://github.com/sendgrid/sendgrid-ruby
require 'sendgrid-ruby'

class AccountMailer < Devise::Mailer
  include SendGrid
  default template_path: 'devise/mailer'

基于此,constantize 方法应该能够完成我所理解的所有需要​​的事情(文件名与 class 名称匹配,文件位于正确的文件夹结构下)。

使用邮件程序设计初始化

我的自定义 AccountMailer 在 Devise 初始化程序中初始化如下:config.mailer = 'AccountMailer'.

正如我所说 - 这一切在 Rails 6.1.1 中都很有效,但现在我无法理解 Rails 升级后出了什么问题。

我尝试过的:

我注释掉了 所有 AccountMailer 参考文献。之后我开始收到关于 Devise::Mailer 的错误信息(在 RSpec 中):

An error occurred while loading rails_helper.
Failure/Error: require File.expand_path('../config/environment', __dir__)

NameError:
  uninitialized constant Devise::Mailer
  Did you mean?  Devise::Mailers
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/activesupport-7.0.3/lib/active_support/inflector/methods.rb:280:in `constantize'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/activesupport-7.0.3/lib/active_support/core_ext/string/inflections.rb:74:in `constantize'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/devise-4.8.1/lib/devise.rb:320:in `get'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/devise-4.8.1/lib/devise.rb:343:in `mailer'
# ./config/initializers/devise.rb:25:in `block in <main>'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/devise-4.8.1/lib/devise.rb:307:in `setup'
# ./config/initializers/devise.rb:5:in `<main>'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/bootsnap-1.11.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:39:in `load'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/bootsnap-1.11.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:39:in `load'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/railties-7.0.3/lib/rails/engine.rb:667:in `block in load_config_initializer'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/activesupport-7.0.3/lib/active_support/notifications.rb:208:in `instrument'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/railties-7.0.3/lib/rails/engine.rb:666:in `load_config_initializer'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/railties-7.0.3/lib/rails/engine.rb:620:in `block (2 levels) in <class:Engine>'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/railties-7.0.3/lib/rails/engine.rb:619:in `each'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/railties-7.0.3/lib/rails/engine.rb:619:in `block in <class:Engine>'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/railties-7.0.3/lib/rails/initializable.rb:32:in `instance_exec'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/railties-7.0.3/lib/rails/initializable.rb:32:in `run'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/railties-7.0.3/lib/rails/initializable.rb:61:in `block in run_initializers'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/railties-7.0.3/lib/rails/initializable.rb:50:in `each'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/railties-7.0.3/lib/rails/initializable.rb:50:in `tsort_each_child'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/railties-7.0.3/lib/rails/initializable.rb:60:in `run_initializers'
# /home/andres/.rvm/gems/ruby-3.0.0@vaab/gems/railties-7.0.3/lib/rails/application.rb:372:in `initialize!'
# ./config/environment.rb:7:in `<top (required)>'
# ./spec/rails_helper.rb:8:in `require'
# ./spec/rails_helper.rb:8:in `<top (required)>'

当我注释掉 all Devise 初始化程序中的 Devise 邮件程序配置行时,如下所示:

# config.mailer = 'AccountMailer'
# config.mailer.perform_deliveries = !Rails.env.test?
# config.mailer.raise_delivery_errors = true

那时我的应用程序似乎可以 100% 正常工作。

我检查了 Devise changelog 4.7.3 之后的版本,我是从 4.​​7.3 升级的,但我没有注意到那里有任何相关的变化。

我还检查了 Rails release notes 版本 6.1.1 和 7.0.3 之间与邮件程序相关的配置更改,我再次没有注意到任何可能影响的内容。

error trace中出现的constantize方法说明如下:https://api.rubyonrails.org/classes/String.html#method-i-constantize

我将 Rails 升级教程作为 here 的基础。 这可能是什么问题?

设置class名称后,下一行getterconfig.mailer常量化,因为它需要class对象

在 Rails 6.0 和 6.1 中,该访问会收到嘈杂的警告,在启动期间自动加载可重新加载 class 已被弃用。在 Rails 7 中,弃用周期结束,在引导期间自动加载可重新加载的 class 或模块是错误条件。

一种可能的解决方案是在 Devise 的初始化程序中使用 require 调用预加载该邮件程序。自动加载器会看到 AccountMailer 已经加载,并且会忽略 account_mailer.rb.

基本原理是 AccountMailer 不能重新加载,因为重新加载的更改不会有任何效果,因为在重新加载时初始化程序不会 运行。这个错误防止在没有注意到的情况下引入这种不一致。