No commit activity in last 3 years
No release in over 3 years
Forked from 'spring' gem, it preloads your application so commands run faster
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
 Dependencies

Development

>= 0
>= 0
 Project Readme

SpringStandalone

Gem Version

IMPORTANT: The gem is functional and can be used with non rails projects, however, it is an experiment and I haven't updated neither the tests to drop dependency on rails command nor this README.md file to describe all it's functionalities, so whenever it refers to bin/spring replace it with bin/spring_sa and so on as well as ignore the plugins as those are not yet supported given it's usage on the SpringStandalone.register_command so keep that in mind while reading this README.

SpringStandalone Standalone is a ruby application preloader based on SpringStandalone, 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 your application commands.

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

Compatibility

  • Ruby versions: MRI 2.5, MRI 2.6

SpringStandalone Standalone makes extensive use of Process.fork (inherited from SpringStandalone gem), so won't be able to provide a speed up on platforms which don't support forking (Windows, JRuby).

Walkthrough

Setup

Add SpringStandalone to your Gemfile:

gem "spring_standalone", group: :development

(Note: using gem "spring_standalone", git: "..." won't work and is not a supported way of using SpringStandalone.)

It's recommended to 'springify' the executables in your bin/ directory:

$ bundle install
$ bundle exec spring_sa binstub --all

This generates a bin/spring_sa executable, and inserts a small snippet of code into relevant existing executables. The snippet looks like this:

begin
  load File.expand_path('../spring_sa', __FILE__)
rescue LoadError
end

On platforms where SpringStandalone Standalone is installed and supported, this snippet hooks SpringStandalone into the execution of commands. In other cases, the snippet will just be silently ignored and the lines after it will be executed as normal.

If you don't want to prefix every command you type with bin/, you can use direnv to automatically add ./bin to your PATH when you cd into your application. Simply create an .envrc file with the command PATH_add bin in your Rails directory.

Usage

For this walkthrough I've generated a new Rails application, and run rails generate scaffold post name:string.

Let's run a test:

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

# 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 SpringStandalone had to boot the application. It's now running:

$ bin/spring status
SpringStandalone 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 SpringStandalone 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
SpringStandalone 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 SpringStandalone 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
SpringStandalone 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" SpringStandalone. 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
SpringStandalone stopped.

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

Removal

To remove SpringStandalone:

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

Deployment

You must not install SpringStandalone on your production environment. To prevent it from being installed, provide the --without development test argument to the bundle install command which is used to install gems on your production machines:

$ bundle install --without development test

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

SpringStandalone::Commands::Rake.environment_matchers["perf_test"] = "test"
SpringStandalone::Commands::Rake.environment_matchers[/^perf/]     = "test"

# To change the environment when you run `rake` with no arguments
SpringStandalone::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 SpringStandalone 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:

Use without adding to bundle

If you don't want SpringStandalone-related code checked into your source repository, it's possible to use SpringStandalone without adding to your Gemfile. However, using SpringStandalone binstubs without adding SpringStandalone to the Gemfile is not supported.

To use SpringStandalone like this, do a gem install spring and then prefix commands with spring. For example, rather than running bin/rake -T, you'd run spring rake -T.

Temporarily disabling SpringStandalone

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

Class reloading

SpringStandalone uses Rails' class reloading mechanism (ActiveSupport::Dependencies) 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.

Using SpringStandalone with a containerized development environment

As of SpringStandalone 1.7, there is some support for doing this. See this example repository for information about how to do it with Docker.

Configuration

SpringStandalone 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

SpringStandalone 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 SpringStandalone where your app is located:

SpringStandalone.application_root = './test/dummy'

Running code before forking

There is no SpringStandalone.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 SpringStandalone 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.

SpringStandalone.after_fork do
  # run arbitrary code
end

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

Watching files and directories

SpringStandalone 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 SpringStandalone.watch:

SpringStandalone.watch "config/some_config_file.yml"

By default SpringStandalone 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 SpringStandalone preloader" message which is shown each time a command runs:

SpringStandalone.quiet = true

Environment variables

The following environment variables are used by SpringStandalone:

  • DISABLE_SPRING - If set, SpringStandalone will be bypassed and your application will boot in a foreground process
  • SPRING_LOG - The path to a file which SpringStandalone will write log messages to.
  • SPRING_TMP_PATH - The directory where SpringStandalone should write its temporary files (a pidfile and a socket). By default we use the XDG_RUNTIME_DIR environment variable, or else Dir.tmpdir, and then create a directory in that named spring-$UID. We don't use your Rails application's tmp/ directory because that may be on a filesystem which doesn't support UNIX sockets.
  • SPRING_APPLICATION_ID - Used to identify distinct Rails applications. By default it is an MD5 hash of the current RUBY_VERSION, and the path to your Rails project root.
  • SPRING_SOCKET - The path which should be used for the UNIX socket which SpringStandalone uses to communicate with the long-running SpringStandalone server process. By default this is SPRING_TMP_PATH/SPRING_APPLICATION_ID.
  • SPRING_PIDFILE - The path which should be used to store the pid of the long-running SpringStandalone server process. By default this is related to the socket path; if the socket path is /foo/bar/spring.sock the pidfile will be /foo/bar/spring.pid.
  • SPRING_SERVER_COMMAND - The command to run to start up the SpringStandalone server when it is not already running. Defaults to spring _[version]_ server --background.

Troubleshooting

If you want to get more information about what SpringStandalone is doing, you can run SpringStandalone 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.