Reputation: 9456
I just upgraded an Engine from Rails 5 to Rails 7. This error started appearing at Rails 6.1.7.6, but I thought perhaps it might've been fixed in Rails 7.
Here's the error I get when I run rspec
An error occurred while loading ./spec/awesome_engine/services/awesome_engine/pdf_exporter/termination_spec.rb.
Failure/Error: Rails.application.initialize!
FrozenError:
can't modify frozen Array: ["/Users/bobbert/.gem/ruby/2.7.6/gems/actiontext-7.0.7.2/app/helpers", "/Users/bobbert/.gem/ruby/2.7.6/gems/actiontext-7.0.7.2/app/models"]
# /Users/bobbert/.gem/ruby/2.7.6/gems/railties-7.0.7.2/lib/rails/engine.rb:575:in `unshift'
# /Users/bobbert/.gem/ruby/2.7.6/gems/railties-7.0.7.2/lib/rails/engine.rb:575:in `block in <class:Engine>'
# /Users/bobbert/.gem/ruby/2.7.6/gems/railties-7.0.7.2/lib/rails/initializable.rb:32:in `instance_exec'
# /Users/bobbert/.gem/ruby/2.7.6/gems/railties-7.0.7.2/lib/rails/initializable.rb:32:in `run'
# /Users/bobbert/.gem/ruby/2.7.6/gems/railties-7.0.7.2/lib/rails/initializable.rb:61:in `block in run_initializers'
# /Users/bobbert/.gem/ruby/2.7.6/gems/railties-7.0.7.2/lib/rails/initializable.rb:50:in `each'
# /Users/bobbert/.gem/ruby/2.7.6/gems/railties-7.0.7.2/lib/rails/initializable.rb:50:in `tsort_each_child'
# /Users/bobbert/.gem/ruby/2.7.6/gems/railties-7.0.7.2/lib/rails/initializable.rb:50:in `each'
# /Users/bobbert/.gem/ruby/2.7.6/gems/railties-7.0.7.2/lib/rails/initializable.rb:50:in `tsort_each_child'
# /Users/bobbert/.gem/ruby/2.7.6/gems/railties-7.0.7.2/lib/rails/initializable.rb:60:in `run_initializers'
# /Users/bobbert/.gem/ruby/2.7.6/gems/railties-7.0.7.2/lib/rails/application.rb:372:in `initialize!'
# ./spec/dummy/config/environment.rb:5:in `<top (required)>'
# /Users/bobbert/.gem/ruby/2.7.6/gems/zeitwerk-2.6.11/lib/zeitwerk/kernel.rb:38:in `require'
# /Users/bobbert/.gem/ruby/2.7.6/gems/zeitwerk-2.6.11/lib/zeitwerk/kernel.rb:38:in `require'
# ./spec/spec_helper.rb:5:in `<top (required)>'
# /Users/bobbert/.gem/ruby/2.7.6/gems/zeitwerk-2.6.11/lib/zeitwerk/kernel.rb:38:in `require'
# /Users/bobbert/.gem/ruby/2.7.6/gems/zeitwerk-2.6.11/lib/zeitwerk/kernel.rb:38:in `require'
# ./spec/awesome_engine/services/awesome_engine/pdf_exporter/termination_spec.rb:1:in `<top (required)>'
...
Finished in 0.00005 seconds (files took 10.79 seconds to load)
0 examples, 0 failures, 133 errors occurred outside of examples
It occurs multiple times when trying different specs, and it always stems from the first line in the spec, then spec_helper.rb:5
, and finally environment.rb:5
.
First line of every spec is:
require 'spec_helper'
spec_helper.rb, Line 5
require File.expand_path("../dummy/config/environment.rb", __FILE__)
environment.rb, Line 5
Rails.application.initialize!
And this is the Rails code that's throwing the error (ruby/2.7.6/gems/railties-7.0.7.2/lib/rails/engine.rb:575
):
573 initializer :set_autoload_paths, before: :bootstrap_hook do
574 ActiveSupport::Dependencies.autoload_paths.unshift(*_all_autoload_paths)
575 ActiveSupport::Dependencies.autoload_once_paths.unshift(*_all_autoload_once_paths)
576
577 config.autoload_paths.freeze
578 config.autoload_once_paths.freeze
579 end
I've been following various Rails guides on the changes to autoloading, including:
I've also tried the suggestions in other Stackoverflow questions:
I've been at this now for several hours and am not progressing. Does anyone have any clue what's happening here and/or how to fix this issue?
So, I've decided to debug and step through the code.
There are 573 initializers in total. I placed a breakpoint on initializer :set_autoload_paths
in engine.rb Line 574
to keep track of how many times this initializer is called. Here's what I found:
Rails::Application.initialize! L372
Rails::Initializable::Initalizer.run_initializers L61
initializer(name: :set_autoload_paths) #initializer 103 of 573
initializer(name: :set_autoload_paths) #initializer 119 of 573
initializer(name: :set_autoload_paths) #initializer 140 of 573
initializer(name: :set_autoload_paths) #initializer 158 of 573
initializer(name: :set_autoload_paths) #initializer 171 of 573
...
At this point, it's obvious that it's being called multiple times. So I decided analyze the initializers
array to see just how many times it's being called and who is calling it. Here's what I found:
:set_autoload_paths=>{:count=>34, :contexts=>[#<ActionView::Railtie>, #<ActiveStorage::Engine>, #<ActionCable::Engine>, #<ActionMailbox::Engine>, #<ActionText::Engine>, #<StateMachine::RailsEngine>, #<Select2::Rails::Engine>, #<Doccex::Engine>, #<SmartListing::Engine>, #<Kaminari::Engine>, #<Devise::Engine>, #<DeviseInvitable::Engine>, #<Bootstrap::Rails::Engine>, #<Bootstrap::Switch::Rails::Engine>, #<Cocoon::Engine>, #<FontAwesome::Rails::Engine>, #<Remotipart::Rails::Engine>, #<I18n::JS::Engine>, #<Jquery::Rails::Engine>, #<Jquery::Ui::Rails::Engine>, #<JsRoutes::Engine>, #<DropzonejsRails::Engine>, #<TinyMCE::Rails::Engine>, #<BootstrapDatepickerRails::Rails::Engine>, #<Bootstrap3Datetimepicker::Rails::Engine>, #<Momentjs::Rails::Engine>, #<Uri::Js::Rails::Engine>, #<Sidekiq::Rails>, #<ActsAsTaggableOn::Engine>, #<Jscolor::Rails::Engine>, #<Tribute::Engine>, #<Doorkeeper::Engine>, #<AwesomeEngine::Engine>, #<Dummy::Application>]}
It's being called 34 times by various engines, several of which are from the Rails framework but the majority from 3rd party libraries that are included in the engine's gemspec.
Upvotes: 6
Views: 2479
Reputation: 2080
Try to start your Rails 7.1 app in development with config.eager_load = true
or
rails c -e production
and fix the errors.
After upgrading our Rails app from version 6 to 7.1, we encountered an issue where everything worked fine in the development environment, but all RSpec tests were failing with a FrozenError: can't modify frozen Array
. The error trace provided no helpful hints, which made debugging the issue quite challenging.
Also interesting: Locally the specs were fine, but on our CI they were failing.
Then I found this line in the config/environments/test.rb
:
config.eager_load = ENV["CI"].present?
Which explained the discrepancy between our specs on CI and locally. So when I ran:
CI=yes bundle exec rspec spec/
I was able to reproduce the behaviour and knew that the overall problem had to do with eager loading - which was one of the big changes from Rails 6 to Rails 7.
So I added this line to my /config/environments/development.rb
config.eager_load = true
And finally I received some meaningful error messages as I tried to start Rails, e.g.:
`const_get': uninitialized constant Validations::Users (NameError)
Zeitwerk requires that all classes be named exactly according to their file path which wasn't the case in my app. After resolving all the related errors, everything worked smoothly both locally, on CI, and in production.
Hope this helps someone.
Upvotes: 1