Home > Back-end >  After upgrading Rails from 6.1.1 to 7.0.3 my Devise mailers can't be initialized
After upgrading Rails from 6.1.1 to 7.0.3 my Devise mailers can't be initialized

Time:05-21

I started upgrading my application from Rails 6.1.1 to 7.0.3. While doing this I also had to upgrade Devise from 4.7.3 to 4.8.1.

With old setup everything worked fine. Now after upgrading I can't start none of the following:

  1. rails server
  2. rails console
  3. rspec specs
  4. rake rails:update

They all fail with the following error message:

/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>'

Mailer files

I'm having the following mailer-folder structure:

app/mailers:

account_mailer.rb
application_mailer.rb

My account_mailer.rb has the following declaration:

# 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'

Based on that the constantize method should be able to do all it needs to I understand (file names match the class name, files are present under correct folder structure).

Devise initialization with mailer

My custom AccountMailer is initialized inside Devise initializer like this: config.mailer = 'AccountMailer'.

As I said- this all worked like a charm in Rails 6.1.1 but now I can't get my head around what's wrong after the Rails upgrade.

What I've tried:

I commented out all the AccountMailer references. After that I started getting errors about Devise::Mailer like this (in 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)>'

When I commented out all the Devise mailer configuration lines in Devise initializer like this:

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

Then only at that point my application seemed to work 100% as it needs to.

I checked the Devise changelog for releases after 4.7.3 that I was upgrading from and I didn't notice any relevant changes there.

I also checked Rails mailer related configuration changes between versions 6.1.1 and 7.0.3 from release notes and I again didn't notice anything that might affect.

The constantize method that appears in error trace is described here: https://api.rubyonrails.org/classes/String.html#method-i-constantize

I'm taking the Rails upgrade tutorial as basis from here. What could be the issue here?

CodePudding user response:

After setting the class name, the getter config.mailer in the next line constantizes, because it needs the class object.

In Rails 6.0 and 6.1, that access got a noisy warning, autoloading a reloadable class during boot got deprecated. In Rails 7, the deprecation cycle finished, and autoloading a reloadable class or module during boot is an error condition.

One possible solution is to preload that mailer with a require call in the initializer for Devise. The autoloader will see AccountMailer was already loaded, and will ignore account_mailer.rb.

The rationale is that AccountMailer cannot be reloadable, because reloaded changes would have no effect, since on reload that initializer does not run. This error prevents introducing this incongruence without noticing.

  • Related