spring 1.1.0.beta1 → 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 +0 -1
- data/CHANGELOG.md +14 -2
- data/README.md +47 -11
- data/bin/spring +1 -0
- data/lib/spring/application/boot.rb +16 -0
- data/lib/spring/application.rb +116 -45
- data/lib/spring/application_manager.rb +51 -40
- data/lib/spring/binstub.rb +4 -3
- data/lib/spring/boot.rb +8 -0
- data/lib/spring/client/binstub.rb +16 -14
- data/lib/spring/command_wrapper.rb +4 -1
- data/lib/spring/configuration.rb +11 -2
- data/lib/spring/env.rb +10 -9
- data/lib/spring/server.rb +7 -29
- 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 +20 -13
- data/test/acceptance/helper.rb +36 -19
- data/test/unit/watcher_test.rb +12 -8
- metadata +7 -5
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,8 +1,9 @@
|
|
|
1
|
-
##
|
|
1
|
+
## 1.1.0
|
|
2
2
|
|
|
3
3
|
* A `bin/spring` binstub is now generated. This allows us to load spring
|
|
4
4
|
correctly if you have it installed locally with a `BUNDLE_PATH`, so
|
|
5
|
-
it's no longer necessary to install spring system-wide.
|
|
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
|
|
6
7
|
still can't have spring in your Gemfile as a git repository or local
|
|
7
8
|
path; it must be a proper gem.
|
|
8
9
|
* Various changes to how springified binstubs are implemented. Existing
|
|
@@ -12,6 +13,17 @@
|
|
|
12
13
|
binstubs. This won't work unless you have upgraded your binstubs to
|
|
13
14
|
the new format.
|
|
14
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
|
|
15
27
|
|
|
16
28
|
## 1.0.0
|
|
17
29
|
|
data/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Spring
|
|
2
2
|
|
|
3
|
-
[](https://travis-ci.org/rails/spring)
|
|
4
4
|
[](http://badge.fury.io/rb/spring)
|
|
5
5
|
|
|
6
6
|
Spring is a Rails application preloader. It speeds up development by
|
|
@@ -222,6 +222,52 @@ To use spring like this, do a `gem install spring` and then prefix
|
|
|
222
222
|
commands with `spring`. For example, rather than running `bin/rake -T`,
|
|
223
223
|
you'd run `spring rake -T`.
|
|
224
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
|
+
|
|
225
271
|
## Configuration
|
|
226
272
|
|
|
227
273
|
Spring will read `~/.spring.rb` and `config/spring.rb` for custom
|
|
@@ -250,16 +296,6 @@ which get run when your application initializers, such as
|
|
|
250
296
|
`config/application.rb`, `config/environments/*.rb` or
|
|
251
297
|
`config/initializers/*.rb`.
|
|
252
298
|
|
|
253
|
-
For example, if loading your test helper is slow, you might like to
|
|
254
|
-
preload it to speed up your test runs. To do this you could add the
|
|
255
|
-
following to your `config/environments/test.rb`:
|
|
256
|
-
|
|
257
|
-
``` ruby
|
|
258
|
-
config.after_initialize do
|
|
259
|
-
require Rails.root.join("test/helper")
|
|
260
|
-
end
|
|
261
|
-
```
|
|
262
|
-
|
|
263
299
|
### Running code after forking
|
|
264
300
|
|
|
265
301
|
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,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,40 +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
|
|
24
|
+
|
|
25
|
+
def state!(val)
|
|
26
|
+
state val
|
|
27
|
+
@interrupt.last.write "."
|
|
28
|
+
end
|
|
8
29
|
|
|
9
|
-
def
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@spring_env = Env.new
|
|
13
|
-
@preloaded = false
|
|
14
|
-
@mutex = Mutex.new
|
|
15
|
-
@waiting = 0
|
|
16
|
-
@exiting = false
|
|
30
|
+
def app_env
|
|
31
|
+
ENV['RAILS_ENV']
|
|
32
|
+
end
|
|
17
33
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@fds = [manager, watcher.to_io]
|
|
34
|
+
def app_name
|
|
35
|
+
spring_env.app_name
|
|
21
36
|
end
|
|
22
37
|
|
|
23
38
|
def log(message)
|
|
24
|
-
spring_env.log "[application:#{
|
|
39
|
+
spring_env.log "[application:#{app_env}] #{message}"
|
|
25
40
|
end
|
|
26
41
|
|
|
27
42
|
def preloaded?
|
|
28
43
|
@preloaded
|
|
29
44
|
end
|
|
30
45
|
|
|
46
|
+
def preload_failed?
|
|
47
|
+
@preloaded == :failure
|
|
48
|
+
end
|
|
49
|
+
|
|
31
50
|
def exiting?
|
|
32
|
-
@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
|
|
33
70
|
end
|
|
34
71
|
|
|
35
72
|
def preload
|
|
36
73
|
log "preloading app"
|
|
37
74
|
|
|
75
|
+
begin
|
|
76
|
+
require "spring/commands"
|
|
77
|
+
ensure
|
|
78
|
+
start_watcher
|
|
79
|
+
end
|
|
80
|
+
|
|
38
81
|
require Spring.application_root_path.join("config", "application")
|
|
39
82
|
|
|
40
83
|
# config/environments/test.rb will have config.cache_classes = true. However
|
|
@@ -50,45 +93,42 @@ module Spring
|
|
|
50
93
|
Rails.application.config.cache_classes = false
|
|
51
94
|
disconnect_database
|
|
52
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
|
|
53
102
|
watcher.add loaded_application_features
|
|
54
103
|
watcher.add Spring.gemfile, "#{Spring.gemfile}.lock"
|
|
55
|
-
watcher.add Rails.application.paths["config/initializers"]
|
|
56
|
-
watcher.add Rails.application.paths["config/database"]
|
|
57
104
|
|
|
58
|
-
|
|
105
|
+
if defined?(Rails)
|
|
106
|
+
watcher.add Rails.application.paths["config/initializers"]
|
|
107
|
+
watcher.add Rails.application.paths["config/database"]
|
|
108
|
+
end
|
|
59
109
|
end
|
|
60
110
|
|
|
61
111
|
def run
|
|
62
|
-
|
|
63
|
-
|
|
112
|
+
state :running
|
|
113
|
+
manager.puts
|
|
64
114
|
|
|
65
115
|
loop do
|
|
66
|
-
IO.select
|
|
67
|
-
|
|
68
|
-
if
|
|
69
|
-
|
|
70
|
-
manager.close
|
|
71
|
-
@exiting = true
|
|
72
|
-
try_exit
|
|
73
|
-
sleep
|
|
116
|
+
IO.select [manager, @interrupt.first]
|
|
117
|
+
|
|
118
|
+
if terminating? || watcher_stale? || preload_failed?
|
|
119
|
+
exit
|
|
74
120
|
else
|
|
75
121
|
serve manager.recv_io(UNIXSocket)
|
|
76
122
|
end
|
|
77
123
|
end
|
|
78
124
|
end
|
|
79
125
|
|
|
80
|
-
def try_exit
|
|
81
|
-
@mutex.synchronize {
|
|
82
|
-
exit if exiting? && @waiting == 0
|
|
83
|
-
}
|
|
84
|
-
end
|
|
85
|
-
|
|
86
126
|
def serve(client)
|
|
87
127
|
log "got client"
|
|
88
128
|
manager.puts
|
|
89
129
|
|
|
90
|
-
streams = 3.times.map { client.recv_io }
|
|
91
|
-
[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) }
|
|
92
132
|
|
|
93
133
|
preload unless preloaded?
|
|
94
134
|
|
|
@@ -105,13 +145,15 @@ module Spring
|
|
|
105
145
|
|
|
106
146
|
pid = fork {
|
|
107
147
|
Process.setsid
|
|
108
|
-
STDIN.reopen(
|
|
148
|
+
STDIN.reopen(stdin)
|
|
109
149
|
IGNORE_SIGNALS.each { |sig| trap(sig, "DEFAULT") }
|
|
150
|
+
trap("TERM", "DEFAULT")
|
|
110
151
|
|
|
111
152
|
ARGV.replace(args)
|
|
153
|
+
$0 = command.process_title
|
|
112
154
|
|
|
113
155
|
# Delete all env vars which are unchanged from before spring started
|
|
114
|
-
|
|
156
|
+
original_env.each { |k, v| ENV.delete k if ENV[k] == v }
|
|
115
157
|
|
|
116
158
|
# Load in the current env vars, except those which *were* changed when spring started
|
|
117
159
|
env.each { |k, v| ENV[k] ||= v }
|
|
@@ -147,15 +189,37 @@ 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
|
|
@@ -174,7 +238,8 @@ module Spring
|
|
|
174
238
|
end
|
|
175
239
|
|
|
176
240
|
def loaded_application_features
|
|
177
|
-
|
|
241
|
+
root = Spring.application_root_path.to_s
|
|
242
|
+
$LOADED_FEATURES.select { |f| f.start_with?(root) }
|
|
178
243
|
end
|
|
179
244
|
|
|
180
245
|
def disconnect_database
|
|
@@ -202,5 +267,11 @@ module Spring
|
|
|
202
267
|
end
|
|
203
268
|
end
|
|
204
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
|
|
205
276
|
end
|
|
206
277
|
end
|
|
@@ -1,20 +1,16 @@
|
|
|
1
|
-
require "socket"
|
|
2
|
-
require "thread"
|
|
3
|
-
require "spring/application"
|
|
4
|
-
|
|
5
1
|
module Spring
|
|
6
2
|
class ApplicationManager
|
|
7
|
-
attr_reader :pid, :child, :app_env, :spring_env, :
|
|
3
|
+
attr_reader :pid, :child, :app_env, :spring_env, :status
|
|
8
4
|
|
|
9
|
-
def initialize(
|
|
10
|
-
@server = server
|
|
5
|
+
def initialize(app_env)
|
|
11
6
|
@app_env = app_env
|
|
12
7
|
@spring_env = Env.new
|
|
13
8
|
@mutex = Mutex.new
|
|
9
|
+
@state = :running
|
|
14
10
|
end
|
|
15
11
|
|
|
16
12
|
def log(message)
|
|
17
|
-
|
|
13
|
+
spring_env.log "[application_manager:#{app_env}] #{message}"
|
|
18
14
|
end
|
|
19
15
|
|
|
20
16
|
# We're not using @mutex.synchronize to avoid the weird "<internal:prelude>:10"
|
|
@@ -28,10 +24,10 @@ module Spring
|
|
|
28
24
|
|
|
29
25
|
def start
|
|
30
26
|
start_child
|
|
31
|
-
start_wait_thread
|
|
32
27
|
end
|
|
33
28
|
|
|
34
29
|
def restart
|
|
30
|
+
return if @state == :stopping
|
|
35
31
|
start_child(true)
|
|
36
32
|
end
|
|
37
33
|
|
|
@@ -63,13 +59,15 @@ module Spring
|
|
|
63
59
|
def run(client)
|
|
64
60
|
with_child do
|
|
65
61
|
child.send_io client
|
|
66
|
-
child.gets
|
|
62
|
+
child.gets or raise Errno::EPIPE
|
|
67
63
|
end
|
|
68
64
|
|
|
69
|
-
pid = child.gets
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
65
|
+
pid = child.gets.to_i
|
|
66
|
+
|
|
67
|
+
unless pid.zero?
|
|
68
|
+
log "got worker pid #{pid}"
|
|
69
|
+
pid
|
|
70
|
+
end
|
|
73
71
|
rescue Errno::ECONNRESET, Errno::EPIPE => e
|
|
74
72
|
log "#{e} while reading from child; returning no pid"
|
|
75
73
|
nil
|
|
@@ -78,45 +76,58 @@ module Spring
|
|
|
78
76
|
end
|
|
79
77
|
|
|
80
78
|
def stop
|
|
81
|
-
|
|
79
|
+
@state = :stopping
|
|
80
|
+
|
|
81
|
+
if pid
|
|
82
|
+
Process.kill('TERM', pid)
|
|
83
|
+
Process.wait(pid)
|
|
84
|
+
end
|
|
82
85
|
end
|
|
83
86
|
|
|
84
87
|
private
|
|
85
88
|
|
|
86
89
|
def start_child(preload = false)
|
|
87
|
-
server.application_starting
|
|
88
|
-
|
|
89
90
|
@child, child_socket = UNIXSocket.pair
|
|
90
|
-
@pid = fork {
|
|
91
|
-
(ObjectSpace.each_object(IO).to_a - [STDOUT, STDERR, STDIN, child_socket])
|
|
92
|
-
.reject(&:closed?)
|
|
93
|
-
.each(&:close)
|
|
94
91
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
92
|
+
Bundler.with_clean_env do
|
|
93
|
+
@pid = Process.spawn(
|
|
94
|
+
{
|
|
95
|
+
"RAILS_ENV" => app_env,
|
|
96
|
+
"RACK_ENV" => app_env,
|
|
97
|
+
"SPRING_ORIGINAL_ENV" => JSON.dump(Spring::ORIGINAL_ENV),
|
|
98
|
+
"SPRING_PRELOAD" => preload ? "1" : "0"
|
|
99
|
+
},
|
|
100
|
+
"ruby",
|
|
101
|
+
"-I", File.expand_path("../..", __FILE__),
|
|
102
|
+
"-e", "require 'spring/application/boot'",
|
|
103
|
+
3 => child_socket
|
|
104
|
+
)
|
|
105
|
+
end
|
|
100
106
|
|
|
101
|
-
|
|
102
|
-
app.preload if preload
|
|
103
|
-
app.run
|
|
104
|
-
}
|
|
107
|
+
start_wait_thread(pid, child) if child.gets
|
|
105
108
|
child_socket.close
|
|
106
109
|
end
|
|
107
110
|
|
|
108
|
-
def start_wait_thread
|
|
109
|
-
|
|
110
|
-
Thread.current.abort_on_exception = true
|
|
111
|
+
def start_wait_thread(pid, child)
|
|
112
|
+
Process.detach(pid)
|
|
111
113
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
114
|
+
Thread.new {
|
|
115
|
+
# The recv can raise an ECONNRESET, killing the thread, but that's ok
|
|
116
|
+
# as if it does we're no longer interested in the child
|
|
117
|
+
loop do
|
|
118
|
+
IO.select([child])
|
|
119
|
+
break if child.recv(1, Socket::MSG_PEEK).empty?
|
|
120
|
+
sleep 0.01
|
|
119
121
|
end
|
|
122
|
+
|
|
123
|
+
log "child #{pid} shutdown"
|
|
124
|
+
|
|
125
|
+
synchronize {
|
|
126
|
+
if @pid == pid
|
|
127
|
+
@pid = nil
|
|
128
|
+
restart
|
|
129
|
+
end
|
|
130
|
+
}
|
|
120
131
|
}
|
|
121
132
|
end
|
|
122
133
|
end
|
data/lib/spring/binstub.rb
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
command
|
|
1
|
+
command = File.basename($0)
|
|
2
|
+
bin_path = File.expand_path("../../../bin/spring", __FILE__)
|
|
2
3
|
|
|
3
4
|
if command == "spring"
|
|
4
|
-
load
|
|
5
|
+
load bin_path
|
|
5
6
|
else
|
|
6
7
|
disable = ENV["DISABLE_SPRING"]
|
|
7
8
|
|
|
8
9
|
if Process.respond_to?(:fork) && (disable.nil? || disable.empty? || disable == "0")
|
|
9
10
|
ARGV.unshift(command)
|
|
10
|
-
load
|
|
11
|
+
load bin_path
|
|
11
12
|
end
|
|
12
13
|
end
|
data/lib/spring/boot.rb
ADDED
|
@@ -23,23 +23,27 @@ CODE
|
|
|
23
23
|
# process we'll execute the lines which come after the LOADER block, which
|
|
24
24
|
# is what we want.
|
|
25
25
|
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
|
|
30
|
-
SPRING = <<CODE
|
|
26
|
+
# Parsing the lockfile in this way is pretty nasty but reliable enough
|
|
27
|
+
# The regex ensures that the match must be between a GEM line and an empty
|
|
28
|
+
# line, so it won't go on to the next section.
|
|
29
|
+
SPRING = <<'CODE'
|
|
31
30
|
#!/usr/bin/env ruby
|
|
32
31
|
|
|
32
|
+
# This file loads spring without using Bundler, in order to be fast
|
|
33
|
+
# It gets overwritten when you run the `spring binstub` command
|
|
34
|
+
|
|
33
35
|
unless defined?(Spring)
|
|
34
36
|
require "rubygems"
|
|
35
37
|
require "bundler"
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ spring \((.*?)\)$.*?^$/m)
|
|
40
|
+
ENV["GEM_PATH"] = ([Bundler.bundle_path.to_s] + Gem.path).join(File::PATH_SEPARATOR)
|
|
41
|
+
ENV["GEM_HOME"] = ""
|
|
42
|
+
Gem.paths = ENV
|
|
40
43
|
|
|
41
|
-
|
|
42
|
-
|
|
44
|
+
gem "spring", match[1]
|
|
45
|
+
require "spring/binstub"
|
|
46
|
+
end
|
|
43
47
|
end
|
|
44
48
|
CODE
|
|
45
49
|
|
|
@@ -167,10 +171,8 @@ CODE
|
|
|
167
171
|
when :add
|
|
168
172
|
bindir.mkdir unless bindir.exist?
|
|
169
173
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
spring_binstub.chmod 0755
|
|
173
|
-
end
|
|
174
|
+
File.write(spring_binstub, SPRING)
|
|
175
|
+
spring_binstub.chmod 0755
|
|
174
176
|
|
|
175
177
|
items.each(&:add)
|
|
176
178
|
when :remove
|
|
@@ -37,11 +37,14 @@ module Spring
|
|
|
37
37
|
if command.respond_to?(:call)
|
|
38
38
|
command.call
|
|
39
39
|
else
|
|
40
|
-
$0 = exec
|
|
41
40
|
load exec
|
|
42
41
|
end
|
|
43
42
|
end
|
|
44
43
|
|
|
44
|
+
def process_title
|
|
45
|
+
["spring", name, *ARGV].join(" ")
|
|
46
|
+
end
|
|
47
|
+
|
|
45
48
|
def gem_name
|
|
46
49
|
if command.respond_to?(:gem_name)
|
|
47
50
|
command.gem_name
|
data/lib/spring/configuration.rb
CHANGED
|
@@ -22,15 +22,24 @@ module Spring
|
|
|
22
22
|
|
|
23
23
|
def application_root_path
|
|
24
24
|
@application_root_path ||= begin
|
|
25
|
-
|
|
25
|
+
if application_root
|
|
26
|
+
path = Pathname.new(File.expand_path(application_root))
|
|
27
|
+
else
|
|
28
|
+
path = project_root_path
|
|
29
|
+
end
|
|
30
|
+
|
|
26
31
|
raise MissingApplication.new(path) unless path.join("config/application.rb").exist?
|
|
27
32
|
path
|
|
28
33
|
end
|
|
29
34
|
end
|
|
30
35
|
|
|
36
|
+
def project_root_path
|
|
37
|
+
@project_root_path ||= find_project_root(Pathname.new(File.expand_path(Dir.pwd)))
|
|
38
|
+
end
|
|
39
|
+
|
|
31
40
|
private
|
|
32
41
|
|
|
33
|
-
def find_project_root(current_dir
|
|
42
|
+
def find_project_root(current_dir)
|
|
34
43
|
if current_dir.join(gemfile).exist?
|
|
35
44
|
current_dir
|
|
36
45
|
elsif current_dir.root?
|
data/lib/spring/env.rb
CHANGED
|
@@ -14,14 +14,19 @@ module Spring
|
|
|
14
14
|
attr_reader :log_file
|
|
15
15
|
|
|
16
16
|
def initialize(root = nil)
|
|
17
|
-
@root
|
|
18
|
-
@
|
|
17
|
+
@root = root
|
|
18
|
+
@project_root = root
|
|
19
|
+
@log_file = File.open(ENV["SPRING_LOG"] || "/dev/null", "a")
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
def root
|
|
22
23
|
@root ||= Spring.application_root_path
|
|
23
24
|
end
|
|
24
25
|
|
|
26
|
+
def project_root
|
|
27
|
+
@project_root ||= Spring.project_root_path
|
|
28
|
+
end
|
|
29
|
+
|
|
25
30
|
def version
|
|
26
31
|
Spring::VERSION
|
|
27
32
|
end
|
|
@@ -33,7 +38,7 @@ module Spring
|
|
|
33
38
|
end
|
|
34
39
|
|
|
35
40
|
def application_id
|
|
36
|
-
Digest::MD5.hexdigest(
|
|
41
|
+
Digest::MD5.hexdigest(project_root.to_s)
|
|
37
42
|
end
|
|
38
43
|
|
|
39
44
|
def socket_path
|
|
@@ -60,7 +65,7 @@ module Spring
|
|
|
60
65
|
end
|
|
61
66
|
|
|
62
67
|
def server_running?
|
|
63
|
-
pidfile = pidfile_path.open('r')
|
|
68
|
+
pidfile = pidfile_path.open('r+')
|
|
64
69
|
!pidfile.flock(File::LOCK_EX | File::LOCK_NB)
|
|
65
70
|
rescue Errno::ENOENT
|
|
66
71
|
false
|
|
@@ -71,12 +76,8 @@ module Spring
|
|
|
71
76
|
end
|
|
72
77
|
end
|
|
73
78
|
|
|
74
|
-
def bundle_mtime
|
|
75
|
-
[Bundler.default_lockfile, Bundler.default_gemfile].select(&:exist?).map(&:mtime).max
|
|
76
|
-
end
|
|
77
|
-
|
|
78
79
|
def log(message)
|
|
79
|
-
log_file.puts "[#{Time.now}] #{message}"
|
|
80
|
+
log_file.puts "[#{Time.now}] [#{Process.pid}] #{message}"
|
|
80
81
|
log_file.flush
|
|
81
82
|
end
|
|
82
83
|
end
|
data/lib/spring/server.rb
CHANGED
|
@@ -1,26 +1,13 @@
|
|
|
1
1
|
module Spring
|
|
2
|
-
|
|
3
|
-
attr_reader :original_env
|
|
4
|
-
end
|
|
5
|
-
@original_env = ENV.to_hash
|
|
2
|
+
ORIGINAL_ENV = ENV.to_hash
|
|
6
3
|
end
|
|
7
4
|
|
|
8
|
-
require "
|
|
9
|
-
require "thread"
|
|
10
|
-
|
|
11
|
-
require "spring/configuration"
|
|
12
|
-
require "spring/env"
|
|
5
|
+
require "spring/boot"
|
|
13
6
|
require "spring/application_manager"
|
|
14
|
-
require "spring/process_title_updater"
|
|
15
|
-
require "spring/json"
|
|
16
7
|
|
|
17
|
-
# Must be last, as it requires bundler/setup
|
|
8
|
+
# Must be last, as it requires bundler/setup, which alters the load path
|
|
18
9
|
require "spring/commands"
|
|
19
10
|
|
|
20
|
-
# readline must be required before we setpgid, otherwise the require may hang,
|
|
21
|
-
# if readline has been built against libedit. See issue #70.
|
|
22
|
-
require "readline"
|
|
23
|
-
|
|
24
11
|
module Spring
|
|
25
12
|
class Server
|
|
26
13
|
def self.boot
|
|
@@ -31,7 +18,7 @@ module Spring
|
|
|
31
18
|
|
|
32
19
|
def initialize(env = Env.new)
|
|
33
20
|
@env = env
|
|
34
|
-
@applications = Hash.new { |h, k| h[k] = ApplicationManager.new(
|
|
21
|
+
@applications = Hash.new { |h, k| h[k] = ApplicationManager.new(k) }
|
|
35
22
|
@pidfile = env.pidfile_path.open('a')
|
|
36
23
|
@mutex = Mutex.new
|
|
37
24
|
end
|
|
@@ -48,7 +35,6 @@ module Spring
|
|
|
48
35
|
ignore_signals
|
|
49
36
|
set_exit_hook
|
|
50
37
|
set_process_title
|
|
51
|
-
watch_bundle
|
|
52
38
|
start_server
|
|
53
39
|
end
|
|
54
40
|
|
|
@@ -95,7 +81,7 @@ module Spring
|
|
|
95
81
|
# Ignore SIGINT and SIGQUIT otherwise the user typing ^C or ^\ on the command line
|
|
96
82
|
# will kill the server/application.
|
|
97
83
|
def ignore_signals
|
|
98
|
-
IGNORE_SIGNALS.each { |sig| trap(sig,
|
|
84
|
+
IGNORE_SIGNALS.each { |sig| trap(sig, "IGNORE") }
|
|
99
85
|
end
|
|
100
86
|
|
|
101
87
|
def set_exit_hook
|
|
@@ -106,13 +92,13 @@ module Spring
|
|
|
106
92
|
end
|
|
107
93
|
|
|
108
94
|
def shutdown
|
|
109
|
-
@applications.values.each(&:stop)
|
|
110
|
-
|
|
111
95
|
[env.socket_path, env.pidfile_path].each do |path|
|
|
112
96
|
if path.exist?
|
|
113
97
|
path.unlink rescue nil
|
|
114
98
|
end
|
|
115
99
|
end
|
|
100
|
+
|
|
101
|
+
@applications.values.map { |a| Thread.new { a.stop } }.map(&:join)
|
|
116
102
|
end
|
|
117
103
|
|
|
118
104
|
def write_pidfile
|
|
@@ -138,13 +124,5 @@ module Spring
|
|
|
138
124
|
"spring server | #{env.app_name} | started #{distance} ago"
|
|
139
125
|
}
|
|
140
126
|
end
|
|
141
|
-
|
|
142
|
-
def watch_bundle
|
|
143
|
-
@bundle_mtime = env.bundle_mtime
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
def application_starting
|
|
147
|
-
@mutex.synchronize { exit if env.bundle_mtime != @bundle_mtime }
|
|
148
|
-
end
|
|
149
127
|
end
|
|
150
128
|
end
|
data/lib/spring/version.rb
CHANGED
|
@@ -22,7 +22,7 @@ module Spring
|
|
|
22
22
|
@files = Set.new
|
|
23
23
|
@directories = Set.new
|
|
24
24
|
@stale = false
|
|
25
|
-
@
|
|
25
|
+
@listeners = []
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def add(*items)
|
|
@@ -55,15 +55,14 @@ module Spring
|
|
|
55
55
|
@stale
|
|
56
56
|
end
|
|
57
57
|
|
|
58
|
-
def
|
|
59
|
-
@
|
|
60
|
-
@io_listener.write "." if @io_listener
|
|
58
|
+
def on_stale(&block)
|
|
59
|
+
@listeners << block
|
|
61
60
|
end
|
|
62
61
|
|
|
63
|
-
def
|
|
64
|
-
|
|
65
|
-
@
|
|
66
|
-
|
|
62
|
+
def mark_stale
|
|
63
|
+
return if stale?
|
|
64
|
+
@stale = true
|
|
65
|
+
@listeners.each(&:call)
|
|
67
66
|
end
|
|
68
67
|
|
|
69
68
|
def restart
|
data/spring.gemspec
CHANGED
|
@@ -10,7 +10,7 @@ Gem::Specification.new do |gem|
|
|
|
10
10
|
gem.email = ["j@jonathanleighton.com"]
|
|
11
11
|
gem.description = %q{Rails application preloader}
|
|
12
12
|
gem.summary = %q{Rails application preloader}
|
|
13
|
-
gem.homepage = "http://github.com/
|
|
13
|
+
gem.homepage = "http://github.com/rails/spring"
|
|
14
14
|
gem.license = "MIT"
|
|
15
15
|
|
|
16
16
|
gem.files = `git ls-files`.split($/)
|
data/test/acceptance/app_test.rb
CHANGED
|
@@ -21,28 +21,22 @@ class AppTest < ActiveSupport::TestCase
|
|
|
21
21
|
@app ||= Spring::Test::Application.new("#{TEST_ROOT}/apps/tmp")
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
def debug(artifacts)
|
|
25
|
-
artifacts = artifacts.dup
|
|
26
|
-
artifacts.delete :status
|
|
27
|
-
app.dump_streams(artifacts.delete(:command), artifacts)
|
|
28
|
-
end
|
|
29
|
-
|
|
30
24
|
def assert_output(artifacts, expected)
|
|
31
25
|
expected.each do |stream, output|
|
|
32
26
|
assert artifacts[stream].include?(output),
|
|
33
|
-
"expected #{stream} to include '#{output}'.\n\n#{debug(artifacts)}"
|
|
27
|
+
"expected #{stream} to include '#{output}'.\n\n#{app.debug(artifacts)}"
|
|
34
28
|
end
|
|
35
29
|
end
|
|
36
30
|
|
|
37
31
|
def assert_success(command, expected_output = nil)
|
|
38
32
|
artifacts = app.run(*Array(command))
|
|
39
|
-
assert artifacts[:status].success?, "expected successful exit status\n\n#{debug(artifacts)}"
|
|
33
|
+
assert artifacts[:status].success?, "expected successful exit status\n\n#{app.debug(artifacts)}"
|
|
40
34
|
assert_output artifacts, expected_output if expected_output
|
|
41
35
|
end
|
|
42
36
|
|
|
43
37
|
def assert_failure(command, expected_output = nil)
|
|
44
38
|
artifacts = app.run(*Array(command))
|
|
45
|
-
assert !artifacts[:status].success?, "expected unsuccessful exit status\n\n#{debug(artifacts)}"
|
|
39
|
+
assert !artifacts[:status].success?, "expected unsuccessful exit status\n\n#{app.debug(artifacts)}"
|
|
46
40
|
assert_output artifacts, expected_output if expected_output
|
|
47
41
|
end
|
|
48
42
|
|
|
@@ -196,7 +190,7 @@ class AppTest < ActiveSupport::TestCase
|
|
|
196
190
|
|
|
197
191
|
test "binstub when spring is uninstalled" do
|
|
198
192
|
app.run! "gem uninstall --ignore-dependencies spring"
|
|
199
|
-
File.write(app.gemfile, app.gemfile.read.
|
|
193
|
+
File.write(app.gemfile, app.gemfile.read.gsub(/gem 'spring.*/, ""))
|
|
200
194
|
assert_success "bin/rake -T", stdout: "rake db:migrate"
|
|
201
195
|
end
|
|
202
196
|
|
|
@@ -296,16 +290,29 @@ CODE
|
|
|
296
290
|
assert_success "bin/rake", stdout: "test"
|
|
297
291
|
end
|
|
298
292
|
|
|
299
|
-
test "changing the Gemfile
|
|
293
|
+
test "changing the Gemfile works" do
|
|
300
294
|
assert_success %(bin/rails runner 'require "sqlite3"')
|
|
301
295
|
|
|
302
296
|
File.write(app.gemfile, app.gemfile.read.sub(%{gem 'sqlite3'}, %{# gem 'sqlite3'}))
|
|
303
|
-
app.bundle
|
|
304
|
-
|
|
305
297
|
app.await_reload
|
|
298
|
+
|
|
306
299
|
assert_failure %(bin/rails runner 'require "sqlite3"'), stderr: "sqlite3"
|
|
307
300
|
end
|
|
308
301
|
|
|
302
|
+
test "changing the Gemfile works when spring calls into itself" do
|
|
303
|
+
File.write(app.path("script.rb"), <<-CODE)
|
|
304
|
+
gemfile = Rails.root.join("Gemfile")
|
|
305
|
+
File.write(gemfile, "\#{gemfile.read}gem 'devise'\\n")
|
|
306
|
+
Bundler.with_clean_env do
|
|
307
|
+
system(#{app.env.inspect}, "bundle install")
|
|
308
|
+
end
|
|
309
|
+
output = `\#{Rails.root.join('bin/rails')} runner 'require "devise"; puts "done";'`
|
|
310
|
+
exit output == "done\n"
|
|
311
|
+
CODE
|
|
312
|
+
|
|
313
|
+
assert_success [%(bin/rails runner 'load Rails.root.join("script.rb")'), timeout: 60]
|
|
314
|
+
end
|
|
315
|
+
|
|
309
316
|
test "changing the environment between runs" do
|
|
310
317
|
File.write(app.application_config, "#{app.application_config.read}\nENV['BAR'] = 'bar'")
|
|
311
318
|
|
data/test/acceptance/helper.rb
CHANGED
|
@@ -202,6 +202,12 @@ module Spring
|
|
|
202
202
|
output
|
|
203
203
|
end
|
|
204
204
|
|
|
205
|
+
def debug(artifacts)
|
|
206
|
+
artifacts = artifacts.dup
|
|
207
|
+
artifacts.delete :status
|
|
208
|
+
dump_streams(artifacts.delete(:command), artifacts)
|
|
209
|
+
end
|
|
210
|
+
|
|
205
211
|
def await_reload
|
|
206
212
|
raise "no pid" if @application_pids.nil? || @application_pids.empty?
|
|
207
213
|
|
|
@@ -212,13 +218,15 @@ module Spring
|
|
|
212
218
|
|
|
213
219
|
def run!(*args)
|
|
214
220
|
artifacts = run(*args)
|
|
215
|
-
|
|
221
|
+
unless artifacts[:status].success?
|
|
222
|
+
raise "command failed\n\n#{debug(artifacts)}"
|
|
223
|
+
end
|
|
216
224
|
artifacts
|
|
217
225
|
end
|
|
218
226
|
|
|
219
227
|
def bundle
|
|
220
228
|
run! "(gem list bundler | grep bundler) || gem install bundler", timeout: nil
|
|
221
|
-
run! "bundle
|
|
229
|
+
run! "bundle update", timeout: nil
|
|
222
230
|
end
|
|
223
231
|
|
|
224
232
|
private
|
|
@@ -238,14 +246,22 @@ module Spring
|
|
|
238
246
|
@version_constraint = version_constraint
|
|
239
247
|
@version = RailsVersion.new(version_constraint.split(' ').last)
|
|
240
248
|
@application = Application.new(root)
|
|
249
|
+
@bundled = false
|
|
241
250
|
end
|
|
242
251
|
|
|
243
252
|
def root
|
|
244
|
-
"#{TEST_ROOT}/apps/rails-#{version.major}-#{version.minor}"
|
|
253
|
+
"#{TEST_ROOT}/apps/rails-#{version.major}-#{version.minor}-spring-#{Spring::VERSION}"
|
|
245
254
|
end
|
|
246
255
|
|
|
247
256
|
def system(command)
|
|
248
|
-
|
|
257
|
+
if ENV["SPRING_DEBUG"]
|
|
258
|
+
puts "$ #{command}\n"
|
|
259
|
+
else
|
|
260
|
+
command = "(#{command}) > /dev/null"
|
|
261
|
+
end
|
|
262
|
+
|
|
263
|
+
Kernel.system(command) or raise "command failed: #{command}"
|
|
264
|
+
puts if ENV["SPRING_DEBUG"]
|
|
249
265
|
end
|
|
250
266
|
|
|
251
267
|
# Sporadic SSL errors keep causing test failures so there are anti-SSL workarounds here
|
|
@@ -263,6 +279,8 @@ module Spring
|
|
|
263
279
|
FileUtils.mkdir_p(application.user_home)
|
|
264
280
|
FileUtils.rm_rf(application.path("test/performance"))
|
|
265
281
|
|
|
282
|
+
File.write(application.gemfile, "#{application.gemfile.read}gem 'spring', '#{Spring::VERSION}'\n")
|
|
283
|
+
|
|
266
284
|
if version.needs_testunit?
|
|
267
285
|
File.write(application.gemfile, "#{application.gemfile.read}gem 'spring-commands-testunit'\n")
|
|
268
286
|
end
|
|
@@ -274,7 +292,8 @@ module Spring
|
|
|
274
292
|
end
|
|
275
293
|
end
|
|
276
294
|
|
|
277
|
-
|
|
295
|
+
install_spring
|
|
296
|
+
|
|
278
297
|
application.run! "bundle exec rails g scaffold post title:string"
|
|
279
298
|
application.run! "bundle exec rake db:migrate db:test:clone"
|
|
280
299
|
end
|
|
@@ -284,28 +303,26 @@ module Spring
|
|
|
284
303
|
end
|
|
285
304
|
|
|
286
305
|
def install_spring
|
|
287
|
-
|
|
288
|
-
# Need to do this here too because the app may have been generated with
|
|
289
|
-
# a different ruby
|
|
290
|
-
application.bundle
|
|
306
|
+
return if @installed
|
|
291
307
|
|
|
292
|
-
|
|
293
|
-
|
|
308
|
+
system("gem build spring.gemspec 2>&1")
|
|
309
|
+
application.run! "gem install ../../../spring-#{Spring::VERSION}.gem", timeout: nil
|
|
294
310
|
|
|
295
|
-
|
|
311
|
+
application.bundle
|
|
296
312
|
|
|
297
|
-
|
|
298
|
-
FileUtils.cp_r application.path("bin_original"), application.path("bin")
|
|
299
|
-
end
|
|
313
|
+
FileUtils.rm_rf application.path("bin")
|
|
300
314
|
|
|
301
|
-
|
|
302
|
-
|
|
315
|
+
if application.path("bin_original").exist?
|
|
316
|
+
FileUtils.cp_r application.path("bin_original"), application.path("bin")
|
|
303
317
|
end
|
|
318
|
+
|
|
319
|
+
application.run! "#{application.spring} binstub --all"
|
|
320
|
+
@installed = true
|
|
304
321
|
end
|
|
305
322
|
|
|
306
323
|
def copy_to(path)
|
|
307
|
-
|
|
308
|
-
|
|
324
|
+
system("rm -rf #{path}")
|
|
325
|
+
system("cp -r #{application.root} #{path}")
|
|
309
326
|
end
|
|
310
327
|
end
|
|
311
328
|
end
|
data/test/unit/watcher_test.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require "helper"
|
|
2
2
|
require "tmpdir"
|
|
3
3
|
require "fileutils"
|
|
4
|
+
require "timeout"
|
|
4
5
|
require "active_support/core_ext/numeric/time"
|
|
5
6
|
require "spring/watcher"
|
|
6
7
|
require "spring/watcher/polling"
|
|
@@ -120,22 +121,25 @@ module WatcherTests
|
|
|
120
121
|
assert_stale
|
|
121
122
|
end
|
|
122
123
|
|
|
123
|
-
def
|
|
124
|
+
def test_on_stale
|
|
124
125
|
file = "#{@dir}/omg"
|
|
125
126
|
touch file, Time.now - 2.seconds
|
|
126
127
|
|
|
127
128
|
watcher.add file
|
|
128
129
|
watcher.start
|
|
129
130
|
|
|
130
|
-
|
|
131
|
+
stale = false
|
|
132
|
+
watcher.on_stale { stale = true }
|
|
131
133
|
|
|
132
|
-
|
|
133
|
-
sleep LATENCY * 3
|
|
134
|
-
touch file, Time.now
|
|
135
|
-
}
|
|
134
|
+
touch file, Time.now
|
|
136
135
|
|
|
137
|
-
|
|
138
|
-
assert
|
|
136
|
+
Timeout.timeout(1) { sleep 0.01 until stale }
|
|
137
|
+
assert stale
|
|
138
|
+
|
|
139
|
+
# Check that we only get notified once
|
|
140
|
+
stale = false
|
|
141
|
+
sleep LATENCY * 3
|
|
142
|
+
assert !stale
|
|
139
143
|
end
|
|
140
144
|
|
|
141
145
|
def test_add_relative_path
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: spring
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.1.0
|
|
4
|
+
version: 1.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jon Leighton
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2014-01-
|
|
11
|
+
date: 2014-01-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -55,8 +55,10 @@ files:
|
|
|
55
55
|
- Rakefile
|
|
56
56
|
- bin/spring
|
|
57
57
|
- lib/spring/application.rb
|
|
58
|
+
- lib/spring/application/boot.rb
|
|
58
59
|
- lib/spring/application_manager.rb
|
|
59
60
|
- lib/spring/binstub.rb
|
|
61
|
+
- lib/spring/boot.rb
|
|
60
62
|
- lib/spring/client.rb
|
|
61
63
|
- lib/spring/client/binstub.rb
|
|
62
64
|
- lib/spring/client/command.rb
|
|
@@ -92,7 +94,7 @@ files:
|
|
|
92
94
|
- test/unit/commands_test.rb
|
|
93
95
|
- test/unit/process_title_updater_test.rb
|
|
94
96
|
- test/unit/watcher_test.rb
|
|
95
|
-
homepage: http://github.com/
|
|
97
|
+
homepage: http://github.com/rails/spring
|
|
96
98
|
licenses:
|
|
97
99
|
- MIT
|
|
98
100
|
metadata: {}
|
|
@@ -107,9 +109,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
107
109
|
version: '0'
|
|
108
110
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
109
111
|
requirements:
|
|
110
|
-
- - '
|
|
112
|
+
- - '>='
|
|
111
113
|
- !ruby/object:Gem::Version
|
|
112
|
-
version:
|
|
114
|
+
version: '0'
|
|
113
115
|
requirements: []
|
|
114
116
|
rubyforge_project:
|
|
115
117
|
rubygems_version: 2.1.9
|