spring-jruby 1.4.3

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 (50) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +22 -0
  3. data/README.md +364 -0
  4. data/bin/spring +49 -0
  5. data/lib/spring-jruby/application.rb +283 -0
  6. data/lib/spring-jruby/application/boot.rb +19 -0
  7. data/lib/spring-jruby/binstub.rb +13 -0
  8. data/lib/spring-jruby/boot.rb +9 -0
  9. data/lib/spring-jruby/client.rb +46 -0
  10. data/lib/spring-jruby/client/binstub.rb +188 -0
  11. data/lib/spring-jruby/client/command.rb +18 -0
  12. data/lib/spring-jruby/client/help.rb +62 -0
  13. data/lib/spring-jruby/client/rails.rb +34 -0
  14. data/lib/spring-jruby/client/run.rb +167 -0
  15. data/lib/spring-jruby/client/status.rb +30 -0
  16. data/lib/spring-jruby/client/stop.rb +22 -0
  17. data/lib/spring-jruby/client/version.rb +11 -0
  18. data/lib/spring-jruby/command_wrapper.rb +82 -0
  19. data/lib/spring-jruby/commands.rb +51 -0
  20. data/lib/spring-jruby/commands/rails.rb +112 -0
  21. data/lib/spring-jruby/commands/rake.rb +30 -0
  22. data/lib/spring-jruby/configuration.rb +60 -0
  23. data/lib/spring-jruby/env.rb +109 -0
  24. data/lib/spring-jruby/errors.rb +36 -0
  25. data/lib/spring-jruby/impl/application.rb +7 -0
  26. data/lib/spring-jruby/impl/application_manager.rb +7 -0
  27. data/lib/spring-jruby/impl/fork/application.rb +69 -0
  28. data/lib/spring-jruby/impl/fork/application_manager.rb +137 -0
  29. data/lib/spring-jruby/impl/fork/run.rb +47 -0
  30. data/lib/spring-jruby/impl/pool/application.rb +47 -0
  31. data/lib/spring-jruby/impl/pool/application_manager.rb +226 -0
  32. data/lib/spring-jruby/impl/pool/run.rb +27 -0
  33. data/lib/spring-jruby/impl/run.rb +7 -0
  34. data/lib/spring-jruby/io_helpers.rb +92 -0
  35. data/lib/spring-jruby/json.rb +626 -0
  36. data/lib/spring-jruby/platform.rb +23 -0
  37. data/lib/spring-jruby/process_title_updater.rb +65 -0
  38. data/lib/spring-jruby/server.rb +130 -0
  39. data/lib/spring-jruby/sid.rb +42 -0
  40. data/lib/spring-jruby/test.rb +18 -0
  41. data/lib/spring-jruby/test/acceptance_test.rb +371 -0
  42. data/lib/spring-jruby/test/application.rb +217 -0
  43. data/lib/spring-jruby/test/application_generator.rb +134 -0
  44. data/lib/spring-jruby/test/rails_version.rb +40 -0
  45. data/lib/spring-jruby/test/watcher_test.rb +167 -0
  46. data/lib/spring-jruby/version.rb +3 -0
  47. data/lib/spring-jruby/watcher.rb +30 -0
  48. data/lib/spring-jruby/watcher/abstract.rb +86 -0
  49. data/lib/spring-jruby/watcher/polling.rb +61 -0
  50. metadata +137 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: b99ce59145a28c1dc4d73025347f9158c552d467
