spring 1.0.0 → 1.1.1
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 +36 -0
- data/CONTRIBUTING.md +32 -0
- data/README.md +116 -45
- data/bin/spring +1 -0
- data/lib/spring/application/boot.rb +18 -0
- data/lib/spring/application.rb +140 -62
- 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 +10 -9
- data/lib/spring/server.rb +8 -36
- data/lib/spring/version.rb +1 -1
- data/lib/spring/watcher/abstract.rb +7 -8
- data/spring.gemspec +1 -1
- data/test/acceptance/app_test.rb +178 -355
- 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 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e8e020e7dc1eb3f27faa79634ad8c9ff4e9c1ddb
|
|
4
|
+
data.tar.gz: 5bd55b0c1e659701f4cd61afaba2b854070bc482
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ebecedcb2e4a616615dc33a752e54a380f685e9d43227f67a5c3a0f29ae8c500df20ab75705778e13f2efa5c52d51f4ed682472eda7b12006e3720a762232dd9
|
|
7
|
+
data.tar.gz: 349a6b90ba96409a80e90a14e5bc39d7057970d3a6a81eb8fe7e6c6b7cd48aebc7465d687ffd08b704a3977cc0c3405a72cc93521b022fd54ef7a13a154ba3e8
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,39 @@
|
|
|
1
|
+
## 1.1.1
|
|
2
|
+
|
|
3
|
+
* Fix `$0` so that it is no longer prefixed with "spring ", as doing
|
|
4
|
+
this cause issues with rspec when running just `rspec` with no
|
|
5
|
+
arguments.
|
|
6
|
+
* Ensure we're always connected to a tty when preloading the
|
|
7
|
+
application in the background, in order to avoid loading issues
|
|
8
|
+
with readline + libedit which affected pry-rails.
|
|
9
|
+
|
|
10
|
+
## 1.1.0
|
|
11
|
+
|
|
12
|
+
* A `bin/spring` binstub is now generated. This allows us to load spring
|
|
13
|
+
correctly if you have it installed locally with a `BUNDLE_PATH`, so
|
|
14
|
+
it's no longer necessary to install spring system-wide. We also
|
|
15
|
+
activate the correct version from your Gemfile.lock. Note that you
|
|
16
|
+
still can't have spring in your Gemfile as a git repository or local
|
|
17
|
+
path; it must be a proper gem.
|
|
18
|
+
* Various changes to how springified binstubs are implemented. Existing
|
|
19
|
+
binstubs will continue to work, but it's recommended to run `spring binstub`
|
|
20
|
+
again to upgrade them to the new format.
|
|
21
|
+
* `spring binstub --remove` option added for removing spring from
|
|
22
|
+
binstubs. This won't work unless you have upgraded your binstubs to
|
|
23
|
+
the new format.
|
|
24
|
+
* `config/database.yml` is watched
|
|
25
|
+
* Better application restarts - if you introduce an error, for example
|
|
26
|
+
by editing `config/application.rb`, spring will now continue to watch
|
|
27
|
+
your files and will immediately try to restart the application when
|
|
28
|
+
you edit `config/application.rb` again (hopefully to correct the error).
|
|
29
|
+
This means that by the time you come to run a command the application
|
|
30
|
+
may well already be running.
|
|
31
|
+
* Gemfile changes are now gracefully handled. Previously they would
|
|
32
|
+
cause spring to simply quit, meaning that you'd incur the full startup
|
|
33
|
+
penalty on the next run. Now spring doesn't quit, and will try to load
|
|
34
|
+
up your new bundle in the background.
|
|
35
|
+
* Fix support for using spring with Rails engines/plugins
|
|
36
|
+
|
|
1
37
|
## 1.0.0
|
|
2
38
|
|
|
3
39
|
* Enterprise ready secret sauce added
|
data/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Contributing guide
|
|
2
|
+
|
|
3
|
+
## Getting set up
|
|
4
|
+
|
|
5
|
+
Check out the code and run `bundle install` as usual.
|
|
6
|
+
|
|
7
|
+
## Running tests
|
|
8
|
+
|
|
9
|
+
Running `rake` will run all tests. There are both unit tests and
|
|
10
|
+
acceptance tests. You can run them individually with `rake test:unit` or
|
|
11
|
+
`rake test:acceptance`.
|
|
12
|
+
|
|
13
|
+
If one doesn't already exist, the acceptance tests will generate a dummy
|
|
14
|
+
Rails app in `test/apps/`. On each test run, the dummy app is copied to
|
|
15
|
+
`test/apps/tmp/` so that any changes won't affect the pre-generated app
|
|
16
|
+
(this saves us having to regenerate the app on each run).
|
|
17
|
+
|
|
18
|
+
If tests are failing and you don't know why, it might be that the
|
|
19
|
+
pre-generated app has become inconsistent in some way. In that case the
|
|
20
|
+
best solution is to purge it with `rm -rf test/apps/*` and then run the
|
|
21
|
+
acceptance tests again, which will generate a new app.
|
|
22
|
+
|
|
23
|
+
## Testing different Rails versions
|
|
24
|
+
|
|
25
|
+
You can set the `RAILS_VERSION` environment variable:
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
$ RAILS_VERSION="~> 3.2.0" rake test:acceptance
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
The apps in `test/apps` will be named based on the rails version and the
|
|
32
|
+
spring version.
|
data/README.md
CHANGED
|
@@ -1,11 +1,14 @@
|
|
|
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
|
|
7
8
|
boot it every time you run a test, rake task or migration.
|
|
8
9
|
|
|
10
|
+
**Upgrading to 1.1? It's recommended to run `bundle exec spring binstub --all` to regenerate your binstubs.**
|
|
11
|
+
|
|
9
12
|
## Features
|
|
10
13
|
|
|
11
14
|
* Totally automatic; no need to explicitly start and stop the background process
|
|
@@ -15,24 +18,50 @@ boot it every time you run a test, rake task or migration.
|
|
|
15
18
|
|
|
16
19
|
## Compatibility
|
|
17
20
|
|
|
18
|
-
* Ruby versions: MRI 1.9.3, MRI 2.0.
|
|
21
|
+
* Ruby versions: MRI 1.9.3, MRI 2.0, MRI 2.1
|
|
19
22
|
* Rails versions: 3.2, 4.0
|
|
20
23
|
|
|
21
|
-
Spring makes extensive use of `Process
|
|
24
|
+
Spring makes extensive use of `Process.fork`, so won't be able to
|
|
22
25
|
provide a speed up on platforms which don't support forking (Windows, JRuby).
|
|
23
26
|
|
|
24
27
|
## Walkthrough
|
|
25
28
|
|
|
26
|
-
|
|
29
|
+
### Setup
|
|
30
|
+
|
|
31
|
+
Add spring to your Gemfile:
|
|
32
|
+
|
|
33
|
+
``` ruby
|
|
34
|
+
gem "spring", group: :development
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
It's recommended to 'springify' the executables in your `bin/`
|
|
38
|
+
directory:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
$ bundle install
|
|
42
|
+
$ bundle exec spring binstub --all
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This generates a `bin/spring` executable, and inserts a small snippet of
|
|
46
|
+
code into relevant existing executables. The snippet looks like this:
|
|
27
47
|
|
|
28
48
|
``` ruby
|
|
29
|
-
|
|
30
|
-
|
|
49
|
+
begin
|
|
50
|
+
load File.expand_path("../spring", __FILE__)
|
|
51
|
+
rescue LoadError
|
|
31
52
|
end
|
|
32
53
|
```
|
|
33
54
|
|
|
34
|
-
|
|
35
|
-
|
|
55
|
+
On platforms where spring is installed and supported, this snippet
|
|
56
|
+
hooks spring into the execution of commands. In other cases, the snippet
|
|
57
|
+
will just be silently ignored and the lines after it will be executed as
|
|
58
|
+
normal.
|
|
59
|
+
|
|
60
|
+
If you don't want to prefix every command you type with `bin/`, you
|
|
61
|
+
can [use direnv](https://github.com/zimbatm/direnv) to automatically add
|
|
62
|
+
`./bin` to your `PATH` when you `cd` into your application.
|
|
63
|
+
|
|
64
|
+
### Usage
|
|
36
65
|
|
|
37
66
|
For this walkthrough I've generated a new Rails application, and run
|
|
38
67
|
`rails generate scaffold posts name:string`.
|
|
@@ -40,7 +69,7 @@ For this walkthrough I've generated a new Rails application, and run
|
|
|
40
69
|
Let's run a test:
|
|
41
70
|
|
|
42
71
|
```
|
|
43
|
-
$ time
|
|
72
|
+
$ time bin/rake test test/functional/posts_controller_test.rb
|
|
44
73
|
Run options:
|
|
45
74
|
|
|
46
75
|
# Running tests:
|
|
@@ -60,7 +89,7 @@ That wasn't particularly fast because it was the first run, so spring
|
|
|
60
89
|
had to boot the application. It's now running:
|
|
61
90
|
|
|
62
91
|
```
|
|
63
|
-
$ spring status
|
|
92
|
+
$ bin/spring status
|
|
64
93
|
Spring is running:
|
|
65
94
|
|
|
66
95
|
26150 spring server | spring-demo-app | started 3 secs ago
|
|
@@ -70,7 +99,7 @@ Spring is running:
|
|
|
70
99
|
The next run is faster:
|
|
71
100
|
|
|
72
101
|
```
|
|
73
|
-
$ time
|
|
102
|
+
$ time bin/rake test test/functional/posts_controller_test.rb
|
|
74
103
|
Run options:
|
|
75
104
|
|
|
76
105
|
# Running tests:
|
|
@@ -86,20 +115,6 @@ user 0m0.276s
|
|
|
86
115
|
sys 0m0.059s
|
|
87
116
|
```
|
|
88
117
|
|
|
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
118
|
If we edit any of the application files, or test files, the changes will
|
|
104
119
|
be picked up on the next run without the background process having to
|
|
105
120
|
restart. This works in exactly the same way as the code reloading
|
|
@@ -114,7 +129,7 @@ Let's "edit" `config/application.rb`:
|
|
|
114
129
|
|
|
115
130
|
```
|
|
116
131
|
$ touch config/application.rb
|
|
117
|
-
$ spring status
|
|
132
|
+
$ bin/spring status
|
|
118
133
|
Spring is running:
|
|
119
134
|
|
|
120
135
|
26150 spring server | spring-demo-app | started 36 secs ago
|
|
@@ -137,7 +152,7 @@ edit_post GET /posts/:id/edit(.:format) posts#edit
|
|
|
137
152
|
PUT /posts/:id(.:format) posts#update
|
|
138
153
|
DELETE /posts/:id(.:format) posts#destroy
|
|
139
154
|
|
|
140
|
-
$ spring status
|
|
155
|
+
$ bin/spring status
|
|
141
156
|
Spring is running:
|
|
142
157
|
|
|
143
158
|
26150 spring server | spring-demo-app | started 1 min ago
|
|
@@ -150,25 +165,18 @@ when you close your terminal. However if you do want to do a manual shut
|
|
|
150
165
|
down, use the `stop` command:
|
|
151
166
|
|
|
152
167
|
```
|
|
153
|
-
$ spring stop
|
|
168
|
+
$ bin/spring stop
|
|
154
169
|
Spring stopped.
|
|
155
170
|
```
|
|
156
171
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
The following commands are shipped by default.
|
|
172
|
+
### Removal
|
|
160
173
|
|
|
161
|
-
|
|
162
|
-
[`lib/spring/commands/`](https://github.com/jonleighton/spring/blob/master/lib/spring/commands/)
|
|
163
|
-
for examples.
|
|
174
|
+
To remove spring:
|
|
164
175
|
|
|
165
|
-
|
|
176
|
+
* 'Unspring' your bin/ executables: `bin/spring binstub --remove --all`
|
|
177
|
+
* Remove spring from your Gemfile
|
|
166
178
|
|
|
167
|
-
|
|
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.
|
|
179
|
+
## Commands
|
|
172
180
|
|
|
173
181
|
### `rake`
|
|
174
182
|
|
|
@@ -194,6 +202,74 @@ a different sub command (e.g. `rails server`) then spring will automatically
|
|
|
194
202
|
pass it through to the underlying `rails` executable (without the
|
|
195
203
|
speed-up).
|
|
196
204
|
|
|
205
|
+
### Additional commands
|
|
206
|
+
|
|
207
|
+
You can add these to your Gemfile for additional commands (run `spring stop` afterwards
|
|
208
|
+
to pick up the changes):
|
|
209
|
+
|
|
210
|
+
* [spring-commands-rspec](https://github.com/jonleighton/spring-commands-rspec)
|
|
211
|
+
* [spring-commands-cucumber](https://github.com/jonleighton/spring-commands-cucumber)
|
|
212
|
+
* [spring-commands-testunit](https://github.com/jonleighton/spring-commands-testunit) - useful for
|
|
213
|
+
running `Test::Unit` tests on Rails 3, since only Rails 4 allows you
|
|
214
|
+
to use `rake test path/to/test` to run a particular test/directory.
|
|
215
|
+
|
|
216
|
+
## Use without adding to bundle
|
|
217
|
+
|
|
218
|
+
If you don't want spring-related code checked into your source
|
|
219
|
+
repository, it's possible to use spring without adding to your Gemfile.
|
|
220
|
+
However, using spring binstubs without adding spring to the Gemfile is not
|
|
221
|
+
supported.
|
|
222
|
+
|
|
223
|
+
To use spring like this, do a `gem install spring` and then prefix
|
|
224
|
+
commands with `spring`. For example, rather than running `bin/rake -T`,
|
|
225
|
+
you'd run `spring rake -T`.
|
|
226
|
+
|
|
227
|
+
## Temporarily disabling Spring
|
|
228
|
+
|
|
229
|
+
If you're using Spring binstubs, but temporarily don't want commands to
|
|
230
|
+
run through Spring, set the `DISABLE_SPRING` environment variable.
|
|
231
|
+
|
|
232
|
+
## Class reloading
|
|
233
|
+
|
|
234
|
+
Spring uses Rails' class reloading mechanism
|
|
235
|
+
(`ActiveSupport::Dependencies`) to keep your code up to date between
|
|
236
|
+
test runs. This is the same mechanism which allows you to see changes
|
|
237
|
+
during development when you refresh the page. However, you may never
|
|
238
|
+
have used this mechanism with your `test` environment before, and this
|
|
239
|
+
can cause problems.
|
|
240
|
+
|
|
241
|
+
It's important to realise that code reloading means that the constants
|
|
242
|
+
in your application are *different objects* after files have changed:
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
$ bin/rails runner 'puts User.object_id'
|
|
246
|
+
70127987886040
|
|
247
|
+
$ touch app/models/user.rb
|
|
248
|
+
$ bin/rails runner 'puts User.object_id'
|
|
249
|
+
70127976764620
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
Suppose you have an initializer `config/initializers/save_user_class.rb`
|
|
253
|
+
like so:
|
|
254
|
+
|
|
255
|
+
``` ruby
|
|
256
|
+
USER_CLASS = User
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
This saves off the *first* version of the `User` class, which will not
|
|
260
|
+
be the same object as `User` after the code has been reloaded:
|
|
261
|
+
|
|
262
|
+
```
|
|
263
|
+
$ bin/rails runner 'puts User == USER_CLASS'
|
|
264
|
+
true
|
|
265
|
+
$ touch app/models/user.rb
|
|
266
|
+
$ bin/rails runner 'puts User == USER_CLASS'
|
|
267
|
+
false
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
So to avoid this problem, don't save off references to application
|
|
271
|
+
constants in your initialization code.
|
|
272
|
+
|
|
197
273
|
## Configuration
|
|
198
274
|
|
|
199
275
|
Spring will read `~/.spring.rb` and `config/spring.rb` for custom
|
|
@@ -222,11 +298,6 @@ which get run when your application initializers, such as
|
|
|
222
298
|
`config/application.rb`, `config/environments/*.rb` or
|
|
223
299
|
`config/initializers/*.rb`.
|
|
224
300
|
|
|
225
|
-
For example, if loading your test helper is slow, you might like to
|
|
226
|
-
preload it to speed up your test runs. To do this you could put a
|
|
227
|
-
`require Rails.root.join("test/helper")` in
|
|
228
|
-
`config/environments/test.rb`.
|
|
229
|
-
|
|
230
301
|
### Running code after forking
|
|
231
302
|
|
|
232
303
|
You might want to run code after Spring forked off the process but
|
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,18 @@
|
|
|
1
|
+
# This is necessary for the terminal to work correctly when we reopen stdin.
|
|
2
|
+
Process.setsid
|
|
3
|
+
|
|
4
|
+
require "spring/application"
|
|
5
|
+
|
|
6
|
+
app = Spring::Application.new(
|
|
7
|
+
UNIXSocket.for_fd(3),
|
|
8
|
+
Spring::JSON.load(ENV.delete("SPRING_ORIGINAL_ENV").dup)
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
Signal.trap("TERM") { app.terminate }
|
|
12
|
+
|
|
13
|
+
Spring::ProcessTitleUpdater.run { |distance|
|
|
14
|
+
"spring app | #{app.app_name} | started #{distance} ago | #{app.app_env} mode"
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
app.eager_preload if ENV.delete("SPRING_PRELOAD") == "1"
|
|
18
|
+
app.run
|
data/lib/spring/application.rb
CHANGED
|
@@ -1,41 +1,84 @@
|
|
|
1
|
+
require "spring/boot"
|
|
1
2
|
require "set"
|
|
2
|
-
require "
|
|
3
|
-
require "thread"
|
|
3
|
+
require "pty"
|
|
4
4
|
|
|
5
5
|
module Spring
|
|
6
6
|
class Application
|
|
7
|
-
attr_reader :manager, :watcher, :spring_env
|
|
7
|
+
attr_reader :manager, :watcher, :spring_env, :original_env
|
|
8
|
+
|
|
9
|
+
def initialize(manager, original_env)
|
|
10
|
+
@manager = manager
|
|
11
|
+
@original_env = original_env
|
|
12
|
+
@spring_env = Env.new
|
|
13
|
+
@mutex = Mutex.new
|
|
14
|
+
@waiting = 0
|
|
15
|
+
@preloaded = false
|
|
16
|
+
@state = :initialized
|
|
17
|
+
@interrupt = IO.pipe
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def state(val)
|
|
21
|
+
return if exiting?
|
|
22
|
+
log "#{@state} -> #{val}"
|
|
23
|
+
@state = val
|
|
24
|
+
end
|
|
8
25
|
|
|
9
|
-
def
|
|
10
|
-
|
|
11
|
-
@
|
|
12
|
-
|
|
13
|
-
@spring_env = Env.new
|
|
14
|
-
@preloaded = false
|
|
15
|
-
@mutex = Mutex.new
|
|
16
|
-
@waiting = 0
|
|
17
|
-
@exiting = false
|
|
26
|
+
def state!(val)
|
|
27
|
+
state val
|
|
28
|
+
@interrupt.last.write "."
|
|
29
|
+
end
|
|
18
30
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
31
|
+
def app_env
|
|
32
|
+
ENV['RAILS_ENV']
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def app_name
|
|
36
|
+
spring_env.app_name
|
|
22
37
|
end
|
|
23
38
|
|
|
24
39
|
def log(message)
|
|
25
|
-
spring_env.log "[application:#{
|
|
40
|
+
spring_env.log "[application:#{app_env}] #{message}"
|
|
26
41
|
end
|
|
27
42
|
|
|
28
43
|
def preloaded?
|
|
29
44
|
@preloaded
|
|
30
45
|
end
|
|
31
46
|
|
|
47
|
+
def preload_failed?
|
|
48
|
+
@preloaded == :failure
|
|
49
|
+
end
|
|
50
|
+
|
|
32
51
|
def exiting?
|
|
33
|
-
@exiting
|
|
52
|
+
@state == :exiting
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def terminating?
|
|
56
|
+
@state == :terminating
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def watcher_stale?
|
|
60
|
+
@state == :watcher_stale
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def initialized?
|
|
64
|
+
@state == :initialized
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def start_watcher
|
|
68
|
+
@watcher = Spring.watcher
|
|
69
|
+
@watcher.on_stale { state! :watcher_stale }
|
|
70
|
+
@watcher.start
|
|
34
71
|
end
|
|
35
72
|
|
|
36
73
|
def preload
|
|
37
74
|
log "preloading app"
|
|
38
75
|
|
|
76
|
+
begin
|
|
77
|
+
require "spring/commands"
|
|
78
|
+
ensure
|
|
79
|
+
start_watcher
|
|
80
|
+
end
|
|
81
|
+
|
|
39
82
|
require Spring.application_root_path.join("config", "application")
|
|
40
83
|
|
|
41
84
|
# config/environments/test.rb will have config.cache_classes = true. However
|
|
@@ -51,44 +94,46 @@ module Spring
|
|
|
51
94
|
Rails.application.config.cache_classes = false
|
|
52
95
|
disconnect_database
|
|
53
96
|
|
|
97
|
+
@preloaded = :success
|
|
98
|
+
rescue Exception => e
|
|
99
|
+
@preloaded = :failure
|
|
100
|
+
watcher.add e.backtrace.map { |line| line.match(/^(.*)\:\d+\:in /)[1] }
|
|
101
|
+
raise e unless initialized?
|
|
102
|
+
ensure
|
|
54
103
|
watcher.add loaded_application_features
|
|
55
104
|
watcher.add Spring.gemfile, "#{Spring.gemfile}.lock"
|
|
56
|
-
watcher.add Rails.application.paths["config/initializers"]
|
|
57
105
|
|
|
58
|
-
|
|
106
|
+
if defined?(Rails) && Rails.application
|
|
107
|
+
watcher.add Rails.application.paths["config/initializers"]
|
|
108
|
+
watcher.add Rails.application.paths["config/database"]
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def eager_preload
|
|
113
|
+
with_pty { preload }
|
|
59
114
|
end
|
|
60
115
|
|
|
61
116
|
def run
|
|
62
|
-
|
|
63
|
-
|
|
117
|
+
state :running
|
|
118
|
+
manager.puts
|
|
64
119
|
|
|
65
120
|
loop do
|
|
66
|
-
IO.select
|
|
67
|
-
|
|
68
|
-
if
|
|
69
|
-
|
|
70
|
-
manager.close
|
|
71
|
-
@exiting = true
|
|
72
|
-
try_exit
|
|
73
|
-
sleep
|
|
121
|
+
IO.select [manager, @interrupt.first]
|
|
122
|
+
|
|
123
|
+
if terminating? || watcher_stale? || preload_failed?
|
|
124
|
+
exit
|
|
74
125
|
else
|
|
75
126
|
serve manager.recv_io(UNIXSocket)
|
|
76
127
|
end
|
|
77
128
|
end
|
|
78
129
|
end
|
|
79
130
|
|
|
80
|
-
def try_exit
|
|
81
|
-
@mutex.synchronize {
|
|
82
|
-
exit if exiting? && @waiting == 0
|
|
83
|
-
}
|
|
84
|
-
end
|
|
85
|
-
|
|
86
131
|
def serve(client)
|
|
87
132
|
log "got client"
|
|
88
133
|
manager.puts
|
|
89
134
|
|
|
90
|
-
streams = 3.times.map { client.recv_io }
|
|
91
|
-
[STDOUT, STDERR].zip(streams).each { |a, b| a.reopen(b) }
|
|
135
|
+
stdout, stderr, stdin = streams = 3.times.map { client.recv_io }
|
|
136
|
+
[STDOUT, STDERR, STDIN].zip(streams).each { |a, b| a.reopen(b) }
|
|
92
137
|
|
|
93
138
|
preload unless preloaded?
|
|
94
139
|
|
|
@@ -104,38 +149,33 @@ module Spring
|
|
|
104
149
|
end
|
|
105
150
|
|
|
106
151
|
pid = fork {
|
|
107
|
-
Process.setsid
|
|
108
|
-
STDIN.reopen(streams.last)
|
|
109
152
|
IGNORE_SIGNALS.each { |sig| trap(sig, "DEFAULT") }
|
|
153
|
+
trap("TERM", "DEFAULT")
|
|
110
154
|
|
|
111
155
|
ARGV.replace(args)
|
|
156
|
+
$0 = command.process_title
|
|
112
157
|
|
|
113
158
|
# Delete all env vars which are unchanged from before spring started
|
|
114
|
-
|
|
159
|
+
original_env.each { |k, v| ENV.delete k if ENV[k] == v }
|
|
115
160
|
|
|
116
161
|
# Load in the current env vars, except those which *were* changed when spring started
|
|
117
162
|
env.each { |k, v| ENV[k] ||= v }
|
|
118
163
|
|
|
164
|
+
# requiring is faster, and we don't need constant reloading in this process
|
|
165
|
+
ActiveSupport::Dependencies.mechanism = :require
|
|
166
|
+
Rails.application.config.cache_classes = true
|
|
167
|
+
|
|
119
168
|
connect_database
|
|
120
169
|
srand
|
|
121
170
|
|
|
122
171
|
invoke_after_fork_callbacks
|
|
123
172
|
shush_backtraces
|
|
124
173
|
|
|
125
|
-
|
|
126
|
-
command.call
|
|
127
|
-
else
|
|
128
|
-
exec_name = command.exec_name
|
|
129
|
-
gem_name = command.gem_name if command.respond_to?(:gem_name)
|
|
130
|
-
|
|
131
|
-
exec = Gem.bin_path(gem_name || exec_name, exec_name)
|
|
132
|
-
$0 = exec
|
|
133
|
-
load exec
|
|
134
|
-
end
|
|
174
|
+
command.call
|
|
135
175
|
}
|
|
136
176
|
|
|
137
177
|
disconnect_database
|
|
138
|
-
|
|
178
|
+
reset_streams
|
|
139
179
|
|
|
140
180
|
log "forked #{pid}"
|
|
141
181
|
manager.puts pid
|
|
@@ -152,26 +192,44 @@ module Spring
|
|
|
152
192
|
client.close
|
|
153
193
|
|
|
154
194
|
@mutex.synchronize { @waiting -= 1 }
|
|
155
|
-
|
|
195
|
+
exit_if_finished
|
|
156
196
|
}
|
|
157
197
|
|
|
158
|
-
rescue => e
|
|
198
|
+
rescue Exception => e
|
|
159
199
|
log "exception: #{e}"
|
|
160
|
-
|
|
161
|
-
|
|
200
|
+
manager.puts unless pid
|
|
201
|
+
|
|
202
|
+
if streams && !e.is_a?(SystemExit)
|
|
203
|
+
print_exception(stderr, e)
|
|
204
|
+
streams.each(&:close)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
client.puts(1) if pid
|
|
162
208
|
client.close
|
|
163
|
-
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def terminate
|
|
212
|
+
state! :terminating
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def exit
|
|
216
|
+
state :exiting
|
|
217
|
+
manager.shutdown(:RDWR)
|
|
218
|
+
exit_if_finished
|
|
219
|
+
sleep
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
def exit_if_finished
|
|
223
|
+
@mutex.synchronize {
|
|
224
|
+
Kernel.exit if exiting? && @waiting == 0
|
|
225
|
+
}
|
|
164
226
|
end
|
|
165
227
|
|
|
166
228
|
# The command might need to require some files in the
|
|
167
229
|
# main process so that they are cached. For example a test command wants to
|
|
168
230
|
# load the helper file once and have it cached.
|
|
169
231
|
def setup(command)
|
|
170
|
-
|
|
171
|
-
@setup << command.class
|
|
172
|
-
|
|
173
|
-
if command.respond_to?(:setup)
|
|
174
|
-
command.setup
|
|
232
|
+
if command.setup
|
|
175
233
|
watcher.add loaded_application_features # loaded features may have changed
|
|
176
234
|
end
|
|
177
235
|
end
|
|
@@ -183,7 +241,8 @@ module Spring
|
|
|
183
241
|
end
|
|
184
242
|
|
|
185
243
|
def loaded_application_features
|
|
186
|
-
|
|
244
|
+
root = Spring.application_root_path.to_s
|
|
245
|
+
$LOADED_FEATURES.select { |f| f.start_with?(root) }
|
|
187
246
|
end
|
|
188
247
|
|
|
189
248
|
def disconnect_database
|
|
@@ -211,5 +270,24 @@ module Spring
|
|
|
211
270
|
end
|
|
212
271
|
end
|
|
213
272
|
end
|
|
273
|
+
|
|
274
|
+
def print_exception(stream, error)
|
|
275
|
+
first, rest = error.backtrace.first, error.backtrace.drop(1)
|
|
276
|
+
stream.puts("#{first}: #{error} (#{error.class})")
|
|
277
|
+
rest.each { |line| stream.puts("\tfrom #{line}") }
|
|
278
|
+
end
|
|
279
|
+
|
|
280
|
+
def with_pty
|
|
281
|
+
PTY.open do |master, slave|
|
|
282
|
+
[STDOUT, STDERR, STDIN].each { |s| s.reopen slave }
|
|
283
|
+
yield
|
|
284
|
+
reset_streams
|
|
285
|
+
end
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
def reset_streams
|
|
289
|
+
[STDOUT, STDERR].each { |stream| stream.reopen(spring_env.log_file) }
|
|
290
|
+
STDIN.reopen("/dev/null")
|
|
291
|
+
end
|
|
214
292
|
end
|
|
215
293
|
end
|