spring 1.2.0 → 4.7.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.
Files changed (53) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE.txt +3 -2
  3. data/README.md +148 -42
  4. data/bin/spring +2 -1
  5. data/lib/spring/application/boot.rb +11 -4
  6. data/lib/spring/application.rb +178 -50
  7. data/lib/spring/application_manager.rb +17 -9
  8. data/lib/spring/boot.rb +2 -0
  9. data/lib/spring/client/binstub.rb +44 -45
  10. data/lib/spring/client/help.rb +1 -1
  11. data/lib/spring/client/rails.rb +6 -5
  12. data/lib/spring/client/run.rb +187 -41
  13. data/lib/spring/client/server.rb +18 -0
  14. data/lib/spring/client/status.rb +2 -2
  15. data/lib/spring/client/stop.rb +7 -22
  16. data/lib/spring/client.rb +11 -1
  17. data/lib/spring/command_wrapper.rb +0 -4
  18. data/lib/spring/commands/rails.rb +21 -0
  19. data/lib/spring/commands.rb +11 -4
  20. data/lib/spring/configuration.rb +49 -2
  21. data/lib/spring/env.rb +43 -12
  22. data/lib/spring/errors.rb +1 -1
  23. data/lib/spring/failsafe_thread.rb +14 -0
  24. data/lib/spring/json.rb +30 -33
  25. data/lib/spring/process_title_updater.rb +2 -2
  26. data/lib/spring/server.rb +45 -13
  27. data/lib/spring/version.rb +1 -1
  28. data/lib/spring/watcher/abstract.rb +44 -13
  29. data/lib/spring/watcher/polling.rb +48 -11
  30. data/lib/spring/watcher.rb +1 -1
  31. metadata +14 -71
  32. data/.gitignore +0 -19
  33. data/.travis.yml +0 -11
  34. data/CHANGELOG.md +0 -188
  35. data/CONTRIBUTING.md +0 -52
  36. data/Gemfile +0 -4
  37. data/Rakefile +0 -21
  38. data/lib/spring/sid.rb +0 -42
  39. data/lib/spring/test/acceptance_test.rb +0 -333
  40. data/lib/spring/test/application.rb +0 -217
  41. data/lib/spring/test/application_generator.rb +0 -121
  42. data/lib/spring/test/rails_version.rb +0 -40
  43. data/lib/spring/test/watcher_test.rb +0 -167
  44. data/lib/spring/test.rb +0 -15
  45. data/spring.gemspec +0 -23
  46. data/test/acceptance_test.rb +0 -4
  47. data/test/apps/.gitignore +0 -3
  48. data/test/helper.rb +0 -7
  49. data/test/unit/client/help_test.rb +0 -60
  50. data/test/unit/client/version_test.rb +0 -14
  51. data/test/unit/commands_test.rb +0 -59
  52. data/test/unit/process_title_updater_test.rb +0 -24
  53. data/test/unit/watcher_test.rb +0 -9
@@ -1,27 +1,63 @@
1
1
  require "rbconfig"
2
2
  require "socket"
3
+ require "bundler"
3
4
 
4
5
  module Spring
5
6
  module Client
6
7
  class Run < Command
7
- FORWARDED_SIGNALS = %w(INT QUIT USR1 USR2 INFO) & Signal.list.keys
8
- TIMEOUT = 1
8
+ FORWARDED_SIGNALS = %w(INT QUIT USR1 USR2 INFO WINCH) & Signal.list.keys
9
+ ServerReadTimeout = Class.new(StandardError)
10
+
11
+ attr_reader :server
9
12
 
10
13
  def initialize(args)
11
14
  super
12
- @signal_queue = []
15
+
16
+ @signal_queue = []
17
+ @server_booted = false
13
18
  end
14
19
 
15
20
  def log(message)
16
21
  env.log "[client] #{message}"
17
22
  end
18
23
 
19
- def server
20
- @server ||= UNIXSocket.open(env.socket_name)
24
+ def connect
25
+ @server = UNIXSocket.open(env.socket_name)
21
26
  end
22
27
 
23
28
  def call
