spring 0.9.1 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +2 -0
- data/CHANGELOG.md +51 -0
- data/README.md +118 -53
- data/bin/spring +1 -0
- data/lib/spring/application/boot.rb +16 -0
- data/lib/spring/application.rb +131 -64
- data/lib/spring/application_manager.rb +51 -40
- data/lib/spring/binstub.rb +13 -0
- data/lib/spring/boot.rb +8 -0
- data/lib/spring/client/binstub.rb +150 -39
- data/lib/spring/client/help.rb +4 -12
- data/lib/spring/client/rails.rb +1 -3
- data/lib/spring/client/version.rb +11 -0
- data/lib/spring/client.rb +8 -5
- data/lib/spring/command_wrapper.rb +86 -0
- data/lib/spring/commands/rails.rb +1 -2
- data/lib/spring/commands/rake.rb +0 -4
- data/lib/spring/commands.rb +3 -2
- data/lib/spring/configuration.rb +11 -2
- data/lib/spring/env.rb +24 -22
- data/lib/spring/errors.rb +0 -14
- data/lib/spring/server.rb +10 -34
- data/lib/spring/version.rb +1 -1
- data/lib/spring/watcher/abstract.rb +7 -8
- data/spring.gemspec +2 -1
- data/test/acceptance/app_test.rb +178 -345
- data/test/acceptance/helper.rb +329 -0
- data/test/unit/client/version_test.rb +14 -0
- data/test/unit/watcher_test.rb +12 -8
- metadata +14 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a2357648d0fc0f884098f352ab693e66556de6df
|
|
4
|
+
data.tar.gz: df070385ccdf9fbbb6d48770f21886dbab93516e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: cc65ad0c92b3a134505805c9b8ed999029b49030ab0ca8e46473c47c18a6ff17804de47897a7493e28765dc3fa6dc18df2de5beb4be7b8f6bdf04bd979d2a7da
|
|
7
|
+
data.tar.gz: 1b408b559590cf9a1b13da2d390d191964809107edc00c4b67e6c54384d05df1a5f9673fcc796318e1962c1f3c4e9fd49141596a3692c80b831d9cc933dd67c5
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,54 @@
|
|
|
1
|
+
## 1.1.0
|
|
2
|
+
|
|
3
|
+
* A `bin/spring` binstub is now generated. This allows us to load spring
|
|
4
|
+
correctly if you have it installed locally with a `BUNDLE_PATH`, so
|
|
5
|
+
it's no longer necessary to install spring system-wide. We also
|
|
6
|
+
activate the correct version from your Gemfile.lock. Note that you
|
|
7
|
+
still can't have spring in your Gemfile as a git repository or local
|
|
8
|
+
path; it must be a proper gem.
|
|
9
|
+
* Various changes to how springified binstubs are implemented. Existing
|
|
10
|
+
binstubs will continue to work, but it's recommended to run `spring binstub`
|
|
11
|
+
again to upgrade them to the new format.
|
|
12
|
+
* `spring binstub --remove` option added for removing spring from
|
|
13
|
+
binstubs. This won't work unless you have upgraded your binstubs to
|
|
14
|
+
the new format.
|
|
15
|
+
* `config/database.yml` is watched
|
|
16
|
+
* Better application restarts - if you introduce an error, for example
|
|
17
|
+
by editing `config/application.rb`, spring will now continue to watch
|
|
18
|
+
your files and will immediately try to restart the application when
|
|
19
|
+
you edit `config/application.rb` again (hopefully to correct the error).
|
|
20
|
+
This means that by the time you come to run a command the application
|
|
21
|
+
may well already be running.
|
|
22
|
+
* Gemfile changes are now gracefully handled. Previously they would
|
|
23
|
+
cause spring to simply quit, meaning that you'd incur the full startup
|
|
24
|
+
penalty on the next run. Now spring doesn't quit, and will try to load
|
|
25
|
+
up your new bundle in the background.
|
|
26
|
+
* Fix support for using spring with Rails engines/plugins
|
|
27
|
+
|
|
28
|
+
## 1.0.0
|
|
29
|
+
|
|
30
|
+
* Enterprise ready secret sauce added
|
|
31
|
+
|
|
32
|
+
## 0.9.2
|
|
33
|
+
|
|
34
|
+
* Bugfix: environment variables set by bundler (`BUNDLE_GEMFILE`,
|
|
35
|
+
`RUBYOPT`, etc...) were being removed from the environment.
|
|
36
|
+
* Ensure we only run the code reloader when files have actually changed.
|
|
37
|
+
This issue became more prominent with Rails 4, since Rails 4 will now
|
|
38
|
+
reload routes whenever the code is reloaded (see
|
|
39
|
+
https://github.com/rails/rails/commit/b9b06daa915fdc4d11e8cfe11a7175e5cd8f104f).
|
|
40
|
+
* Allow spring to be used in a descendant directory of the application
|
|
41
|
+
root
|
|
42
|
+
* Use the system tmpdir for our temporary files. Previously we used
|
|
43
|
+
`APP_ROOT/tmp/spring`, which caused problems on filesystems which did
|
|
44
|
+
not support sockets, and also caused problems if `APP_ROOT` was
|
|
45
|
+
sufficiently deep in the filesystem to exhaust the operating system's
|
|
46
|
+
socket name limit. Hence we had a `SPRING_TMP_PATH` environment
|
|
47
|
+
variable for configuration. We now use `/tmp/spring/[md5(APP_ROOT)]`
|
|
48
|
+
for the socket and `/tmp/spring/[md5(APP_ROOT)].pid` for the pid file.
|
|
49
|
+
Thanks @Kriechi for the suggestion. Setting `SPRING_TMP_PATH` no longer
|
|
50
|
+
has any effect.
|
|
51
|
+
|
|
1
52
|
## 0.9.1
|
|
2
53
|
|
|
3
54
|
* Environment variables which were created during application startup are no
|
data/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Spring
|
|
2
2
|
|
|
3
|
-
[](https://travis-ci.org/rails/spring)
|
|
4
|
+
[](http://badge.fury.io/rb/spring)
|
|
4
5
|
|
|
5
6
|
Spring is a Rails application preloader. It speeds up development by
|
|
6
7
|
keeping your application running in the background so you don't need to
|
|
@@ -15,24 +16,50 @@ boot it every time you run a test, rake task or migration.
|
|
|
15
16
|
|
|
16
17
|
## Compatibility
|
|
17
18
|
|
|
18
|
-
* Ruby versions: MRI 1.9.3, MRI 2.0.
|
|
19
|
+
* Ruby versions: MRI 1.9.3, MRI 2.0, MRI 2.1
|
|
19
20
|
* Rails versions: 3.2, 4.0
|
|
20
21
|
|
|
21
|
-
Spring makes extensive use of `Process
|
|
22
|
+
Spring makes extensive use of `Process.fork`, so won't be able to
|
|
22
23
|
provide a speed up on platforms which don't support forking (Windows, JRuby).
|
|
23
24
|
|
|
24
25
|
## Walkthrough
|
|
25
26
|
|
|
26
|
-
|
|
27
|
+
### Setup
|
|
28
|
+
|
|
29
|
+
Add spring to your Gemfile:
|
|
30
|
+
|
|
31
|
+
``` ruby
|
|
32
|
+
gem "spring", group: :development
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
It's recommended to 'springify' the executables in your `bin/`
|
|
36
|
+
directory:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
$ bundle install
|
|
40
|
+
$ bundle exec spring binstub --all
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
This generates a `bin/spring` executable, and inserts a small snippet of
|
|
44
|
+
code into relevant existing executables. The snippet looks like this:
|
|
27
45
|
|
|
28
46
|
``` ruby
|
|
29
|
-
|
|
30
|
-
|
|
47
|
+
begin
|
|
48
|
+
load File.expand_path("../spring", __FILE__)
|
|
49
|
+
rescue LoadError
|
|
31
50
|
end
|
|
32
51
|
```
|
|
33
52
|
|
|
34
|
-
|
|
35
|
-
|
|
53
|
+
On platforms where spring is installed and supported, this snippet
|
|
54
|
+
hooks spring into the execution of commands. In other cases, the snippet
|
|
55
|
+
will just be silently ignored and the lines after it will be executed as
|
|
56
|
+
normal.
|
|
57
|
+
|
|
58
|
+
If you don't want to prefix every command you type with `bin/`, you
|
|
59
|
+
can [use direnv](https://github.com/zimbatm/direnv) to automatically add
|
|
60
|
+
`./bin` to your `PATH` when you `cd` into your application.
|
|
61
|
+
|
|
62
|
+
### Usage
|
|
36
63
|
|
|
37
64
|
For this walkthrough I've generated a new Rails application, and run
|
|
38
65
|
`rails generate scaffold posts name:string`.
|
|
@@ -40,7 +67,7 @@ For this walkthrough I've generated a new Rails application, and run
|
|
|
40
67
|
Let's run a test:
|
|
41
68
|
|
|
42
69
|
```
|
|
43
|
-
$ time
|
|
70
|
+
$ time bin/rake test test/functional/posts_controller_test.rb
|
|
44
71
|
Run options:
|
|
45
72
|
|
|
46
73
|
# Running tests:
|
|
@@ -60,7 +87,7 @@ That wasn't particularly fast because it was the first run, so spring
|
|
|
60
87
|
had to boot the application. It's now running:
|
|
61
88
|
|
|
62
89
|
```
|
|
63
|
-
$ spring status
|
|
90
|
+
$ bin/spring status
|
|
64
91
|
Spring is running:
|
|
65
92
|
|
|
66
93
|
26150 spring server | spring-demo-app | started 3 secs ago
|
|
@@ -70,7 +97,7 @@ Spring is running:
|
|
|
70
97
|
The next run is faster:
|
|
71
98
|
|
|
72
99
|
```
|
|
73
|
-
$ time
|
|
100
|
+
$ time bin/rake test test/functional/posts_controller_test.rb
|
|
74
101
|
Run options:
|
|
75
102
|
|
|
76
103
|
# Running tests:
|
|
@@ -86,20 +113,6 @@ user 0m0.276s
|
|
|
86
113
|
sys 0m0.059s
|
|
87
114
|
```
|
|
88
115
|
|
|
89
|
-
Writing `spring` before every command gets a bit tedious. Spring binstubs solve this:
|
|
90
|
-
|
|
91
|
-
```
|
|
92
|
-
$ spring binstub rake rails
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
This will generate `bin/rake` and `bin/rails`. They
|
|
96
|
-
replace any binstubs that you might already have in your `bin/`
|
|
97
|
-
directory. Check them in to source control.
|
|
98
|
-
|
|
99
|
-
If you don't want to prefix every command you type with `bin/`, you
|
|
100
|
-
can [use direnv](https://github.com/zimbatm/direnv) to automatically add
|
|
101
|
-
`./bin` to your `PATH` when you `cd` into your application.
|
|
102
|
-
|
|
103
116
|
If we edit any of the application files, or test files, the changes will
|
|
104
117
|
be picked up on the next run without the background process having to
|
|
105
118
|
restart. This works in exactly the same way as the code reloading
|
|
@@ -114,7 +127,7 @@ Let's "edit" `config/application.rb`:
|
|
|
114
127
|
|
|
115
128
|
```
|
|
116
129
|
$ touch config/application.rb
|
|
117
|
-
$ spring status
|
|
130
|
+
$ bin/spring status
|
|
118
131
|
Spring is running:
|
|
119
132
|
|
|
120
133
|
26150 spring server | spring-demo-app | started 36 secs ago
|
|
@@ -137,7 +150,7 @@ edit_post GET /posts/:id/edit(.:format) posts#edit
|
|
|
137
150
|
PUT /posts/:id(.:format) posts#update
|
|
138
151
|
DELETE /posts/:id(.:format) posts#destroy
|
|
139
152
|
|
|
140
|
-
$ spring status
|
|
153
|
+
$ bin/spring status
|
|
141
154
|
Spring is running:
|
|
142
155
|
|
|
143
156
|
26150 spring server | spring-demo-app | started 1 min ago
|
|
@@ -150,25 +163,18 @@ when you close your terminal. However if you do want to do a manual shut
|
|
|
150
163
|
down, use the `stop` command:
|
|
151
164
|
|
|
152
165
|
```
|
|
153
|
-
$ spring stop
|
|
166
|
+
$ bin/spring stop
|
|
154
167
|
Spring stopped.
|
|
155
168
|
```
|
|
156
169
|
|
|
157
|
-
|
|
170
|
+
### Removal
|
|
158
171
|
|
|
159
|
-
|
|
172
|
+
To remove spring:
|
|
160
173
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
for examples.
|
|
174
|
+
* 'Unspring' your bin/ executables: `bin/spring binstub --remove --all`
|
|
175
|
+
* Remove spring from your Gemfile
|
|
164
176
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
* [spring-commands-rspec](https://github.com/jonleighton/spring-commands-rspec)
|
|
168
|
-
* [spring-commands-cucumber](https://github.com/jonleighton/spring-commands-cucumber)
|
|
169
|
-
* [spring-commands-testunit](https://github.com/jonleighton/spring-commands-testunit) - useful for
|
|
170
|
-
running `Test::Unit` tests on Rails 3, since only Rails 4 allows you
|
|
171
|
-
to use `rake test path/to/test` to run a particular test/directory.
|
|
177
|
+
## Commands
|
|
172
178
|
|
|
173
179
|
### `rake`
|
|
174
180
|
|
|
@@ -194,13 +200,82 @@ a different sub command (e.g. `rails server`) then spring will automatically
|
|
|
194
200
|
pass it through to the underlying `rails` executable (without the
|
|
195
201
|
speed-up).
|
|
196
202
|
|
|
203
|
+
### Additional commands
|
|
204
|
+
|
|
205
|
+
You can add these to your Gemfile for additional commands (run `spring stop` afterwards
|
|
206
|
+
to pick up the changes):
|
|
207
|
+
|
|
208
|
+
* [spring-commands-rspec](https://github.com/jonleighton/spring-commands-rspec)
|
|
209
|
+
* [spring-commands-cucumber](https://github.com/jonleighton/spring-commands-cucumber)
|
|
210
|
+
* [spring-commands-testunit](https://github.com/jonleighton/spring-commands-testunit) - useful for
|
|
211
|
+
running `Test::Unit` tests on Rails 3, since only Rails 4 allows you
|
|
212
|
+
to use `rake test path/to/test` to run a particular test/directory.
|
|
213
|
+
|
|
214
|
+
## Use without adding to bundle
|
|
215
|
+
|
|
216
|
+
If you don't want spring-related code checked into your source
|
|
217
|
+
repository, it's possible to use spring without adding to your Gemfile.
|
|
218
|
+
However, using spring binstubs without adding spring to the Gemfile is not
|
|
219
|
+
supported.
|
|
220
|
+
|
|
221
|
+
To use spring like this, do a `gem install spring` and then prefix
|
|
222
|
+
commands with `spring`. For example, rather than running `bin/rake -T`,
|
|
223
|
+
you'd run `spring rake -T`.
|
|
224
|
+
|
|
225
|
+
## Temporarily disabling Spring
|
|
226
|
+
|
|
227
|
+
If you're using Spring binstubs, but temporarily don't want commands to
|
|
228
|
+
run through Spring, set the `DISABLE_SPRING` environment variable.
|
|
229
|
+
|
|
230
|
+
## Class reloading
|
|
231
|
+
|
|
232
|
+
Spring uses Rails' class reloading mechanism
|
|
233
|
+
(`ActiveSupport::Dependencies`) to keep your code up to date between
|
|
234
|
+
test runs. This is the same mechanism which allows you to see changes
|
|
235
|
+
during development when you refresh the page. However, you may never
|
|
236
|
+
have used this mechanism with your `test` environment before, and this
|
|
237
|
+
can cause problems.
|
|
238
|
+
|
|
239
|
+
It's important to realise that code reloading means that the constants
|
|
240
|
+
in your application are *different objects* after files have changed:
|
|
241
|
+
|
|
242
|
+
```
|
|
243
|
+
$ bin/rails runner 'puts User.object_id'
|
|
244
|
+
70127987886040
|
|
245
|
+
$ touch app/models/user.rb
|
|
246
|
+
$ bin/rails runner 'puts User.object_id'
|
|
247
|
+
70127976764620
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Suppose you have an initializer `config/initializers/save_user_class.rb`
|
|
251
|
+
like so:
|
|
252
|
+
|
|
253
|
+
``` ruby
|
|
254
|
+
USER_CLASS = User
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
This saves off the *first* version of the `User` class, which will not
|
|
258
|
+
be the same object as `User` after the code has been reloaded:
|
|
259
|
+
|
|
260
|
+
```
|
|
261
|
+
$ bin/rails runner 'puts User == USER_CLASS'
|
|
262
|
+
true
|
|
263
|
+
$ touch app/models/user.rb
|
|
264
|
+
$ bin/rails runner 'puts User == USER_CLASS'
|
|
265
|
+
false
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
So to avoid this problem, don't save off references to application
|
|
269
|
+
constants in your initialization code.
|
|
270
|
+
|
|
197
271
|
## Configuration
|
|
198
272
|
|
|
199
273
|
Spring will read `~/.spring.rb` and `config/spring.rb` for custom
|
|
200
274
|
settings. Note that `~/.spring.rb` is loaded *before* bundler, but
|
|
201
|
-
`config/spring.rb` is loaded *after* bundler.
|
|
202
|
-
|
|
203
|
-
to be
|
|
275
|
+
`config/spring.rb` is loaded *after* bundler. So if you have any
|
|
276
|
+
`spring-commands-*` gems installed that you want to be available in all
|
|
277
|
+
projects without having to be added to the project's Gemfile, require
|
|
278
|
+
them in your `~/.spring.rb`.
|
|
204
279
|
|
|
205
280
|
### Application root
|
|
206
281
|
|
|
@@ -221,11 +296,6 @@ which get run when your application initializers, such as
|
|
|
221
296
|
`config/application.rb`, `config/environments/*.rb` or
|
|
222
297
|
`config/initializers/*.rb`.
|
|
223
298
|
|
|
224
|
-
For example, if loading your test helper is slow, you might like to
|
|
225
|
-
preload it to speed up your test runs. To do this you could put a
|
|
226
|
-
`require Rails.root.join("test/helper")` in
|
|
227
|
-
`config/environments/test.rb`.
|
|
228
|
-
|
|
229
299
|
### Running code after forking
|
|
230
300
|
|
|
231
301
|
You might want to run code after Spring forked off the process but
|
|
@@ -264,11 +334,6 @@ Spring.watch_method = :listen
|
|
|
264
334
|
|
|
265
335
|
You may need to add the [`listen` gem](https://github.com/guard/listen) to your `Gemfile`.
|
|
266
336
|
|
|
267
|
-
### tmp directory
|
|
268
|
-
|
|
269
|
-
Spring needs a tmp directory. This will default to `Rails.root.join('tmp', 'spring')`.
|
|
270
|
-
You can set your own configuration directory by setting the `SPRING_TMP_PATH` environment variable.
|
|
271
|
-
|
|
272
337
|
## Troubleshooting
|
|
273
338
|
|
|
274
339
|
If you want to get more information about what spring is doing, you can
|
data/bin/spring
CHANGED
|
@@ -21,6 +21,7 @@ if defined?(Gem)
|
|
|
21
21
|
|
|
22
22
|
if unstubbed.any?
|
|
23
23
|
warn "Warning: Running `gem pristine --all` to regenerate your installed gemspecs " \
|
|
24
|
+
"(and deleting then reinstalling your bundle if you use bundle --path) " \
|
|
24
25
|
"will improve the startup performance of Spring."
|
|
25
26
|
end
|
|
26
27
|
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require "spring/application"
|
|
2
|
+
|
|
3
|
+
app = Spring::Application.new(
|
|
4
|
+
UNIXSocket.for_fd(3),
|
|
5
|
+
Spring::JSON.load(ENV.delete("SPRING_ORIGINAL_ENV").dup)
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
Signal.trap("TERM") { app.terminate }
|
|
9
|
+
Signal.trap("TTOU", "IGNORE")
|
|
10
|
+
|
|
11
|
+
Spring::ProcessTitleUpdater.run { |distance|
|
|
12
|
+
"spring app | #{app.app_name} | started #{distance} ago | #{app.app_env} mode"
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
app.preload if ENV.delete("SPRING_PRELOAD") == "1"
|
|
16
|
+
app.run
|
data/lib/spring/application.rb
CHANGED
|
@@ -1,42 +1,83 @@
|
|
|
1
|
+
require "spring/boot"
|
|
1
2
|
require "set"
|
|
2
|
-
require "spring/watcher"
|
|
3
|
-
require "thread"
|
|
4
3
|
|
|
5
4
|
module Spring
|
|
6
5
|
class Application
|
|
7
|
-
attr_reader :manager, :watcher, :spring_env
|
|
6
|
+
attr_reader :manager, :watcher, :spring_env, :original_env
|
|
7
|
+
|
|
8
|
+
def initialize(manager, original_env)
|
|
9
|
+
@manager = manager
|
|
10
|
+
@original_env = original_env
|
|
11
|
+
@spring_env = Env.new
|
|
12
|
+
@mutex = Mutex.new
|
|
13
|
+
@waiting = 0
|
|
14
|
+
@preloaded = false
|
|
15
|
+
@state = :initialized
|
|
16
|
+
@interrupt = IO.pipe
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def state(val)
|
|
20
|
+
return if exiting?
|
|
21
|
+
log "#{@state} -> #{val}"
|
|
22
|
+
@state = val
|
|
23
|
+
end
|
|
8
24
|
|
|
9
|
-
def
|
|
10
|
-
|
|
11
|
-
@
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
@exiting = false
|
|
18
|
-
@env_keys = ENV.keys
|
|
25
|
+
def state!(val)
|
|
26
|
+
state val
|
|
27
|
+
@interrupt.last.write "."
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def app_env
|
|
31
|
+
ENV['RAILS_ENV']
|
|
32
|
+
end
|
|
19
33
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
@fds = [manager, watcher.to_io]
|
|
34
|
+
def app_name
|
|
35
|
+
spring_env.app_name
|
|
23
36
|
end
|
|
24
37
|
|
|
25
38
|
def log(message)
|
|
26
|
-
spring_env.log "[application:#{
|
|
39
|
+
spring_env.log "[application:#{app_env}] #{message}"
|
|
27
40
|
end
|
|
28
41
|
|
|
29
42
|
def preloaded?
|
|
30
43
|
@preloaded
|
|
31
44
|
end
|
|
32
45
|
|
|
46
|
+
def preload_failed?
|
|
47
|
+
@preloaded == :failure
|
|
48
|
+
end
|
|
49
|
+
|
|
33
50
|
def exiting?
|
|
34
|
-
@exiting
|
|
51
|
+
@state == :exiting
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def terminating?
|
|
55
|
+
@state == :terminating
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def watcher_stale?
|
|
59
|
+
@state == :watcher_stale
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def initialized?
|
|
63
|
+
@state == :initialized
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def start_watcher
|
|
67
|
+
@watcher = Spring.watcher
|
|
68
|
+
@watcher.on_stale { state! :watcher_stale }
|
|
69
|
+
@watcher.start
|
|
35
70
|
end
|
|
36
71
|
|
|
37
72
|
def preload
|
|
38
73
|
log "preloading app"
|
|
39
74
|
|
|
75
|
+
begin
|
|
76
|
+
require "spring/commands"
|
|
77
|
+
ensure
|
|
78
|
+
start_watcher
|
|
79
|
+
end
|
|
80
|
+
|
|
40
81
|
require Spring.application_root_path.join("config", "application")
|
|
41
82
|
|
|
42
83
|
# config/environments/test.rb will have config.cache_classes = true. However
|
|
@@ -52,44 +93,42 @@ module Spring
|
|
|
52
93
|
Rails.application.config.cache_classes = false
|
|
53
94
|
disconnect_database
|
|
54
95
|
|
|
96
|
+
@preloaded = :success
|
|
97
|
+
rescue Exception => e
|
|
98
|
+
@preloaded = :failure
|
|
99
|
+
watcher.add e.backtrace.map { |line| line.match(/^(.*)\:\d+\:in /)[1] }
|
|
100
|
+
raise e unless initialized?
|
|
101
|
+
ensure
|
|
55
102
|
watcher.add loaded_application_features
|
|
56
103
|
watcher.add Spring.gemfile, "#{Spring.gemfile}.lock"
|
|
57
|
-
watcher.add Rails.application.paths["config/initializers"]
|
|
58
104
|
|
|
59
|
-
|
|
105
|
+
if defined?(Rails)
|
|
106
|
+
watcher.add Rails.application.paths["config/initializers"]
|
|
107
|
+
watcher.add Rails.application.paths["config/database"]
|
|
108
|
+
end
|
|
60
109
|
end
|
|
61
110
|
|
|
62
111
|
def run
|
|
63
|
-
|
|
64
|
-
|
|
112
|
+
state :running
|
|
113
|
+
manager.puts
|
|
65
114
|
|
|
66
115
|
loop do
|
|
67
|
-
IO.select
|
|
68
|
-
|
|
69
|
-
if
|
|
70
|
-
|
|
71
|
-
manager.close
|
|
72
|
-
@exiting = true
|
|
73
|
-
try_exit
|
|
74
|
-
sleep
|
|
116
|
+
IO.select [manager, @interrupt.first]
|
|
117
|
+
|
|
118
|
+
if terminating? || watcher_stale? || preload_failed?
|
|
119
|
+
exit
|
|
75
120
|
else
|
|
76
121
|
serve manager.recv_io(UNIXSocket)
|
|
77
122
|
end
|
|
78
123
|
end
|
|
79
124
|
end
|
|
80
125
|
|
|
81
|
-
def try_exit
|
|
82
|
-
@mutex.synchronize {
|
|
83
|
-
exit if exiting? && @waiting == 0
|
|
84
|
-
}
|
|
85
|
-
end
|
|
86
|
-
|
|
87
126
|
def serve(client)
|
|
88
127
|
log "got client"
|
|
89
128
|
manager.puts
|
|
90
129
|
|
|
91
|
-
streams = 3.times.map { client.recv_io }
|
|
92
|
-
[STDOUT, STDERR].zip(
|
|
130
|
+
stdout, stderr, stdin = streams = 3.times.map { client.recv_io }
|
|
131
|
+
[STDOUT, STDERR].zip([stdout, stderr]).each { |a, b| a.reopen(b) }
|
|
93
132
|
|
|
94
133
|
preload unless preloaded?
|
|
95
134
|
|
|
@@ -99,17 +138,29 @@ module Spring
|
|
|
99
138
|
connect_database
|
|
100
139
|
setup command
|
|
101
140
|
|
|
102
|
-
|
|
103
|
-
|
|
141
|
+
if Rails.application.reloaders.any?(&:updated?)
|
|
142
|
+
ActionDispatch::Reloader.cleanup!
|
|
143
|
+
ActionDispatch::Reloader.prepare!
|
|
144
|
+
end
|
|
104
145
|
|
|
105
146
|
pid = fork {
|
|
106
147
|
Process.setsid
|
|
107
|
-
STDIN.reopen(
|
|
148
|
+
STDIN.reopen(stdin)
|
|
108
149
|
IGNORE_SIGNALS.each { |sig| trap(sig, "DEFAULT") }
|
|
150
|
+
trap("TERM", "DEFAULT")
|
|
109
151
|
|
|
110
152
|
ARGV.replace(args)
|
|
111
|
-
|
|
112
|
-
|
|
153
|
+
$0 = command.process_title
|
|
154
|
+
|
|
155
|
+
# Delete all env vars which are unchanged from before spring started
|
|
156
|
+
original_env.each { |k, v| ENV.delete k if ENV[k] == v }
|
|
157
|
+
|
|
158
|
+
# Load in the current env vars, except those which *were* changed when spring started
|
|
159
|
+
env.each { |k, v| ENV[k] ||= v }
|
|
160
|
+
|
|
161
|
+
# requiring is faster, and we don't need constant reloading in this process
|
|
162
|
+
ActiveSupport::Dependencies.mechanism = :require
|
|
163
|
+
Rails.application.config.cache_classes = true
|
|
113
164
|
|
|
114
165
|
connect_database
|
|
115
166
|
srand
|
|
@@ -117,16 +168,7 @@ module Spring
|
|
|
117
168
|
invoke_after_fork_callbacks
|
|
118
169
|
shush_backtraces
|
|
119
170
|
|
|
120
|
-
|
|
121
|
-
command.call
|
|
122
|
-
else
|
|
123
|
-
exec_name = command.exec_name
|
|
124
|
-
gem_name = command.gem_name if command.respond_to?(:gem_name)
|
|
125
|
-
|
|
126
|
-
exec = Gem.bin_path(gem_name || exec_name, exec_name)
|
|
127
|
-
$0 = exec
|
|
128
|
-
load exec
|
|
129
|
-
end
|
|
171
|
+
command.call
|
|
130
172
|
}
|
|
131
173
|
|
|
132
174
|
disconnect_database
|
|
@@ -147,26 +189,44 @@ module Spring
|
|
|
147
189
|
client.close
|
|
148
190
|
|
|
149
191
|
@mutex.synchronize { @waiting -= 1 }
|
|
150
|
-
|
|
192
|
+
exit_if_finished
|
|
151
193
|
}
|
|
152
194
|
|
|
153
|
-
rescue => e
|
|
195
|
+
rescue Exception => e
|
|
154
196
|
log "exception: #{e}"
|
|
155
|
-
|
|
156
|
-
|
|
197
|
+
manager.puts unless pid
|
|
198
|
+
|
|
199
|
+
if streams && !e.is_a?(SystemExit)
|
|
200
|
+
print_exception(stderr, e)
|
|
201
|
+
streams.each(&:close)
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
client.puts(1) if pid
|
|
157
205
|
client.close
|
|
158
|
-
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
def terminate
|
|
209
|
+
state! :terminating
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
def exit
|
|
213
|
+
state :exiting
|
|
214
|
+
manager.shutdown(:RDWR)
|
|
215
|
+
exit_if_finished
|
|
216
|
+
sleep
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def exit_if_finished
|
|
220
|
+
@mutex.synchronize {
|
|
221
|
+
Kernel.exit if exiting? && @waiting == 0
|
|
222
|
+
}
|
|
159
223
|
end
|
|
160
224
|
|
|
161
225
|
# The command might need to require some files in the
|
|
162
226
|
# main process so that they are cached. For example a test command wants to
|
|
163
227
|
# load the helper file once and have it cached.
|
|
164
228
|
def setup(command)
|
|
165
|
-
|
|
166
|
-
@setup << command.class
|
|
167
|
-
|
|
168
|
-
if command.respond_to?(:setup)
|
|
169
|
-
command.setup
|
|
229
|
+
if command.setup
|
|
170
230
|
watcher.add loaded_application_features # loaded features may have changed
|
|
171
231
|
end
|
|
172
232
|
end
|
|
@@ -178,7 +238,8 @@ module Spring
|
|
|
178
238
|
end
|
|
179
239
|
|
|
180
240
|
def loaded_application_features
|
|
181
|
-
|
|
241
|
+
root = Spring.application_root_path.to_s
|
|
242
|
+
$LOADED_FEATURES.select { |f| f.start_with?(root) }
|
|
182
243
|
end
|
|
183
244
|
|
|
184
245
|
def disconnect_database
|
|
@@ -206,5 +267,11 @@ module Spring
|
|
|
206
267
|
end
|
|
207
268
|
end
|
|
208
269
|
end
|
|
270
|
+
|
|
271
|
+
def print_exception(stream, error)
|
|
272
|
+
first, rest = error.backtrace.first, error.backtrace.drop(1)
|
|
273
|
+
stream.puts("#{first}: #{error} (#{error.class})")
|
|
274
|
+
rest.each { |line| stream.puts("\tfrom #{line}") }
|
|
275
|
+
end
|
|
209
276
|
end
|
|
210
277
|
end
|