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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ab103d29ee132ac8637c7ddca8d5c41fc567c31e
4
- data.tar.gz: 88e600c6e1d59b4150625769f20f6d543958087c
3
+ metadata.gz: e8e020e7dc1eb3f27faa79634ad8c9ff4e9c1ddb
4
+ data.tar.gz: 5bd55b0c1e659701f4cd61afaba2b854070bc482
5
5
  SHA512:
6
- metadata.gz: 7b4d762562b8d4c1832bcec43819b6bcc6d9168473095fac089a743c0c421d4450f77e7c0a3cf31902256ed42a3d0fa57e7767cd4a0cf9d0bd55dc24c758f460
7
- data.tar.gz: 9c929b2636980dc30486f475caeacb1f3de296420c22e9f0a52c67ce732a8953e28f1b090258a18456d210ce217610e8e4df8ffeb7a1a8ebd0e2b546554235ec
6
+ metadata.gz: ebecedcb2e4a616615dc33a752e54a380f685e9d43227f67a5c3a0f29ae8c500df20ab75705778e13f2efa5c52d51f4ed682472eda7b12006e3720a762232dd9
7
+ data.tar.gz: 349a6b90ba96409a80e90a14e5bc39d7057970d3a6a81eb8fe7e6c6b7cd48aebc7465d687ffd08b704a3977cc0c3405a72cc93521b022fd54ef7a13a154ba3e8
data/.travis.yml CHANGED
@@ -2,6 +2,8 @@ language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
4
  - 2.0.0
5
+ - 2.1.0
5
6
  env:
6
7
  - RAILS_VERSION="~> 3.2.0"
7
8
  - RAILS_VERSION="~> 4.0.0"
9
+ - RAILS_VERSION="~> 4.1.0.beta1"
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
- [![Build Status](https://travis-ci.org/jonleighton/spring.png?branch=master)](https://travis-ci.org/jonleighton/spring)
3
+ [![Build Status](https://travis-ci.org/rails/spring.png?branch=master)](https://travis-ci.org/rails/spring)
4
+ [![Gem Version](https://badge.fury.io/rb/spring.png)](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.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#fork`, so won't be able to
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
- Either `gem install spring` or add it to your Gemfile:
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
- group :development do
30
- gem "spring"
49
+ begin
50
+ load File.expand_path("../spring", __FILE__)
51
+ rescue LoadError
31
52
  end
32
53
  ```
33
54
 
34
- Spring is designed to be used *without* bundle exec, so use `spring
35
- [command]` rather than `bundle exec spring [command]`.
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 spring rake test test/functional/posts_controller_test.rb
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 spring rake test test/functional/posts_controller_test.rb
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
- ## Commands
158
-
159
- The following commands are shipped by default.
172
+ ### Removal
160
173
 
161
- Custom commands can be specified in the Spring config file. See
162
- [`lib/spring/commands/`](https://github.com/jonleighton/spring/blob/master/lib/spring/commands/)
163
- for examples.
174
+ To remove spring:
164
175
 
165
- You can add the following gems to your Gemfile for additional commands:
176
+ * 'Unspring' your bin/ executables: `bin/spring binstub --remove --all`
177
+ * Remove spring from your Gemfile
166
178
 
167
- * [spring-commands-rspec](https://github.com/jonleighton/spring-commands-rspec)
168
- * [spring-commands-cucumber](https://github.com/jonleighton/spring-commands-cucumber)
169
- * [spring-commands-testunit](https://github.com/jonleighton/spring-commands-testunit) - useful for
170
- running `Test::Unit` tests on Rails 3, since only Rails 4 allows you
171
- to use `rake test path/to/test` to run a particular test/directory.
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
@@ -1,41 +1,84 @@
1
+ require "spring/boot"
1
2
  require "set"
2
- require "spring/watcher"
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 initialize(manager, watcher = Spring.watcher)
10
- @manager = manager
11
- @watcher = watcher
12
- @setup = Set.new
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
- # Workaround for GC bug in Ruby 2 which causes segfaults if watcher.to_io
20
- # instances get dereffed.
21
- @fds = [manager, watcher.to_io]
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:#{ENV['RAILS_ENV']}] #{message}"
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
- @preloaded = true
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
- log "running"
63
- watcher.start
117
+ state :running
118
+ manager.puts
64
119
 
65
120
  loop do
66
- IO.select(@fds)
67
-
68
- if watcher.stale?
69
- log "watcher stale; exiting"
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
- Spring.original_env.each { |k, v| ENV.delete k if ENV[k] == v }
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
- if command.respond_to?(:call)
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
- [STDOUT, STDERR].each { |stream| stream.reopen(spring_env.log_file) }
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
- try_exit
195
+ exit_if_finished
156
196
  }
157
197
 
158
- rescue => e
198
+ rescue Exception => e
159
199
  log "exception: #{e}"
160
- streams.each(&:close) if streams
161
- client.puts(1)
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
- raise
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
- return if @setup.include?(command.class)
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
- $LOADED_FEATURES.select { |f| f.start_with?(File.realpath(Rails.root)) }
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