24
- boot_server unless env.server_running?
29
+ begin
30
+ connect
31
+ rescue Errno::ENOENT, Errno::ECONNRESET, Errno::ECONNREFUSED
32
+ cold_run
33
+ else
34
+ warm_run
35
+ end
36
+ ensure
37
+ server.close if server
38
+ end
39
+
40
+ def warm_run
41
+ run
42
+ rescue CommandNotFound
43
+ require "spring/commands"
44
+
45
+ if Spring.command?(args.first)
46
+ # Command installed since Spring started
47
+ stop_server
48
+ cold_run
49
+ else
50
+ raise
51
+ end
52
+ end
53
+
54
+ def cold_run
55
+ boot_server
56
+ connect
57
+ run
58
+ end
59
+
60
+ def run
25
61
  verify_server_version
26
62
 
27
63
  application, client = UNIXSocket.pair
@@ -31,43 +67,123 @@ module Spring
31
67
  run_command(client, application)
32
68
  rescue Errno::ECONNRESET
33
69
  exit 1
34
- ensure
35
- server.close if @server
36
70
  end
37
71
 
38
72
  def boot_server
39
73
  env.socket_path.unlink if env.socket_path.exist?
40
74
 
41
- pid = fork {
42
- require "spring/server"
43
- Spring::Server.boot
44
- }
75
+ pid = Process.spawn(server_process_env, env.server_command, out: File::NULL)
76
+ timeout = Time.now + Spring.boot_timeout
77
+
78
+ @server_booted = true
45
79
 
46
80
  until env.socket_path.exist?
47
81
  _, status = Process.waitpid2(pid, Process::WNOHANG)
48
- exit status.exitstatus if status
82
+
83
+ if status
84
+ exit status.exitstatus
85
+ elsif Time.now > timeout
86
+ $stderr.puts "Starting Spring server with `#{env.server_command}` " \
87
+ "timed out after #{Spring.boot_timeout} seconds"
88
+ exit 1
89
+ end
90
+
49
91
  sleep 0.1
50
92
  end
51
93
  end
52
94
 
95
+ def server_booted?
96
+ @server_booted
97
+ end
98
+
99
+ def gem_env
100
+ bundle = Bundler.bundle_path.to_s
101
+ paths = Gem.path + ENV["GEM_PATH"].to_s.split(File::PATH_SEPARATOR)
102
+
103
+ {
104
+ "GEM_PATH" => [bundle, *paths].uniq.join(File::PATH_SEPARATOR),
105
+ "GEM_HOME" => bundle
106
+ }
107
+ end
108
+
109
+ def reset_env
110
+ ENV.slice(*Spring.reset_on_env)
111
+ end
112
+
113
+ def server_process_env
114
+ reset_env.merge(gem_env)
115
+ end
116
+
117
+ def stop_server
118
+ server.close
119
+ @server = nil
120
+ env.stop
121
+ end
122
+
53
123
  def verify_server_version
54
- server_version = server.gets.chomp
124
+ begin
125
+ line = read_server_line
126
+ rescue ServerReadTimeout
127
+ if waiting_for_server_boot?
128
+ begin
129
+ # Try again, but with same timeout as booting, as server might still be booting
130
+ # from another client starting it.
131
+ line = read_server_line(Spring.boot_timeout)
132
+ rescue ServerReadTimeout
133
+ reboot_or_raise_connection_error
134
+ return
135
+ end
136
+ else
137
+ reboot_or_raise_connection_error
138
+ return
139
+ end
140
+ end
141
+
142
+ if line.nil?
143
+ reboot_or_raise_connection_error
144
+ return
145
+ end
146
+
147
+ server_version = line.chomp
55
148
  if server_version != env.version
56
- $stderr.puts <<-ERROR
57
- There is a version mismatch between the spring client and the server.
58
- You should restart the server and make sure to use the same version.
149
+ $stderr.puts "There is a version mismatch between the Spring client " \
150
+ "(#{env.version}) and the server (#{server_version})."
59
151
 
60
- CLIENT: #{env.version}, SERVER: #{server_version}
61
- ERROR
62
- exit 1
152
+ if server_booted?
153
+ $stderr.puts "We already tried to reboot the server, but the mismatch is still present."
154
+ exit 1
155
+ else
156
+ $stderr.puts "Restarting to resolve."
157
+ stop_server
158
+ cold_run
159
+ end
160
+ end
161
+ end
162
+
163
+ def read_server_line(timeout = Spring.connect_timeout)
164
+ raise ServerReadTimeout if IO.select([server], [], [], timeout).nil?
165
+
166
+ server.gets
167
+ end
168
+
169
+ def waiting_for_server_boot?
170
+ !server_booted? && env.server_running?
171
+ end
172
+
173
+ def reboot_or_raise_connection_error
174
+ if server_booted?
175
+ raise "Error connecting to Spring server"
176
+ else
177
+ stop_server
178
+ cold_run
63
179
  end
