spring
spring copied to clipboard
Spring in Rails 6 tries to load ActiveRecord even when AR is not part of the application
If I create a skeleton Rails API application, and then remove ActiveRecord from the application, spring still tries to load AR which ultimately fails and prevents generators etc. from working.
This is a regression in Rails 6 - in Rails 5.2 everything works as expected.
I created two applications, which are identical except for version of Rails used.
https://github.com/p-mongo/tests/tree/master/spring-ar-sqlite-5 https://github.com/p-mongo/tests/tree/master/spring-ar-sqlite-6
Generate the app:
rails new --api spring-ar-sqlite-6
cd spring-ar-sqlite
Edit Gemfile, removing reference to sqlite.
Edit config/application.rb, commenting out ActiveRecord and ActiveStorage:
#require "active_record/railtie"
#require "active_storage/engine"
Run:
rails g controller foo
Result:
butler% rails g controller foo
/home/sandbox/.rbenv/versions/2.6.5/lib/ruby/2.6.0/bundler/rubygems_integration.rb:408:in `block (2 levels) in replace_gem': Error loading the 'sqlite3' Active Record adapter. Missing a gem it depends on? sqlite3 is not part of the bundle. Add it to your Gemfile. (LoadError)
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.0/lib/active_record/connection_adapters/sqlite3_adapter.rb:13:in `<main>'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `block in require_with_bootsnap_lfi'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:21:in `require_with_bootsnap_lfi'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/zeitwerk-2.2.0/lib/zeitwerk/kernel.rb:23:in `require'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.0/lib/active_record/connection_adapters/connection_specification.rb:170:in `spec'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.0/lib/active_record/connection_adapters/abstract/connection_pool.rb:1044:in `establish_connection'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.0/lib/active_record/connection_handling.rb:51:in `establish_connection'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.0/lib/active_record/railtie.rb:201:in `block (2 levels) in <class:Railtie>'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.0/lib/active_support/lazy_load_hooks.rb:72:in `class_eval'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.0/lib/active_support/lazy_load_hooks.rb:72:in `block in execute_hook'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.0/lib/active_support/lazy_load_hooks.rb:62:in `with_execution_control'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.0/lib/active_support/lazy_load_hooks.rb:67:in `execute_hook'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.0/lib/active_support/lazy_load_hooks.rb:52:in `block in run_load_hooks'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.0/lib/active_support/lazy_load_hooks.rb:51:in `each'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activesupport-6.0.0/lib/active_support/lazy_load_hooks.rb:51:in `run_load_hooks'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.0/lib/active_record/base.rb:327:in `<module:ActiveRecord>'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/activerecord-6.0.0/lib/active_record/base.rb:27:in `<main>'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `require'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:22:in `block in require_with_bootsnap_lfi'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/loaded_features_index.rb:92:in `register'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:21:in `require_with_bootsnap_lfi'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/zeitwerk-2.2.0/lib/zeitwerk/kernel.rb:23:in `require'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/application.rb:370:in `active_record_configured?'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/application.rb:287:in `disconnect_database'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/application.rb:111:in `preload'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/application.rb:157:in `serve'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/application.rb:145:in `block in run'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/application.rb:139:in `loop'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/application.rb:139:in `run'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/spring-2.1.0/lib/spring/application/boot.rb:19:in `<top (required)>'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
from /home/sandbox/.rbenv/versions/2.6.5/lib/ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
from -e:1:in `<main>'
Something in your app is defining ActiveRecord::Base. Spring only try to load it if the constant exists https://github.com/rails/spring/blob/master/lib/spring/application.rb#L370.
The AR require is triggered from that line, leading me to believe that something configures AR to be autoloaded, but I don't know how to determine what that would be.
In any event "my app" has no code, as stated in the above description. I generated it using the standard Rails generator and removed AR railtie.
oh. Yeah, that is true. ActiveRecord::Base is autoloaded, so of course that code doesn't work. We need to find a way to check if Active Record is loaded without checking for defined?.
Try this "--skip-active-record" with "rails new"
rails new --skip-acitve-record --api also omits spring from the generated application. Thus it is a valid workaround but does not fix the problem reported in this issue.
I thought simply adding sqlite to the bundle was a sufficient workaround, but per https://stackoverflow.com/questions/58352612/rails-tasks-and-generators-are-failing/58383659#58383659 the application must define a full AR configuration. If this configuration is loaded, i.e. connection(s) to the database are established, this is a rather heavy workaround considering the AR database won't ever be used by any application code.
I tripped over this as well deploying an application to Heroku. I did not need ActiveRecord, and simply leaving sqlite installed is not compatible with Heroku. It looks like a few indirect dependencies on ActiveRecord precipitated the autoloading. Anyways, the resolution turns out to be relatively straightfoward.
- I removed sqlite from my Gemfile
- deleted
config/database.yml - Manually required the railties that would NOT result in ActiveRecord loading:
--- a/config/application.rb
+++ b/config/application.rb
@@ -1,6 +1,29 @@
require_relative 'boot'
-require 'rails/all'
+# require 'rails/all'
+
+ # Removed because ActiveRecord:
+ # active_record/railtie
+ #
+ # Removed because precipitates AR load:
+ # action_mailbox/engine
+ # active_storage/engine
+ # action_text/engine
+
+%w(
+ action_cable/engine
+ active_job/railtie
+ action_mailer/railtie
+ action_controller/railtie
+ action_view/railtie
+ rails/test_unit/railtie
+ sprockets/railtie
+).each do |railtie|
+ begin
+ require railtie
+ rescue LoadError
+ end
+end
- And finally, removed any reference to
active_storageandactive_recordin myconfig/environments/*.rbfiles, e.g.:
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -29,7 +29,7 @@ Rails.application.configure do
end
# Store uploaded files on the local file system (see config/storage.yml for options).
- config.active_storage.service = :local
+ # config.active_storage.service = :local
# Don't care if the mailer can't send.
config.action_mailer.raise_delivery_errors = false
@@ -40,10 +40,10 @@ Rails.application.configure do
config.active_support.deprecation = :log
# Raise an error on page load if there are pending migrations.
- config.active_record.migration_error = :page_load
+ # config.active_record.migration_error = :page_load
# Highlight code that triggered database queries in logs.
- config.active_record.verbose_query_logs = true
+ # config.active_record.verbose_query_logs = true
Hopefully this helps!
Thanks so much @coderifous ! You really helped me out today
@coderifous active storage is the point in my case. Thank you