diff --git a/CHANGELOG.md b/CHANGELOG.md index f6e5d39e..c92cc5ef 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## Unreleased + +* Add `Spring.dangerously_allow_disabling_reloading` opt-in to skip the `:ensure_reloading_is_enabled` initializer check, so projects that want to run with `config.cache_classes = true` / `config.enable_reloading = false` can. +The default behavior (refuse to boot) is unchanged, as using this option requires a Rails application that uses +lazy-loader for everything (most importantly, routes & i18n translations). + + ## 4.5.0 * Skip spring without error if spring is not in installed bundler groups. diff --git a/README.md b/README.md index 6084c18c..11ac0ad2 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,31 @@ Spring manages. That setting is typically configured in Note: in versions of Rails before 7.1, the setting is called `cache_classes`, and it needs to be `false` for Spring to work. +#### Running Spring with reloading disabled (advanced, risky) + +If you know what you are doing, and if every reloadable resource is configured +to materialize *lazily in the forked child*, not in the Spring server process, +then you can decide to run Spring without Rail's in-process reloaders. + +To opt into this, set in `config/spring.rb`: + +```ruby +Spring.dangerously_allow_disabling_reloading = true +``` + +If you set this, you should make sure that you are NOT pre-loading reloadable +resource in the Spring server. One approach is to add boot-time assertions at +the end of `Spring.after_environment_load`, e.g.: + +```ruby +Spring.after_environment_load do + raise "routes were drawn at boot" if Rails.application.routes_reloader.loaded + raise "i18n locales where loaded at boot" unless I18n.backend.instance_variable_get(:@translations).nil? +end +``` + +When unsure, leave this disabled. + ### Usage For this walkthrough I've generated a new Rails application, and run diff --git a/lib/spring/application.rb b/lib/spring/application.rb index 2e0c7f46..338a7fea 100644 --- a/lib/spring/application.rb +++ b/lib/spring/application.rb @@ -101,6 +101,8 @@ def preload end Rails::Application.initializer :ensure_reloading_is_enabled, group: :all do + next if Spring.dangerously_allow_disabling_reloading + if Rails.application.config.cache_classes config_name, set_to = if Rails.application.config.respond_to?(:enable_reloading=) ["enable_reloading", "true"] @@ -110,6 +112,8 @@ def preload raise <<-MSG.strip_heredoc Spring reloads, and therefore needs the application to have reloading enabled. Please, set config.#{config_name} to #{set_to} in config/environments/#{Rails.env}.rb. + (If you understand the trade-offs and want to disable Rails' reloader anyway, + set `Spring.dangerously_allow_disabling_reloading = true` in config/spring.rb.) MSG end end diff --git a/lib/spring/configuration.rb b/lib/spring/configuration.rb index 8476aaeb..6f2179fa 100644 --- a/lib/spring/configuration.rb +++ b/lib/spring/configuration.rb @@ -3,11 +3,17 @@ module Spring @connect_timeout = 5 @boot_timeout = 20 + @dangerously_allow_disabling_reloading = false class << self attr_accessor :application_root, :connect_timeout, :boot_timeout attr_writer :quiet + # Opt-in: skip the `:ensure_reloading_is_enabled` initializer so Spring + # boots with `config.enable_reloading = false`. Default `false`. See + # README "Running Spring with reloading disabled" for what this gives up. + attr_accessor :dangerously_allow_disabling_reloading + def gemfile require "bundler" diff --git a/test/support/acceptance_test.rb b/test/support/acceptance_test.rb index 4c263fdf..31d8a4f6 100644 --- a/test/support/acceptance_test.rb +++ b/test/support/acceptance_test.rb @@ -158,6 +158,20 @@ def without_gem(name) assert_failure "bin/rails runner 1", stderr: expected_error end + test "Spring.dangerously_allow_disabling_reloading bypasses the reloading-enabled check" do + config_path = app.path("config/environments/development.rb") + config = File.read(config_path) + if config.include?("config.cache_classes") + config.sub!(/config\.cache_classes\s*=\s*false/, "config.cache_classes = true") + else + config.sub!(/config.enable_reloading = true/, "config.enable_reloading = true\nconfig.cache_classes = true") + end + File.write(config_path, config) + File.write(app.path("config/spring.rb"), "Spring.dangerously_allow_disabling_reloading = true\n") + + assert_success "bin/rails runner 1" + end + test "test changes are picked up" do assert_speedup do assert_success app.spring_test_command, stdout: "0 failures"