64
180
  end
65
181
 
66
182
  def connect_to_application(client)
67
183
  server.send_io client
68
- send_json server, "args" => args, "default_rails_env" => default_rails_env
184
+ send_json server, "args" => args, "default_rails_env" => default_rails_env, "spawn_env" => spawn_env, "reset_env" => reset_env
69
185
 
70
- if IO.select([server], [], [], TIMEOUT)
186
+ if IO.select([server], [], [], Spring.connect_timeout)
71
187
  server.gets or raise CommandNotFound
72
188
  else
73
189
  raise "Error connecting to Spring server"
@@ -75,12 +191,17 @@ ERROR
75
191
  end
76
192
 
77
193
  def run_command(client, application)
78
- log "sending command"
79
-
80
194
  application.send_io STDOUT
81
195
  application.send_io STDERR
82
196
  application.send_io STDIN
83
197
 
198
+ log "waiting for the application to be preloaded"
199
+ preload_status = application.gets
200
+ preload_status = preload_status.chomp if preload_status
201
+ log "app preload status: #{preload_status}"
202
+ exit 1 if preload_status == "1"
203
+
204
+ log "sending command"
84
205
  send_json application, "args" => args, "env" => ENV.to_hash
85
206
 
86
207
  pid = server.gets
@@ -94,12 +215,19 @@ ERROR
94
215
  if pid && !pid.empty?
95
216
  log "got pid: #{pid}"
96
217
 
97
- forward_signals(pid.to_i)
98
- status = application.read.to_i
218
+ suspend_resume_on_tstp_cont(pid)
219
+
220
+ forward_signals(application)
221
+ status = application.read
222
+ log "got exit status #{status.inspect}"
99
223
 
100
- log "got exit status #{status}"
224
+ # Status should always be an integer. If it is empty, something unexpected must have happened to the server.
225
+ if status.to_s.strip.empty?
226
+ log "unexpected empty exit status, app crashed?"
227
+ exit 1
228
+ end
101
229
 
102
- exit status
230
+ exit status.to_i
103
231
  else
104
232
  log "got no pid"
105
233
  exit 1
@@ -114,26 +242,38 @@ ERROR
114
242
  end
115
243
  end
116
244
 
117
- def forward_signals(pid)
118
- @signal_queue.each { |sig| kill sig, pid }
245
+ def suspend_resume_on_tstp_cont(pid)
246
+ trap("TSTP") {
247
+ log "suspended"
248
+ Process.kill("STOP", pid.to_i)
249
+ Process.kill("STOP", Process.pid)
250
+ }
251
+ trap("CONT") {
252
+ log "resumed"
253
+ Process.kill("CONT", pid.to_i)
254
+ }
255
+ end
256
+
257
+ def forward_signals(application)
258
+ @signal_queue.each { |sig| kill sig, application }
119
259
 
120
260
  FORWARDED_SIGNALS.each do |sig|
121
- trap(sig) { forward_signal sig, pid }
261
+ trap(sig) { forward_signal sig, application }
122
262
  end
123
- rescue Errno::ESRCH
124
263
  end
125
264
 
126
- def forward_signal(sig, pid)
127
- kill(sig, pid)
128
- rescue Errno::ESRCH
129
- # If the application process is gone, then don't block the
130
- # signal on this process.
131
- trap(sig, 'DEFAULT')
132
- Process.kill(sig, Process.pid)
265
+ def forward_signal(sig, application)
266
+ if kill(sig, application) != 0
267
+ # If the application process is gone, then don't block the
268
+ # signal on this process.
269
+ trap(sig, 'DEFAULT')
270
+ Process.kill(sig, Process.pid)
271
+ end
133
272
  end
134
273
 
135
- def kill(sig, pid)
136
- Process.kill(sig, -Process.getpgid(pid))
274
+ def kill(sig, application)
275
+ application.puts(sig)
276
+ application.gets.to_i
137
277
  end
138
278
 
139
279
  def send_json(socket, data)
@@ -146,6 +286,12 @@ ERROR
146
286
  def default_rails_env
147
287
  ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
148
288
  end
289
+
290
+ def spawn_env
291
+ Spring.spawn_on_env.to_h do |key|
292
+ [key, ENV[key]]
293
+ end
294
+ end
149
295
  end
