XUtils

Spring

Preloads your rails environment in the background for faster testing and Rake tasks.


Spring

Build Status Gem Version

Spring is a Rails application preloader. It speeds up development by keeping your application running in the background, so you don’t need to boot it every time you run a test, rake task or migration.

Features

  • Totally automatic; no need to explicitly start and stop the background process
  • Reloads your application code on each run
  • Restarts your application when configs / initializers / gem dependencies are changed

Walkthrough

Enable reloading

Spring reloads application code, and therefore needs the application to have reloading enabled.

Ensure that config.enable_reloading is true in the environments that Spring manages. That setting is typically configured in config/environments/*.rb. In particular, make sure it is true for the test environment.

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 tests:

…….

Finished tests in 0.127245s, 55.0121 tests/s, 78.5887 assertions/s.

7 tests, 10 assertions, 0 failures, 0 errors, 0 skips

real 0m2.165s user 0m0.281s sys 0m0.066s


That wasn't particularly fast because it was the first run, so Spring
had to boot the application. It's now running:

$ bin/spring status Spring is running:

26150 spring server | spring-demo-app | started 3 secs ago 26155 spring app | spring-demo-app | started 3 secs ago | test mode


The next run is faster:

$ time bin/rake test test/controllers/posts_controller_test.rb Running via Spring preloader in process 8352 Run options:

Running tests:

…….

Finished tests in 0.176896s, 39.5714 tests/s, 56.5305 assertions/s.

7 tests, 10 assertions, 0 failures, 0 errors, 0 skips

real 0m0.610s user 0m0.276s sys 0m0.059s


If we edit any of the application files, or test files, the changes will
be picked up on the next run without the background process having to
restart. This works in exactly the same way as the code reloading
which allows you to refresh your browser and instantly see changes during
development.

But if we edit any of the files which were used to start the application
(configs, initializers, your gemfile), the application needs to be fully
restarted. This happens automatically.

Let's "edit" `config/application.rb`:

\( touch config/application.rb \) bin/spring status Spring is running:

26150 spring server | spring-demo-app | started 36 secs ago 26556 spring app | spring-demo-app | started 1 sec ago | test mode


The application detected that `config/application.rb` changed and
automatically restarted itself.

If we run a command that uses a different environment, then that
environment gets booted up:

$ bin/rake routes Running via Spring preloader in process 2363

posts GET    /posts(.:format)          posts#index
      POST   /posts(.:format)          posts#create

new_post GET /posts/new(.:format) posts#new edit_post GET /posts/:id/edit(.:format) posts#edit

 post GET    /posts/:id(.:format)      posts#show
      PUT    /posts/:id(.:format)      posts#update
      DELETE /posts/:id(.:format)      posts#destroy

$ bin/spring status Spring is running:

26150 spring server | spring-demo-app | started 1 min ago 26556 spring app | spring-demo-app | started 42 secs ago | test mode 26707 spring app | spring-demo-app | started 2 secs ago | development mode


There's no need to "shut down" Spring. This will happen automatically
when you close your terminal. However, if you do want to do a manual shut
down, use the `stop` command:

$ bin/spring stop Spring stopped.


From within your code, you can check whether Spring is active with `if defined?(Spring)`.

### Removal

To remove Spring:

* 'Unspring' your bin/ executables: `bin/spring binstub --remove --all`
* Remove spring from your Gemfile

### Deployment

You must not install Spring on your production environment. To prevent it from
being installed, run the `bundle config set without 'development test'` before
`bundle install` command which is used to install gems on your production
machines:

\( bundle config set without 'development test' \) bundle install


## Commands

### `rake`

Runs a rake task. Rake tasks run in the `development` environment by
default. You can change this on the fly by using the `RAILS_ENV`
environment variable. The environment is also configurable with the
`Spring::Commands::Rake.environment_matchers` hash. This has sensible
defaults, but if you need to match a specific task to a specific
environment, you'd do it like this:

``` ruby
Spring::Commands::Rake.environment_matchers["perf_test"] = "test"
Spring::Commands::Rake.environment_matchers[/^perf/]     = "test"

# To change the environment when you run `rake` with no arguments
Spring::Commands::Rake.environment_matchers[:default] = "development"

rails console, rails generate, rails runner

These execute the rails command you already know and love. If you run a different sub command (e.g. rails server) then Spring will automatically pass it through to the underlying rails executable (without the speed-up).

Additional commands

You can add these to your Gemfile for additional commands:

Temporarily disabling Spring

If you’re using Spring binstubs, but temporarily don’t want commands to run through Spring, set the DISABLE_SPRING environment variable.

Class reloading

Spring uses Rails’ class reloading mechanism to keep your code up to date between test runs. This is the same mechanism which allows you to see changes during development when you refresh the page. However, you may never have used this mechanism with your test environment before, and this can cause problems.

It’s important to realise that code reloading means that the constants in your application are different objects after files have changed:

$ bin/rails runner 'puts User.object_id'
70127987886040
$ touch app/models/user.rb
$ bin/rails runner 'puts User.object_id'
70127976764620

Suppose you have an initializer config/initializers/save_user_class.rb like so:

USER_CLASS = User

This saves off the first version of the User class, which will not be the same object as User after the code has been reloaded:

$ bin/rails runner 'puts User == USER_CLASS'
true
$ touch app/models/user.rb
$ bin/rails runner 'puts User == USER_CLASS'
false

So to avoid this problem, don’t save off references to application constants in your initialization code.

Configuration

Spring will read ~/.spring.rb and config/spring.rb for custom settings. Note that ~/.spring.rb is loaded before bundler, but config/spring.rb is loaded after bundler. So if you have any spring-commands-* gems installed that you want to be available in all projects without having to be added to the project’s Gemfile, require them in your ~/.spring.rb.

config/spring_client.rb is also loaded before bundler and before a server process is started, it can be used to add new top-level commands.

Application root

Spring must know how to find your Rails application. If you have a normal app everything works out of the box. If you are working on a project with a special setup (an engine for example), you must tell Spring where your app is located:

Spring.application_root = './test/dummy'

Running code before forking

There is no Spring.before_fork callback. To run something before the fork, you can place it in ~/.spring.rb or config/spring.rb or in any of the files which get run when your application initializes, such as config/application.rb, config/environments/*.rb or config/initializers/*.rb.

Running code after forking

You might want to run code after Spring forked off the process but before the actual command is run. You might want to use an after_fork callback if you have to connect to an external service, do some general cleanup or set up dynamic configuration.

Spring.after_fork do
  # run arbitrary code
end

If you want to register multiple callbacks you can simply call Spring.after_fork multiple times with different blocks.

Watching files and directories

Spring will automatically detect file changes to any file loaded when the server boots. Changes will cause the affected environments to be restarted.

If there are additional files or directories which should trigger an application restart, you can specify them with Spring.watch:

Spring.watch "config/some_config_file.yml"

By default, Spring polls the filesystem for changes once every 0.2 seconds. This method requires zero configuration, but if you find that it’s using too much CPU, then you can use event-based file system listening by installing the spring-watcher-listen gem.

Quiet output

To disable the “Running via Spring preloader” message which is shown each time a command runs:

Spring.quiet = true

You can also set the initial state of the quiet configuration option to true by setting the SPRING_QUIET environment variable before executing Spring. This is useful if you want to set quiet mode when invoking the Spring executable in a subprocess, and cannot or prefer not to set it programmatically via the Spring.quiet option in ~/.spring.rb or the app’s config/spring.rb.

Troubleshooting

If you want to get more information about what Spring is doing, you can run Spring explicitly in a separate terminal:

$ spring server

Logging output will be printed to stdout. You can also send log output to a file with the SPRING_LOG environment variable.


Articles

  • coming soon...