spring 1.0.0 → 1.3.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/.gitignore +1 -0
- data/.travis.yml +6 -1
- data/CHANGELOG.md +81 -0
- data/CONTRIBUTING.md +52 -0
- data/Gemfile +0 -2
- data/README.md +137 -54
- data/Rakefile +1 -1
- data/bin/spring +17 -0
- data/lib/spring/application/boot.rb +18 -0
- data/lib/spring/application.rb +183 -76
- data/lib/spring/application_manager.rb +54 -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 +2 -3
- data/lib/spring/client/run.rb +69 -11
- data/lib/spring/client/status.rb +2 -2
- data/lib/spring/client/stop.rb +6 -21
- data/lib/spring/client/version.rb +11 -0
- data/lib/spring/client.rb +8 -5
- data/lib/spring/command_wrapper.rb +82 -0
- data/lib/spring/commands/rails.rb +44 -13
- data/lib/spring/commands/rake.rb +0 -4
- data/lib/spring/commands.rb +14 -4
- data/lib/spring/configuration.rb +11 -2
- data/lib/spring/env.rb +36 -10
- data/lib/spring/server.rb +9 -35
- data/lib/spring/sid.rb +1 -1
- data/lib/spring/test/acceptance_test.rb +346 -0
- data/lib/spring/test/application.rb +217 -0
- data/lib/spring/test/application_generator.rb +121 -0
- data/lib/spring/test/rails_version.rb +40 -0
- data/lib/spring/test/watcher_test.rb +167 -0
- data/lib/spring/test.rb +18 -0
- data/lib/spring/version.rb +1 -1
- data/lib/spring/watcher/abstract.rb +7 -8
- data/lib/spring/watcher/polling.rb +2 -0
- data/lib/spring/watcher.rb +4 -8
- data/spring.gemspec +2 -2
- data/test/acceptance_test.rb +4 -0
- data/test/helper.rb +2 -2
- data/test/unit/client/version_test.rb +14 -0
- data/test/unit/commands_test.rb +23 -1
- data/test/unit/watcher_test.rb +2 -184
- metadata +30 -17
- data/lib/spring/watcher/listen.rb +0 -52
- data/test/acceptance/app_test.rb +0 -511
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 = Set.new
|
|
15
|
+
@preloaded = false
|
|
16
|
+
@state = :initialized
|
|
17
|
+
@interrupt = IO.pipe
|
|
18
|
+
end
|
|
8
19
|
|
|
9
|
-
def
|
|
10
|
-
|
|
11
|
-
@
|
|
12
|
-
@
|
|
13
|
-
|
|
14
|
-
@preloaded = false
|
|
15
|
-
@mutex = Mutex.new
|
|
16
|
-
@waiting = 0
|
|
17
|
-
@exiting = false
|
|
20
|
+
def state(val)
|
|
21
|
+
return if exiting?
|
|
22
|
+
log "#{@state} -> #{val}"
|
|
23
|
+
@state = val
|
|
24
|
+
end
|
|
18
25
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@
|
|
26
|
+
def state!(val)
|
|
27
|
+
state val
|
|
28
|
+
@interrupt.last.write "."
|
|
29
|
+
end
|
|
30
|
+
|
|
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
|
|
@@ -48,47 +91,54 @@ module Spring
|
|
|
48
91
|
|
|
49
92
|
require Spring.application_root_path.join("config", "environment")
|
|
50
93
|
|
|
94
|
+
@original_cache_classes = Rails.application.config.cache_classes
|
|
51
95
|
Rails.application.config.cache_classes = false
|
|
96
|
+
|
|
52
97
|
disconnect_database
|
|
53
98
|
|
|
99
|
+
@preloaded = :success
|
|
100
|
+
rescue Exception => e
|
|
101
|
+
@preloaded = :failure
|
|
102
|
+
watcher.add e.backtrace.map { |line| line.match(/^(.*)\:\d+\:in /)[1] }
|
|
103
|
+
raise e unless initialized?
|
|
104
|
+
ensure
|
|
54
105
|
watcher.add loaded_application_features
|
|
55
106
|
watcher.add Spring.gemfile, "#{Spring.gemfile}.lock"
|
|
56
|
-
watcher.add Rails.application.paths["config/initializers"]
|
|
57
107
|
|
|
58
|
-
|
|
108
|
+
if defined?(Rails) && Rails.application
|
|
109
|
+
watcher.add Rails.application.paths["config/initializers"]
|
|
110
|
+
watcher.add Rails.application.paths["config/database"]
|
|
111
|
+
if secrets_path = Rails.application.paths["config/secrets"]
|
|
112
|
+
watcher.add secrets_path
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def eager_preload
|
|
118
|
+
with_pty { preload }
|
|
59
119
|
end
|
|
60
120
|
|
|
61
121
|
def run
|
|
62
|
-
|
|
63
|
-
|
|
122
|
+
state :running
|
|
123
|
+
manager.puts
|
|
64
124
|
|
|
65
125
|
loop do
|
|
66
|
-
IO.select
|
|
67
|
-
|
|
68
|
-
if
|
|
69
|
-
|
|
70
|
-
manager.close
|
|
71
|
-
@exiting = true
|
|
72
|
-
try_exit
|
|
73
|
-
sleep
|
|
126
|
+
IO.select [manager, @interrupt.first]
|
|
127
|
+
|
|
128
|
+
if terminating? || watcher_stale? || preload_failed?
|
|
129
|
+
exit
|
|
74
130
|
else
|
|
75
131
|
serve manager.recv_io(UNIXSocket)
|
|
76
132
|
end
|
|
77
133
|
end
|
|
78
134
|
end
|
|
79
135
|
|
|
80
|
-
def try_exit
|
|
81
|
-
@mutex.synchronize {
|
|
82
|
-
exit if exiting? && @waiting == 0
|
|
83
|
-
}
|
|
84
|
-
end
|
|
85
|
-
|
|
86
136
|
def serve(client)
|
|
87
137
|
log "got client"
|
|
88
138
|
manager.puts
|
|
89
139
|
|
|
90
|
-
streams = 3.times.map { client.recv_io }
|
|
91
|
-
[STDOUT, STDERR].zip(streams).each { |a, b| a.reopen(b) }
|
|
140
|
+
stdout, stderr, stdin = streams = 3.times.map { client.recv_io }
|
|
141
|
+
[STDOUT, STDERR, STDIN].zip(streams).each { |a, b| a.reopen(b) }
|
|
92
142
|
|
|
93
143
|
preload unless preloaded?
|
|
94
144
|
|
|
@@ -104,74 +154,84 @@ module Spring
|
|
|
104
154
|
end
|
|
105
155
|
|
|
106
156
|
pid = fork {
|
|
107
|
-
Process.setsid
|
|
108
|
-
STDIN.reopen(streams.last)
|
|
109
157
|
IGNORE_SIGNALS.each { |sig| trap(sig, "DEFAULT") }
|
|
158
|
+
trap("TERM", "DEFAULT")
|
|
110
159
|
|
|
111
160
|
ARGV.replace(args)
|
|
161
|
+
$0 = command.exec_name
|
|
112
162
|
|
|
113
163
|
# Delete all env vars which are unchanged from before spring started
|
|
114
|
-
|
|
164
|
+
original_env.each { |k, v| ENV.delete k if ENV[k] == v }
|
|
115
165
|
|
|
116
166
|
# Load in the current env vars, except those which *were* changed when spring started
|
|
117
167
|
env.each { |k, v| ENV[k] ||= v }
|
|
118
168
|
|
|
169
|
+
# requiring is faster, so if config.cache_classes was true in
|
|
170
|
+
# the environment's config file, then we can respect that from
|
|
171
|
+
# here on as we no longer need constant reloading.
|
|
172
|
+
if @original_cache_classes
|
|
173
|
+
ActiveSupport::Dependencies.mechanism = :require
|
|
174
|
+
Rails.application.config.cache_classes = true
|
|
175
|
+
end
|
|
176
|
+
|
|
119
177
|
connect_database
|
|
120
178
|
srand
|
|
121
179
|
|
|
122
180
|
invoke_after_fork_callbacks
|
|
123
181
|
shush_backtraces
|
|
124
182
|
|
|
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
|
|
183
|
+
command.call
|
|
135
184
|
}
|
|
136
185
|
|
|
137
186
|
disconnect_database
|
|
138
|
-
|
|
187
|
+
reset_streams
|
|
139
188
|
|
|
140
189
|
log "forked #{pid}"
|
|
141
190
|
manager.puts pid
|
|
142
191
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
_, status = Process.wait2 pid
|
|
148
|
-
log "#{pid} exited with #{status.exitstatus}"
|
|
192
|
+
wait pid, streams, client
|
|
193
|
+
rescue Exception => e
|
|
194
|
+
log "exception: #{e}"
|
|
195
|
+
manager.puts unless pid
|
|
149
196
|
|
|
197
|
+
if streams && !e.is_a?(SystemExit)
|
|
198
|
+
print_exception(stderr, e)
|
|
150
199
|
streams.each(&:close)
|
|
151
|
-
|
|
152
|
-
client.close
|
|
153
|
-
|
|
154
|
-
@mutex.synchronize { @waiting -= 1 }
|
|
155
|
-
try_exit
|
|
156
|
-
}
|
|
200
|
+
end
|
|
157
201
|
|
|
158
|
-
|
|
159
|
-
log "exception: #{e}"
|
|
160
|
-
streams.each(&:close) if streams
|
|
161
|
-
client.puts(1)
|
|
202
|
+
client.puts(1) if pid
|
|
162
203
|
client.close
|
|
163
|
-
|
|
204
|
+
end
|
|
205
|
+
|
|
206
|
+
def terminate
|
|
207
|
+
if exiting?
|
|
208
|
+
# Ensure that we do not ignore subsequent termination attempts
|
|
209
|
+
log "forced exit"
|
|
210
|
+
@waiting.each { |pid| Process.kill("TERM", pid) }
|
|
211
|
+
Kernel.exit
|
|
212
|
+
else
|
|
213
|
+
state! :terminating
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
def exit
|
|
218
|
+
state :exiting
|
|
219
|
+
manager.shutdown(:RDWR)
|
|
220
|
+
exit_if_finished
|
|
221
|
+
sleep
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
def exit_if_finished
|
|
225
|
+
@mutex.synchronize {
|
|
226
|
+
Kernel.exit if exiting? && @waiting.empty?
|
|
227
|
+
}
|
|
164
228
|
end
|
|
165
229
|
|
|
166
230
|
# The command might need to require some files in the
|
|
167
231
|
# main process so that they are cached. For example a test command wants to
|
|
168
232
|
# load the helper file once and have it cached.
|
|
169
233
|
def setup(command)
|
|
170
|
-
|
|
171
|
-
@setup << command.class
|
|
172
|
-
|
|
173
|
-
if command.respond_to?(:setup)
|
|
174
|
-
command.setup
|
|
234
|
+
if command.setup
|
|
175
235
|
watcher.add loaded_application_features # loaded features may have changed
|
|
176
236
|
end
|
|
177
237
|
end
|
|
@@ -183,15 +243,16 @@ module Spring
|
|
|
183
243
|
end
|
|
184
244
|
|
|
185
245
|
def loaded_application_features
|
|
186
|
-
|
|
246
|
+
root = Spring.application_root_path.to_s
|
|
247
|
+
$LOADED_FEATURES.select { |f| f.start_with?(root) }
|
|
187
248
|
end
|
|
188
249
|
|
|
189
250
|
def disconnect_database
|
|
190
|
-
ActiveRecord::Base.remove_connection if
|
|
251
|
+
ActiveRecord::Base.remove_connection if active_record_configured?
|
|
191
252
|
end
|
|
192
253
|
|
|
193
254
|
def connect_database
|
|
194
|
-
ActiveRecord::Base.establish_connection if
|
|
255
|
+
ActiveRecord::Base.establish_connection if active_record_configured?
|
|
195
256
|
end
|
|
196
257
|
|
|
197
258
|
# This feels very naughty
|
|
@@ -209,7 +270,53 @@ module Spring
|
|
|
209
270
|
end
|
|
210
271
|
end
|
|
211
272
|
end
|
|
273
|
+
private :raise
|
|
212
274
|
end
|
|
213
275
|
end
|
|
276
|
+
|
|
277
|
+
def print_exception(stream, error)
|
|
278
|
+
first, rest = error.backtrace.first, error.backtrace.drop(1)
|
|
279
|
+
stream.puts("#{first}: #{error} (#{error.class})")
|
|
280
|
+
rest.each { |line| stream.puts("\tfrom #{line}") }
|
|
281
|
+
end
|
|
282
|
+
|
|
283
|
+
def with_pty
|
|
284
|
+
PTY.open do |master, slave|
|
|
285
|
+
[STDOUT, STDERR, STDIN].each { |s| s.reopen slave }
|
|
286
|
+
Thread.new { master.read }
|
|
287
|
+
yield
|
|
288
|
+
reset_streams
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def reset_streams
|
|
293
|
+
[STDOUT, STDERR].each { |stream| stream.reopen(spring_env.log_file) }
|
|
294
|
+
STDIN.reopen("/dev/null")
|
|
295
|
+
end
|
|
296
|
+
|
|
297
|
+
def wait(pid, streams, client)
|
|
298
|
+
@mutex.synchronize { @waiting << pid }
|
|
299
|
+
|
|
300
|
+
# Wait in a separate thread so we can run multiple commands at once
|
|
301
|
+
Thread.new {
|
|
302
|
+
begin
|
|
303
|
+
_, status = Process.wait2 pid
|
|
304
|
+
log "#{pid} exited with #{status.exitstatus}"
|
|
305
|
+
|
|
306
|
+
streams.each(&:close)
|
|
307
|
+
client.puts(status.exitstatus)
|
|
308
|
+
client.close
|
|
309
|
+
ensure
|
|
310
|
+
@mutex.synchronize { @waiting.delete pid }
|
|
311
|
+
exit_if_finished
|
|
312
|
+
end
|
|
313
|
+
}
|
|
314
|
+
end
|
|
315
|
+
|
|
316
|
+
private
|
|
317
|
+
|
|
318
|
+
def active_record_configured?
|
|
319
|
+
defined?(ActiveRecord::Base) && ActiveRecord::Base.configurations.any?
|
|
320
|
+
end
|
|
214
321
|
end
|
|
215
322
|
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,61 @@ module Spring
|
|
|
78
76
|
end
|
|
79
77
|
|
|
80
78
|
def stop
|
|
81
|
-
|
|
79
|
+
log "stopping"
|
|
80
|
+
@state = :stopping
|
|
81
|
+
|
|
82
|
+
if pid
|
|
83
|
+
Process.kill('TERM', pid)
|
|
84
|
+
Process.wait(pid)
|
|
85
|
+
end
|
|
86
|
+
rescue Errno::ESRCH, Errno::ECHILD
|
|
87
|
+
# Don't care
|
|
82
88
|
end
|
|
83
89
|
|
|
84
90
|
private
|
|
85
91
|
|
|
86
92
|
def start_child(preload = false)
|
|
87
|
-
server.application_starting
|
|
88
|
-
|
|
89
93
|
@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
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
95
|
+
Bundler.with_clean_env do
|
|
96
|
+
@pid = Process.spawn(
|
|
97
|
+
{
|
|
98
|
+
"RAILS_ENV" => app_env,
|
|
99
|
+
"RACK_ENV" => app_env,
|
|
100
|
+
"SPRING_ORIGINAL_ENV" => JSON.dump(Spring::ORIGINAL_ENV),
|
|
101
|
+
"SPRING_PRELOAD" => preload ? "1" : "0"
|
|
102
|
+
},
|
|
103
|
+
"ruby",
|
|
104
|
+
"-I", File.expand_path("../..", __FILE__),
|
|
105
|
+
"-e", "require 'spring/application/boot'",
|
|
106
|
+
3 => child_socket
|
|
107
|
+
)
|
|
108
|
+
end
|
|
100
109
|
|
|
101
|
-
|
|
102
|
-
app.preload if preload
|
|
103
|
-
app.run
|
|
104
|
-
}
|
|
110
|
+
start_wait_thread(pid, child) if child.gets
|
|
105
111
|
child_socket.close
|
|
106
112
|
end
|
|
107
113
|
|
|
108
|
-
def start_wait_thread
|
|
109
|
-
|
|
110
|
-
Thread.current.abort_on_exception = true
|
|
114
|
+
def start_wait_thread(pid, child)
|
|
115
|
+
Process.detach(pid)
|
|
111
116
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
117
|
+
Thread.new {
|
|
118
|
+
# The recv can raise an ECONNRESET, killing the thread, but that's ok
|
|
119
|
+
# as if it does we're no longer interested in the child
|
|
120
|
+
loop do
|
|
121
|
+
IO.select([child])
|
|
122
|
+
break if child.recv(1, Socket::MSG_PEEK).empty?
|
|
123
|
+
sleep 0.01
|
|
119
124
|
end
|
|
125
|
+
|
|
126
|
+
log "child #{pid} shutdown"
|
|
127
|
+
|
|
128
|
+
synchronize {
|
|
129
|
+
if @pid == pid
|
|
130
|
+
@pid = nil
|
|
131
|
+
restart
|
|
132
|
+
end
|
|
133
|
+
}
|
|
120
134
|
}
|
|
121
135
|
end
|
|
122
136
|
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
command = File.basename($0)
|
|
2
|
+
bin_path = File.expand_path("../../../bin/spring", __FILE__)
|
|
3
|
+
|
|
4
|
+
if command == "spring"
|
|
5
|
+
load bin_path
|
|
6
|
+
else
|
|
7
|
+
disable = ENV["DISABLE_SPRING"]
|
|
8
|
+
|
|
9
|
+
if Process.respond_to?(:fork) && (disable.nil? || disable.empty? || disable == "0")
|
|
10
|
+
ARGV.unshift(command)
|
|
11
|
+
load bin_path
|
|
12
|
+
end
|
|
13
|
+
end
|