Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 4 additions & 0 deletions lib/spring/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand All @@ -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
Expand Down
6 changes: 6 additions & 0 deletions lib/spring/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"

Expand Down
14 changes: 14 additions & 0 deletions test/support/acceptance_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Loading