4
+ data.tar.gz: bda2cd1aa00702f9f1076d0acaf2fac62e634912
5
+ SHA512:
6
+ metadata.gz: 47265df7a11d37b58319107153223527763fc7b5c35f46d8f1d03f2c9e657250659fae309eb213bda5d29d96c0d82e528aa3a42ab1fec0307e97a226f757ec1f
7
+ data.tar.gz: 92965eeb3c5c9ef83ef4b5761428ddb18b20bebbc5c3170308eb06ab5efefde6f470f788524bef5aebdbaccefeb5da48fd68041770d37e1c0e5af8aa1c5625f8
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Jon Leighton
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,364 @@
1
+ # Spring
2
+
3
+ [![Build Status](https://travis-ci.org/rails/spring.svg?branch=master)](https://travis-ci.org/rails/spring)
4
+ [![Gem Version](https://badge.fury.io/rb/spring.svg)](http://badge.fury.io/rb/spring)
5
+
6
+ Spring is a Rails application preloader. It speeds up development by
7
+ keeping your application running in the background so you don't need to
8
+ boot it every time you run a test, rake task or migration.
9
+
10
+ ## Features
11
+
12
+ * Totally automatic; no need to explicitly start and stop the background process
13
+ * Reloads your application code on each run
14
+ * Restarts your application when configs / initializers / gem
15
+ dependencies are changed
16
+
17
+ ## Compatibility
18
+
19
+ * Ruby versions: MRI 1.9.3, MRI 2.0, MRI 2.1
20
+ * Rails versions: 4.0+ (in Rails 4.1 and up Spring is included by default)
21
+
22
+ Spring makes extensive use of `Process.fork`, so won't be able to
23
+ provide a speed up on platforms which don't support forking (Windows, JRuby).
24
+
25
+ ## Walkthrough
26
+
27
+ ### Setup
28
+
29
+ Add spring to your Gemfile:
30
+
31
+ ``` ruby
32
+ gem "spring", group: :development
33
+ ```
34
+
35
+ (Note: using `gem "spring", git: "..."` *won't* work and is not a
36
+ supported way of using spring.)
37
+
38
+ It's recommended to 'springify' the executables in your `bin/`
39
+ directory:
40
+
41
+ ```
42
+ $ bundle install
43
+ $ bundle exec spring binstub --all
44
+ ```
45
+
46
+ This generates a `bin/spring` executable, and inserts a small snippet of
47
+ code into relevant existing executables. The snippet looks like this:
48
+
49
+ ``` ruby
50
+ begin
51
+ load File.expand_path('../spring', __FILE__)
52
+ rescue LoadError
53
+ end
54
+ ```
55
+
56
+ On platforms where spring is installed and supported, this snippet
57
+ hooks spring into the execution of commands. In other cases, the snippet
58
+ will just be silently ignored and the lines after it will be executed as
59
+ normal.
60
+
61
+ If you don't want to prefix every command you type with `bin/`, you
62
+ can [use direnv](https://github.com/zimbatm/direnv#the-stdlib) to
63
+ automatically add `./bin` to your `PATH` when you `cd` into your application.
64
+ Simply create an `.envrc` file with the command `PATH_add bin` in your
65
+ Rails directory.
66
+
67
+ ### Usage
68
+
69
+ For this walkthrough I've generated a new Rails application, and run
70
+ `rails generate scaffold post name:string`.
71
+
72
+ Let's run a test:
73
+
74
+ ```
75
+ $ time bin/rake test test/controllers/posts_controller_test.rb
76
+ Run options:
77
+
78
+ # Running tests:
79
+
80
+ .......
81
+
82
+ Finished tests in 0.127245s, 55.0121 tests/s, 78.5887 assertions/s.
83
+
84
+ 7 tests, 10 assertions, 0 failures, 0 errors, 0 skips
85
+
86
+ real 0m2.165s
87
+ user 0m0.281s
88
+ sys 0m0.066s
89
+ ```
90
+
91
+ That wasn't particularly fast because it was the first run, so spring
92
+ had to boot the application. It's now running:
93
+
94
+ ```
95
+ $ bin/spring status
96
+ Spring is running:
97
+
98
+ 26150 spring server | spring-demo-app | started 3 secs ago
99
+ 26155 spring app | spring-demo-app | started 3 secs ago | test mode
100
+ ```
101
+
102
+ The next run is faster:
103
+
104
+ ```
105
+ $ time bin/rake test test/controllers/posts_controller_test.rb
106
+ Run options:
107
+
108
+ # Running tests:
109
+
110
+ .......
111
+
112
+ Finished tests in 0.176896s, 39.5714 tests/s, 56.5305 assertions/s.
113
+
114
+ 7 tests, 10 assertions, 0 failures, 0 errors, 0 skips
115
+
116
+ real 0m0.610s
117
+ user 0m0.276s
118
+ sys 0m0.059s
119
+ ```
120
+
121
+ If we edit any of the application files, or test files, the changes will
122
+ be picked up on the next run without the background process having to
123
+ restart. This works in exactly the same way as the code reloading
124
+ which allows you to refresh your browser and instantly see changes during
125
+ development.
126
+
127
+ But if we edit any of the files which were used to start the application
128
+ (configs, initializers, your gemfile), the application needs to be fully
129
+ restarted. This happens automatically.
130
+
131
+ Let's "edit" `config/application.rb`:
132
+
133
+ ```
134
+ $ touch config/application.rb
135
+ $ bin/spring status
136
+ Spring is running:
137
+
138
+ 26150 spring server | spring-demo-app | started 36 secs ago
139
+ 26556 spring app | spring-demo-app | started 1 sec ago | test mode
140
+ ```
141
+
142
+ The application detected that `config/application.rb` changed and
143
+ automatically restarted itself.
144
+
145
+ If we run a command that uses a different environment, then that
146
+ environment gets booted up:
147
+
148
+ ```
149
+ $ bin/rake routes
150
+ posts GET /posts(.:format) posts#index
151
+ POST /posts(.:format) posts#create
152
+ new_post GET /posts/new(.:format) posts#new
153
+ edit_post GET /posts/:id/edit(.:format) posts#edit
154
+ post GET /posts/:id(.:format) posts#show
155
+ PUT /posts/:id(.:format) posts#update
156
+ DELETE /posts/:id(.:format) posts#destroy
157
+
158
+ $ bin/spring status
159
+ Spring is running:
160
+
161
+ 26150 spring server | spring-demo-app | started 1 min ago
162
+ 26556 spring app | spring-demo-app | started 42 secs ago | test mode
163
+ 26707 spring app | spring-demo-app | started 2 secs ago | development mode
164
+ ```
165
+
166
+ There's no need to "shut down" spring. This will happen automatically
167
+ when you close your terminal. However if you do want to do a manual shut
168
+ down, use the `stop` command:
169
+
170
+ ```
171
+ $ bin/spring stop
172
+ Spring stopped.
173
+ ```
174
+
175
+ ### Removal
176
+
177
+ To remove spring:
178
+
179
+ * 'Unspring' your bin/ executables: `bin/spring binstub --remove --all`
180
+ * Remove spring from your Gemfile
181
+
182
+ ### Deployment
183
+
184
+ You must not install Spring on your production environment. To prevent it from
185
+ being installed, provide the `--without development test` argument to the
186
+ `bundle install` command which is used to install gems on your production
187
+ machines:
188
+
189
+ ```
190
+ $ bundle install --without development test
191
+ ```
192
+
193
+ ## Commands
194
+
195
+ ### `rake`
196
+
197
+ Runs a rake task. Rake tasks run in the `development` environment by
198
+ default. You can change this on the fly by using the `RAILS_ENV`
199
+ environment variable. The environment is also configurable with the
200
+ `Spring::Commands::Rake.environment_matchers` hash. This has sensible
201
+ defaults, but if you need to match a specific task to a specific
202
+ environment, you'd do it like this:
203
+
204
+ ``` ruby
205
+ Spring::Commands::Rake.environment_matchers["perf_test"] = "test"
206
+ Spring::Commands::Rake.environment_matchers[/^perf/] = "test"
207
+
208
+ # To change the environment when you run `rake` with no arguments
209
+ Spring::Commands::Rake.environment_matchers[:default] = "development"
210
+ ```
211
+
212
+ ### `rails console`, `rails generate`, `rails runner`
213
+
214
+ These execute the rails command you already know and love. If you run
215
+ a different sub command (e.g. `rails server`) then spring will automatically
216
+ pass it through to the underlying `rails` executable (without the
217
+ speed-up).
218
+
219
+ ### Additional commands
220
+
221
+ You can add these to your Gemfile for additional commands:
222
+
223
+ * [spring-commands-rspec](https://github.com/jonleighton/spring-commands-rspec)
224
+ * [spring-commands-cucumber](https://github.com/jonleighton/spring-commands-cucumber)
225
+ * [spring-commands-spinach](https://github.com/jvanbaarsen/spring-commands-spinach)
226
+ * [spring-commands-testunit](https://github.com/jonleighton/spring-commands-testunit) - useful for
227
+ running `Test::Unit` tests on Rails 3, since only Rails 4 allows you
228
+ to use `rake test path/to/test` to run a particular test/directory.
229
+ * [spring-commands-teaspoon](https://github.com/alejandrobabio/spring-commands-teaspoon.git)
230
+ * [spring-commands-m](https://github.com/gabrieljoelc/spring-commands-m.git)
231
+
232
+ ## Use without adding to bundle
233
+
234
+ If you don't want spring-related code checked into your source
235
+ repository, it's possible to use spring without adding to your Gemfile.
236
+ However, using spring binstubs without adding spring to the Gemfile is not
237
+ supported.
238
+
239
+ To use spring like this, do a `gem install spring` and then prefix
240
+ commands with `spring`. For example, rather than running `bin/rake -T`,
241
+ you'd run `spring rake -T`.
242
+
243
+ ## Temporarily disabling Spring
244
+
245
+ If you're using Spring binstubs, but temporarily don't want commands to
246
+ run through Spring, set the `DISABLE_SPRING` environment variable.
247
+
248
+ ## Class reloading
249
+
250
+ Spring uses Rails' class reloading mechanism
251
+ (`ActiveSupport::Dependencies`) to keep your code up to date between
252
+ test runs. This is the same mechanism which allows you to see changes
253
+ during development when you refresh the page. However, you may never
254
+ have used this mechanism with your `test` environment before, and this
255
+ can cause problems.
256
+
257
+ It's important to realise that code reloading means that the constants
258
+ in your application are *different objects* after files have changed:
259
+
260
+ ```
261
+ $ bin/rails runner 'puts User.object_id'
262
+ 70127987886040
263
+ $ touch app/models/user.rb
264
+ $ bin/rails runner 'puts User.object_id'
265
+ 70127976764620
266
+ ```
267
+
268
+ Suppose you have an initializer `config/initializers/save_user_class.rb`
269
+ like so:
270
+
271
+ ``` ruby
272
+ USER_CLASS = User
273
+ ```
274
+
275
+ This saves off the *first* version of the `User` class, which will not
276
+ be the same object as `User` after the code has been reloaded:
277
+
278
+ ```
279
+ $ bin/rails runner 'puts User == USER_CLASS'
280
+ true
281
+ $ touch app/models/user.rb
282
+ $ bin/rails runner 'puts User == USER_CLASS'
283
+ false
284
+ ```
285
+
286
+ So to avoid this problem, don't save off references to application
287
+ constants in your initialization code.
288
+
289
+ ## Configuration
290
+
291
+ Spring will read `~/.spring.rb` and `config/spring.rb` for custom
292
+ settings. Note that `~/.spring.rb` is loaded *before* bundler, but
293
+ `config/spring.rb` is loaded *after* bundler. So if you have any
294
+ `spring-commands-*` gems installed that you want to be available in all
295
+ projects without having to be added to the project's Gemfile, require
296
+ them in your `~/.spring.rb`.
297
+
298
+ `config/spring_client.rb` is also loaded before bundler and before a
299
+ server process is started, it can be used to add new top-level commands.
300
+
301
+ ### Application root
302
+
303
+ Spring must know how to find your Rails application. If you have a
304
+ normal app everything works out of the box. If you are working on a
305
+ project with a special setup (an engine for example), you must tell
306
+ Spring where your app is located:
307
+
308
+ ```ruby
309
+ Spring.application_root = './test/dummy'
310
+ ```
311
+
312
+ ### Running code before forking
313
+
314
+ There is no `Spring.before_fork` callback. To run something before the
315
+ fork, you can place it in `~/.spring.rb` or `config/spring.rb` or in any of the files
316
+ which get run when your application initializes, such as
317
+ `config/application.rb`, `config/environments/*.rb` or
318
+ `config/initializers/*.rb`.
319
+
320
+ ### Running code after forking
321
+
322
+ You might want to run code after Spring forked off the process but
323
+ before the actual command is run. You might want to use an
324
+ `after_fork` callback if you have to connect to an external service,
325
+ do some general cleanup or set up dynamic configuration.
326
+
327
+ ```ruby
328
+ Spring.after_fork do
329
+ # run arbitrary code
330
+ end
331
+ ```
332
+
333
+ If you want to register multiple callbacks you can simply call
334
+ `Spring.after_fork` multiple times with different blocks.
335
+
336
+ ### Watching files and directories
337
+
338
+ Spring will automatically detect file changes to any file loaded when the server
339
+ boots. Changes will cause the affected environments to be restarted.
340
+
341
+ If there are additional files or directories which should trigger an
342
+ application restart, you can specify them with `Spring.watch`:
343
+
344
+ ```ruby
345
+ Spring.watch "config/some_config_file.yml"
346
+ ```
347
+
348
+ By default Spring polls the filesystem for changes once every 0.2 seconds. This
349
+ method requires zero configuration, but if you find that it's using too
350
+ much CPU, then you can use event-based file system listening by
351
+ installing the
352
+ [spring-watcher-listen](https://github.com/jonleighton/spring-watcher-listen)
353
+ gem.
354
+
355
+ ## Troubleshooting
356
+
357
+ If you want to get more information about what spring is doing, you can
358
+ specify a log file with the `SPRING_LOG` environment variable:
359
+
360
+ ```
361
+ spring stop # if spring is already running
362
+ export SPRING_LOG=/tmp/spring.log
363
+ spring rake -T
364
+ ```
data/bin/spring ADDED
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ if defined?(Spring)
4
+ $stderr.puts "You've tried to invoke Spring when it's already loaded (i.e. the Spring " \
5
+ "constant is defined)."
6
+ $stderr.puts
7
+ $stderr.puts "This is probably because you generated binstubs with " \
8
+ "Spring 1.0, and you now have a Spring version > 1.0 on your system. To solve " \
9
+ "this, upgrade your bundle to the latest Spring version and then run " \
10
+ "`bundle exec spring binstub --all` to regenerate your binstubs. This is a one-time " \
11
+ "step necessary to upgrade from 1.0 to 1.1."
12
+ $stderr.puts
13
+ $stderr.puts "Here's the backtrace:"
14
+ $stderr.puts
15
+ $stderr.puts caller
16
+ exit 1
17
+ end
18
+
19
+ if defined?(Gem)
20
+ if Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.1.0")
21
+ warn "Warning: You're using Rubygems #{Gem::VERSION} with Spring. " \
22
+ "Upgrade to at least Rubygems 2.1.0 and run `gem pristine --all` for better " \
23
+ "startup performance."
24
+ else
25
+ stubs = Gem::Specification.stubs.grep(Gem::StubSpecification)
26
+
27
+ # stubbed? method added in https://github.com/rubygems/rubygems/pull/694
28
+ if Gem::Specification.stubs.first.respond_to?(:stubbed?)
29
+ unstubbed = stubs.reject(&:stubbed?)
30
+ else
31
+ unstubbed = stubs.reject { |s| s.send(:data).is_a?(Gem::StubSpecification::StubLine) }
32
+ end
33
+
34
+ # `gem pristine --all` ignores default gems. it doesn't really matter,
35
+ # as there are probably not many of them on the system.
36
+ unstubbed.reject!(&:default_gem?)
37
+
38
+ if unstubbed.any?
39
+ warn "Warning: Running `gem pristine --all` to regenerate your installed gemspecs " \
40
+ "(and deleting then reinstalling your bundle if you use bundle --path) " \
41
+ "will improve the startup performance of Spring."
42
+ end
43
+ end
44
+ end
45
+
46
+ lib = File.expand_path("../../lib", __FILE__)
47
+ $LOAD_PATH.unshift lib unless $LOAD_PATH.include?(lib) # enable local development
48
+ require 'spring-jruby/client'
49
+ Spring::Client.run(ARGV)
@@ -0,0 +1,283 @@
1
+ require "spring-jruby/boot"
2
+ require "set"
3
+ require "pty"
4
+ require "spring-jruby/impl/application"
5
+
6
+ module Spring
7
+ class Application
8
+ include ApplicationImpl
9
+ attr_reader :manager, :watcher, :spring_env, :original_env
10
+
11
+ def initialize(manager, original_env)
12
+ @manager = manager
13
+ @original_env = original_env
14
+ @spring_env = Env.new
15
+ @mutex = Mutex.new
16
+ @waiting = Set.new
17
+ @preloaded = false
18
+ @state = :initialized
19
+ @interrupt = IO.pipe
20
+ end
21
+
22
+ def state(val)
23
+ return if exiting?
24
+ log "#{@state} -> #{val}"
25
+ @state = val
26
+ end
27
+
28
+ def state!(val)
29
+ state val
30
+ @interrupt.last.write "."
31
+ end
32
+
33
+ def app_env
34
+ ENV['RAILS_ENV']
35
+ end
36
+
37
+ def app_name
38
+ spring_env.app_name
39
+ end
40
+
41
+ def log(message)
42
+ spring_env.log "[application:#{app_env}] #{message}"
43
+ end
44
+
45
+ def preloaded?
46
+ @preloaded
47
+ end
48
+
49
+ def preload_failed?
50
+ @preloaded == :failure
51
+ end
52
+
53
+ def exiting?
54
+ @state == :exiting
55
+ end
56
+
57
+ def terminating?
58
+ @state == :terminating
59
+ end
60
+
61
+ def watcher_stale?
62
+ @state == :watcher_stale
63
+ end
64
+
65
+ def initialized?
66
+ @state == :initialized
67
+ end
68
+
69
+ def start_watcher
70
+ @watcher = Spring.watcher
71
+ @watcher.on_stale { state! :watcher_stale }
72
+ @watcher.start
73
+ end
74
+
75
+ def preload
76
+ log "preloading app"
77
+
78
+ begin
79
+ require "spring-jruby/commands"
80
+ ensure
81
+ start_watcher
82
+ end
83
+
84
+ require Spring.application_root_path.join("config", "application")
85
+
86
+ # config/environments/test.rb will have config.cache_classes = true. However
87
+ # we want it to be false so that we can reload files. This is a hack to
88
+ # override the effect of config.cache_classes = true. We can then actually
89
+ # set config.cache_classes = false after loading the environment.
90
+ Rails::Application.initializer :initialize_dependency_mechanism, group: :all do
91
+ ActiveSupport::Dependencies.mechanism = :load
92
+ end
93
+
94
+ require Spring.application_root_path.join("config", "environment")
95
+
96
+ @original_cache_classes = Rails.application.config.cache_classes
97
+ Rails.application.config.cache_classes = false
98
+
99
+ disconnect_database
100
+
101
+ @preloaded = :success
102
+ rescue Exception => e
103
+ @preloaded = :failure
104
+ watcher.add e.backtrace.map { |line| line.match(/^(.*)\:\d+\:in /)[1] }
105
+ raise e unless initialized?
106
+ ensure
107
+ watcher.add loaded_application_features
108
+ watcher.add Spring.gemfile, "#{Spring.gemfile}.lock"
109
+
110
+ if defined?(Rails) && Rails.application
111
+ watcher.add Rails.application.paths["config/initializers"]
112
+ watcher.add Rails.application.paths["config/database"]
113
+ if secrets_path = Rails.application.paths["config/secrets"]
114
+ watcher.add secrets_path
115
+ end
116
+ end
117
+ end
118
+
119
+ def run
120
+ state :running
121
+ notify_manager_ready
122
+
123
+ loop do
124
+ IO.select [manager, @interrupt.first]
125
+
126
+ if terminating? || watcher_stale? || preload_failed?
127
+ exit
128
+ else
129
+ serve IOWrapper.recv_io(manager, UNIXSocket).to_io
130
+ end
131
+ end
132
+ end
133
+
134
+ def serve(client)
135
+ child_started = [false]
136
+ log "got client"
137
+ manager.puts
138
+
139
+ stdout, stderr, stdin = streams = receive_streams(client)
140
+ reopen_streams(streams)
141
+
142
+ preload unless preloaded?
143
+
144
+ args, env = JSON.load(client.read(client.gets.to_i)).values_at("args", "env")
145
+ command = Spring.command(args.shift)
146
+
147
+ connect_database
148
+ setup command
149
+
150
+ if Rails.application.reloaders.any?(&:updated?)
151
+ ActionDispatch::Reloader.cleanup!
152
+ ActionDispatch::Reloader.prepare!
153
+ end
154
+
155
+ fork_child(client, streams, child_started) {
156
+ IGNORE_SIGNALS.each { |sig| trap(sig, "DEFAULT") }
157
+ trap("TERM", "DEFAULT")
158
+
159
+ ARGV.replace(args)
160
+ $0 = command.exec_name
161
+
162
+ # Delete all env vars which are unchanged from before spring started
163
+ original_env.each { |k, v| ENV.delete k if ENV[k] == v }
164
+
165
+ # Load in the current env vars, except those which *were* changed when spring started
166
+ env.each { |k, v| ENV[k] ||= v }
167
+
168
+ # requiring is faster, so if config.cache_classes was true in
169
+ # the environment's config file, then we can respect that from
170
+ # here on as we no longer need constant reloading.
171
+ if @original_cache_classes
172
+ ActiveSupport::Dependencies.mechanism = :require
173
+ Rails.application.config.cache_classes = true
174
+ end
175
+
176
+ connect_database
177
+ srand
178
+
179
+ invoke_after_fork_callbacks
180
+ shush_backtraces
181
+
182
+ before_command
183
+ command.call
184
+ }
185
+ rescue Exception => e
186
+ Kernel.exit if exiting? && e.is_a?(SystemExit)
187
+
188
+ log "exception: #{e}"
189
+ manager.puts unless child_started[0]
190
+
191
+ if streams && !e.is_a?(SystemExit)
192
+ print_exception(stderr || STDERR, e)
193
+ streams.each(&:close)
194
+ end
195
+
196
+ client.puts(1) if child_started[0]
197
+ client.close
198
+ end
199
+
200
+ def terminate
201
+ if exiting?
202
+ # Ensure that we do not ignore subsequent termination attempts
203
+ log "forced exit"
204
+ @waiting.each { |pid| Process.kill("TERM", pid) }
205
+ Kernel.exit
206
+ else
207
+ state! :terminating
208
+ end
209
+ end
210
+
211
+ def exit
212
+ state :exiting
213
+ manager.shutdown(:RDWR)
214
+ exit_if_finished
215
+ sleep
216
+ end
217
+
218
+ def exit_if_finished
219
+ @mutex.synchronize {
220
+ Kernel.exit if exiting? && @waiting.empty?
221
+ }
222
+ end
223
+
224
+ # The command might need to require some files in the
225
+ # main process so that they are cached. For example a test command wants to
226
+ # load the helper file once and have it cached.
227
+ def setup(command)
228
+ if command.setup
229
+ watcher.add loaded_application_features # loaded features may have changed
230
+ end
231
+ end
232
+
233
+ def invoke_after_fork_callbacks
234
+ Spring.after_fork_callbacks.each do |callback|
235
+ callback.call
236
+ end
237
+ end
238
+
239
+ def loaded_application_features
240
+ root = Spring.application_root_path.to_s
241
+ $LOADED_FEATURES.select { |f| f.start_with?(root) }
242
+ end
243
+
244
+ def disconnect_database
245
+ ActiveRecord::Base.remove_connection if active_record_configured?
246
+ end
247
+
248
+ def connect_database
249
+ ActiveRecord::Base.establish_connection if active_record_configured?
250
+ end
251
+
252
+ # This feels very naughty
253
+ def shush_backtraces
254
+ Kernel.module_eval do
255
+ old_raise = Kernel.method(:raise)
256
+ remove_method :raise
257
+ define_method :raise do |*args|
258
+ begin
259
+ old_raise.call(*args)
260
+ ensure
261
+ if $!
262
+ lib = File.expand_path("..", __FILE__)
263
+ $!.backtrace.reject! { |line| line.start_with?(lib) }
264
+ end
265
+ end
266
+ end
267
+ private :raise
268
+ end
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
276
+
277
+ private
278
+
279
+ def active_record_configured?
280
+ defined?(ActiveRecord::Base) && ActiveRecord::Base.configurations.any?
281
+ end
282
+ end
283
+ end