150
296
  end
151
297
  end
@@ -0,0 +1,18 @@
1
+ module Spring
2
+ module Client
3
+ class Server < Command
4
+ def self.description
5
+ "Explicitly start a Spring server in the foreground"
6
+ end
7
+
8
+ def call
9
+ require "spring/server"
10
+ Spring::Server.boot(foreground: foreground?)
11
+ end
12
+
13
+ def foreground?
14
+ !args.include?("--background")
15
+ end
16
+ end
17
+ end
18
+ end
@@ -17,11 +17,11 @@ module Spring
17
17
  end
18
18
 
19
19
  def print_process(pid)
20
- puts `ps -p #{pid} -o pid= -o args=`
20
+ puts `ps -p #{pid} -o pid= -o command=`
21
21
  end
22
22
 
23
23
  def application_pids
24
- candidates = `ps -A -o ppid= -o pid=`.lines
24
+ candidates = `ps -ax -o ppid= -o pid=`.lines
25
25
  candidates.select { |l| l =~ /^(\s+)?#{env.pid} / }
26
26
  .map { |l| l.split(" ").last }
27
27
  end
@@ -3,35 +3,20 @@ require "spring/version"
3
3
  module Spring
4
4
  module Client
5
5
  class Stop < Command
6
- TIMEOUT = 2 # seconds
7
-
8
6
  def self.description
9
- "Stop all spring processes for this project."
7
+ "Stop all Spring processes for this project."
10
8
  end
11
9
 
12
10
  def call
13
- if env.server_running?
14
- timeout = Time.now + TIMEOUT
15
- kill 'TERM'
16
- sleep 0.1 until !env.server_running? || Time.now >= timeout
17
-
18
- if env.server_running?
19
- $stderr.puts "Spring did not stop; killing forcibly."
20
- kill 'KILL'
21
- else
22
- puts "Spring stopped."
23
- end
24
- else
11
+ case env.stop
12
+ when :stopped
13
+ puts "Spring stopped."
14
+ when :killed
15
+ $stderr.puts "Spring did not stop; killing forcibly."
16
+ when :not_running
25
17
  puts "Spring is not running"
26
18
  end
27
19
  end
28
-
29
- def kill(sig)
30
- pid = env.pid
31
- Process.kill(sig, pid) if pid
32
- rescue Errno::ESRCH
33
- # already dead
34
- end
35
20
  end
36
21
  end
37
22
  end
data/lib/spring/client.rb CHANGED
@@ -9,17 +9,21 @@ require "spring/client/stop"
9
9
  require "spring/client/status"
10
10
  require "spring/client/rails"
11
11
  require "spring/client/version"
12
+ require "spring/client/server"
12
13
 
13
14
  module Spring
14
15
  module Client
15
16
  COMMANDS = {
16
17
  "help" => Client::Help,
18
+ "-h" => Client::Help,
19
+ "--help" => Client::Help,
17
20
  "binstub" => Client::Binstub,
18
21
  "stop" => Client::Stop,
19
22
  "status" => Client::Status,
20
23
  "rails" => Client::Rails,
21
24
  "-v" => Client::Version,
22
- "--version" => Client::Version
25
+ "--version" => Client::Version,
26
+ "server" => Client::Server,
23
27
  }
24
28
 
25
29
  def self.run(args)
@@ -36,3 +40,9 @@ module Spring
36
40
  end
37
41
  end
38
42
  end
43
+
44
+ # allow users to add hooks that do not run in the server
45
+ # or modify start/stop
46
+ if File.exist?("config/spring_client.rb")
47
+ require "./config/spring_client.rb"
48
+ end
@@ -41,10 +41,6 @@ module Spring
41
41
  end
42
42
  end
43
43
 
44
- def process_title
45
- [name, *ARGV].join(" ")
46
- end
47
-
48
44
  def gem_name
49
45
  if command.respond_to?(:gem_name)
50
46
  command.gem_name
@@ -83,9 +83,30 @@ module Spring
83
83
  end
84
84
  end
85
85
 
86
+ class RailsTest < Rails
87
+ def env(args)
88
+ environment = "test"
89
+
90
+ args.each.with_index do |arg, i|
91
+ if arg =~ /--environment=(\w+)/
92
+ environment = $1
93
+ elsif i > 0 && args[i - 1] == "-e"
94
+ environment = arg
95
+ end
96
+ end
97
+
98
+ environment
99
+ end
100
+
101
+ def command_name
102
+ "test"
103
+ end
104
+ end
105
+
86
106
  Spring.register_command "rails_console", RailsConsole.new
87
107
  Spring.register_command "rails_generate", RailsGenerate.new
88
108
  Spring.register_command "rails_destroy", RailsDestroy.new
89
109
  Spring.register_command "rails_runner", RailsRunner.new
110
+ Spring.register_command "rails_test", RailsTest.new
90
111
  end
91
112
  end
@@ -28,11 +28,18 @@ module Spring
28
28
  config = File.expand_path("~/.spring.rb")
29
29
  require config if File.exist?(config)
30
30
 
31
- # If the config/spring.rb contains requires for commands from other gems,
32
- # then we need to be under bundler.
33
- require "bundler/setup"
31
+ # We force the TTY so bundler doesn't show a backtrace in case gems are missing.
32
+ old_env = ENV["BUNDLER_FORCE_TTY"]
33
+ ENV["BUNDLER_FORCE_TTY"] = "true"
34
+ begin
35
+ # If the config/spring.rb contains requires for commands from other gems,
36
+ # then we need to be under bundler.
37
+ require "bundler/setup"
38
+ ensure
39
+ ENV["BUNDLER_FORCE_TTY"] = old_env
40
+ end
34
41
 
35
- # Auto-require any spring extensions which are in the Gemfile
42
+ # Auto-require any Spring extensions which are in the Gemfile
36
43
  Gem::Specification.map(&:name).grep(/^spring-/).each do |command|
37
44
  begin
38
45
  require command
@@ -1,11 +1,36 @@
1
1
  require "spring/errors"
2
2
 
3
3
  module Spring
4
+ @connect_timeout = 5
5
+ @boot_timeout = 20
6
+ @dangerously_allow_disabling_reloading = false
7
+
4
8
  class << self
5
- attr_accessor :application_root
9
+ attr_accessor :application_root, :connect_timeout, :boot_timeout
10
+ attr_writer :quiet
11
+
12
+ # Opt-in: skip the `:ensure_reloading_is_enabled` initializer so Spring
13
+ # boots with `config.enable_reloading = false`. Default `false`. See
14
+ # README "Running Spring with reloading disabled" for what this gives up.
15
+ attr_accessor :dangerously_allow_disabling_reloading
6
16
 
7
17
  def gemfile
8
- ENV['BUNDLE_GEMFILE'] || "Gemfile"
18
+ require "bundler"
19
+
20
+ if /\s1.9.[0-9]/ === Bundler.ruby_scope.gsub(/[\/\s]+/,'')
21
+ Pathname.new(ENV["BUNDLE_GEMFILE"] || "Gemfile").expand_path
22
+ else
23
+ Bundler.default_gemfile
24
+ end
25
+ end
26
+
27
+ def gemfile_lock
28
+ case gemfile.to_s
29
+ when /\bgems\.rb\z/
30
+ gemfile.sub_ext('.locked')
31
+ else
32
+ gemfile.sub_ext('.lock')
33
+ end
9
34
  end
10
35
 
11
36
  def after_fork_callbacks
@@ -16,6 +41,22 @@ module Spring
16
41
  after_fork_callbacks << block
17
42
  end
18
43
 
44
+ def after_environment_load_callbacks
45
+ @after_environment_load_callbacks ||= []
46
+ end
47
+
48
+ def after_environment_load(&block)
49
+ after_environment_load_callbacks << block
50
+ end
51
+
52
+ def spawn_on_env
53
+ @spawn_on_env ||= []
54
+ end
55
+
56
+ def reset_on_env
57
+ @reset_on_env ||= []
58
+ end
59
+
19
60
  def verify_environment
20
61
  application_root_path
21
62
  end
@@ -37,6 +78,10 @@ module Spring
37
78
  @project_root_path ||= find_project_root(Pathname.new(File.expand_path(Dir.pwd)))
38
79
  end
39
80
 
81
+ def quiet
82
+ @quiet || ENV.key?('SPRING_QUIET')
83
+ end
84
+
40
85
  private
41
86
 
42
87
  def find_project_root(current_dir)
@@ -49,4 +94,6 @@ module Spring
49
94
  end
50
95
  end
51
96
  end
97
+
98
+ self.quiet = false
52
99
  end
data/lib/spring/env.rb CHANGED
@@ -1,22 +1,19 @@
1
1
  require "pathname"
2
- require "fileutils"
3
- require "digest/md5"
4
- require "tmpdir"
5
2
 
6
3
  require "spring/version"
7
- require "spring/sid"
8
4
  require "spring/configuration"
9
5
 
10
6
  module Spring
11
7
  IGNORE_SIGNALS = %w(INT QUIT)
8
+ STOP_TIMEOUT = 2 # seconds
12
9
 
13
10
  class Env
14
11
  attr_reader :log_file
15
12
 
16
- def initialize(root = nil)
17
- @root = root
18
- @project_root = root
19
- @log_file = File.open(ENV["SPRING_LOG"] || File::NULL, "a")
13
+ def initialize(options = {})
14
+ @root = options[:root]
15
+ @project_root = options[:root]
16
+ @log_file = options[:log_file] || File.open(ENV["SPRING_LOG"] || File::NULL, "a")
20
17
  end
21
18
 
22
19
  def root
@@ -32,17 +29,23 @@ module Spring
32
29
  end
33
30
 
34
31
  def tmp_path
35
- path = Pathname.new(File.join(ENV['XDG_RUNTIME_DIR'] || Dir.tmpdir, "spring"))
32
+ require "tmpdir"
33
+ path = Pathname.new(
34
+ ENV["SPRING_TMP_PATH"] ||
35
+ File.join(ENV['XDG_RUNTIME_DIR'] || Dir.tmpdir, "spring-#{Process.uid}")
36
+ )
37
+ require "fileutils"
36
38
  FileUtils.mkdir_p(path) unless path.exist?
37
39
  path
38
40
  end
39
41
 
40
42
  def application_id
41
- Digest::MD5.hexdigest(RUBY_VERSION + project_root.to_s)
43
+ require "digest/md5"
44
+ ENV["SPRING_APPLICATION_ID"] || Digest::MD5.hexdigest(RUBY_VERSION + project_root.to_s)
42
45
  end
43
46
 
44
47
  def socket_path
45
- tmp_path.join(application_id)
48
+ Pathname.new(ENV["SPRING_SOCKET"] || tmp_path.join(application_id))
46
49
  end
47
50
 
48
51
  def socket_name
@@ -50,7 +53,7 @@ module Spring
50
53
  end
51
54
 
52
55
  def pidfile_path
53
- tmp_path.join("#{application_id}.pid")
56
+ Pathname.new(ENV["SPRING_PIDFILE"] || socket_path.dirname.join("#{socket_path.basename(".*")}.pid"))
54
57
  end
55
58
 
56
59
  def pid
@@ -80,5 +83,33 @@ module Spring
80
83
  log_file.puts "[#{Time.now}] [#{Process.pid}] #{message}"
81
84
  log_file.flush
82
85
  end
86
+
87
+ def stop
88
+ if server_running?
89
+ timeout = Time.now + STOP_TIMEOUT
90
+ kill 'TERM'
91
+ sleep 0.1 until !server_running? || Time.now >= timeout
92
+
93
+ if server_running?
94
+ kill 'KILL'
95
+ :killed
96
+ else
97
+ :stopped
98
+ end
99
+ else
100
+ :not_running
101
+ end
102
+ end
103
+
104
+ def kill(sig)
105
+ pid = self.pid
106
+ Process.kill(sig, pid) if pid
107
+ rescue Errno::ESRCH
108
+ # already dead
109
+ end
110
+
111
+ def server_command
112
+ ENV["SPRING_SERVER_COMMAND"] || "#{File.expand_path("../../../bin/spring", __FILE__)} server --background"
113
+ end
83
114
  end
84
115
  end
data/lib/spring/errors.rb CHANGED
@@ -24,7 +24,7 @@ module Spring
24
24
 
25
25
  def message
26
26
  "Spring was unable to find your config/application.rb file. " \
27
- "Your project root was detected at #{project_root}, so spring " \
27
+ "Your project root was detected at #{project_root}, so Spring " \
28
28
  "looked for #{project_root}/config/application.rb but it doesn't exist. You can " \
29
29
  "configure the root of your application by setting Spring.application_root in " \
30
30
  "config/spring.rb."
@@ -0,0 +1,14 @@
1
+ require 'thread'
2
+
3
+ module Spring
4
+ class << self
5
+ def failsafe_thread
6
+ Thread.new {
7
+ begin
8
+ yield
9
+ rescue
10
+ end
11
+ }
12
+ end
13
+ end
14
+ end