spring 1.0.0 → 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 +2 -0
- data/CHANGELOG.md +27 -0
- data/README.md +114 -45
- data/bin/spring +1 -0
- data/lib/spring/application/boot.rb +16 -0
- data/lib/spring/application.rb +122 -60
- data/lib/spring/application_manager.rb +51 -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 +1 -3
- data/lib/spring/client/version.rb +11 -0
- data/lib/spring/client.rb +8 -5
- data/lib/spring/command_wrapper.rb +86 -0
- data/lib/spring/commands/rails.rb +1 -2
- data/lib/spring/commands/rake.rb +0 -4
- data/lib/spring/commands.rb +3 -2
- data/lib/spring/configuration.rb +11 -2
- data/lib/spring/env.rb +10 -9
- data/lib/spring/server.rb +8 -36
- 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 +178 -355
- data/test/acceptance/helper.rb +329 -0
- data/test/unit/client/version_test.rb +14 -0
- data/test/unit/watcher_test.rb +12 -8
- metadata +13 -3
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
require "spring/env"
|
|
2
|
+
|
|
3
|
+
module Spring
|
|
4
|
+
module Test
|
|
5
|
+
class RailsVersion
|
|
6
|
+
attr_reader :version
|
|
7
|
+
|
|
8
|
+
def initialize(string)
|
|
9
|
+
@version = Gem::Version.new(string)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def rails_3?
|
|
13
|
+
version < Gem::Version.new("4.0.0")
|
|
14
|
+
end
|
|
15
|
+
alias needs_testunit? rails_3?
|
|
16
|
+
|
|
17
|
+
def test_command
|
|
18
|
+
needs_testunit? ? 'bin/testunit' : 'bin/rake test'
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def controller_tests_dir
|
|
22
|
+
rails_3? ? 'functional' : 'controllers'
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def bundles_spring?
|
|
26
|
+
version >= Gem::Version.new("4.1.0.beta1")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def major
|
|
30
|
+
version.segments[0]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def minor
|
|
34
|
+
version.segments[1]
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class Application
|
|
39
|
+
DEFAULT_TIMEOUT = ENV['CI'] ? 30 : 10
|
|
40
|
+
|
|
41
|
+
attr_reader :root, :spring_env
|
|
42
|
+
|
|
43
|
+
def initialize(root)
|
|
44
|
+
@root = Pathname.new(root)
|
|
45
|
+
@spring_env = Spring::Env.new(root)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def exists?
|
|
49
|
+
root.exist?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def stdout
|
|
53
|
+
@stdout ||= IO.pipe
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def stderr
|
|
57
|
+
@stderr ||= IO.pipe
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def log_file
|
|
61
|
+
@log_file ||= path("tmp/spring.log").open("w+")
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def env
|
|
65
|
+
@env ||= {
|
|
66
|
+
"GEM_HOME" => gem_home.to_s,
|
|
67
|
+
"GEM_PATH" => "",
|
|
68
|
+
"HOME" => user_home.to_s,
|
|
69
|
+
"RAILS_ENV" => nil,
|
|
70
|
+
"RACK_ENV" => nil,
|
|
71
|
+
"SPRING_LOG" => log_file.path
|
|
72
|
+
}
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def path(addition)
|
|
76
|
+
root.join addition
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def gemfile
|
|
80
|
+
path "Gemfile"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def gem_home
|
|
84
|
+
path "vendor/gems/#{RUBY_VERSION}"
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def user_home
|
|
88
|
+
path "user_home"
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def spring
|
|
92
|
+
gem_home.join "bin/spring"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def rails_version
|
|
96
|
+
@rails_version ||= RailsVersion.new(gemfile.read.match(/gem 'rails', '(.*)'/)[1])
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def spring_test_command
|
|
100
|
+
"#{rails_version.test_command} #{test}"
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def stop_spring
|
|
104
|
+
run "#{spring} stop"
|
|
105
|
+
rescue Errno::ENOENT
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def test
|
|
109
|
+
path "test/#{rails_version.controller_tests_dir}/posts_controller_test.rb"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def controller
|
|
113
|
+
path "app/controllers/posts_controller.rb"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def application_config
|
|
117
|
+
path "config/application.rb"
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def spring_config
|
|
121
|
+
path "config/spring.rb"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def run(command, opts = {})
|
|
125
|
+
start_time = Time.now
|
|
126
|
+
|
|
127
|
+
Bundler.with_clean_env do
|
|
128
|
+
Process.spawn(
|
|
129
|
+
env,
|
|
130
|
+
command.to_s,
|
|
131
|
+
out: stdout.last,
|
|
132
|
+
err: stderr.last,
|
|
133
|
+
in: :close,
|
|
134
|
+
chdir: root.to_s,
|
|
135
|
+
)
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
_, status = Timeout.timeout(opts.fetch(:timeout, DEFAULT_TIMEOUT)) { Process.wait2 }
|
|
139
|
+
|
|
140
|
+
if pid = spring_env.pid
|
|
141
|
+
@server_pid = pid
|
|
142
|
+
lines = `ps -A -o ppid= -o pid= | egrep '^\\s*#{@server_pid}'`.lines
|
|
143
|
+
@application_pids = lines.map { |l| l.split.last.to_i }
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
output = read_streams
|
|
147
|
+
puts dump_streams(command, output) if ENV["SPRING_DEBUG"]
|
|
148
|
+
|
|
149
|
+
@times << (Time.now - start_time) if @times
|
|
150
|
+
|
|
151
|
+
output.merge(status: status, command: command)
|
|
152
|
+
rescue Timeout::Error => e
|
|
153
|
+
raise e, "Output:\n\n#{dump_streams(command, read_streams)}"
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def with_timing
|
|
157
|
+
@times = []
|
|
158
|
+
yield
|
|
159
|
+
ensure
|
|
160
|
+
@times = nil
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def last_time
|
|
164
|
+
@times.last
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def first_time
|
|
168
|
+
@times.first
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def timing_ratio
|
|
172
|
+
last_time / first_time
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def read_streams
|
|
176
|
+
{
|
|
177
|
+
stdout: read_stream(stdout.first),
|
|
178
|
+
stderr: read_stream(stderr.first),
|
|
179
|
+
log: read_stream(log_file)
|
|
180
|
+
}
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def read_stream(stream)
|
|
184
|
+
output = ""
|
|
185
|
+
while IO.select([stream], [], [], 0.5) && !stream.eof?
|
|
186
|
+
output << stream.readpartial(10240)
|
|
187
|
+
end
|
|
188
|
+
output
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def dump_streams(command, streams)
|
|
192
|
+
output = "$ #{command}\n"
|
|
193
|
+
|
|
194
|
+
streams.each do |name, stream|
|
|
195
|
+
unless stream.chomp.empty?
|
|
196
|
+
output << "--- #{name} ---\n"
|
|
197
|
+
output << "#{stream.chomp}\n"
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
output << "\n"
|
|
202
|
+
output
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def debug(artifacts)
|
|
206
|
+
artifacts = artifacts.dup
|
|
207
|
+
artifacts.delete :status
|
|
208
|
+
dump_streams(artifacts.delete(:command), artifacts)
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def await_reload
|
|
212
|
+
raise "no pid" if @application_pids.nil? || @application_pids.empty?
|
|
213
|
+
|
|
214
|
+
Timeout.timeout(DEFAULT_TIMEOUT) do
|
|
215
|
+
sleep 0.1 while @application_pids.any? { |p| process_alive?(p) }
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
|
|
219
|
+
def run!(*args)
|
|
220
|
+
artifacts = run(*args)
|
|
221
|
+
unless artifacts[:status].success?
|
|
222
|
+
raise "command failed\n\n#{debug(artifacts)}"
|
|
223
|
+
end
|
|
224
|
+
artifacts
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
def bundle
|
|
228
|
+
run! "(gem list bundler | grep bundler) || gem install bundler", timeout: nil
|
|
229
|
+
run! "bundle update", timeout: nil
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
private
|
|
233
|
+
|
|
234
|
+
def process_alive?(pid)
|
|
235
|
+
Process.kill 0, pid
|
|
236
|
+
true
|
|
237
|
+
rescue Errno::ESRCH
|
|
238
|
+
false
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
class ApplicationGenerator
|
|
243
|
+
attr_reader :version_constraint, :version, :application
|
|
244
|
+
|
|
245
|
+
def initialize(version_constraint)
|
|
246
|
+
@version_constraint = version_constraint
|
|
247
|
+
@version = RailsVersion.new(version_constraint.split(' ').last)
|
|
248
|
+
@application = Application.new(root)
|
|
249
|
+
@bundled = false
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
def root
|
|
253
|
+
"#{TEST_ROOT}/apps/rails-#{version.major}-#{version.minor}-spring-#{Spring::VERSION}"
|
|
254
|
+
end
|
|
255
|
+
|
|
256
|
+
def system(command)
|
|
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"]
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# Sporadic SSL errors keep causing test failures so there are anti-SSL workarounds here
|
|
268
|
+
def generate
|
|
269
|
+
Bundler.with_clean_env do
|
|
270
|
+
system("(gem list rails --installed --version '#{version_constraint}' || " \
|
|
271
|
+
"gem install rails --clear-sources --source http://rubygems.org --version '#{version_constraint}')")
|
|
272
|
+
|
|
273
|
+
skips = %w(--skip-bundle --skip-javascript --skip-sprockets)
|
|
274
|
+
skips << "--skip-spring" if version.bundles_spring?
|
|
275
|
+
|
|
276
|
+
system("rails '_#{version_constraint}_' new #{application.root} #{skips.join(' ')}")
|
|
277
|
+
|
|
278
|
+
FileUtils.mkdir_p(application.gem_home)
|
|
279
|
+
FileUtils.mkdir_p(application.user_home)
|
|
280
|
+
FileUtils.rm_rf(application.path("test/performance"))
|
|
281
|
+
|
|
282
|
+
File.write(application.gemfile, "#{application.gemfile.read}gem 'spring', '#{Spring::VERSION}'\n")
|
|
283
|
+
|
|
284
|
+
if version.needs_testunit?
|
|
285
|
+
File.write(application.gemfile, "#{application.gemfile.read}gem 'spring-commands-testunit'\n")
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
File.write(application.gemfile, application.gemfile.read.sub("https://rubygems.org", "http://rubygems.org"))
|
|
289
|
+
|
|
290
|
+
if application.path("bin").exist?
|
|
291
|
+
FileUtils.cp_r(application.path("bin"), application.path("bin_original"))
|
|
292
|
+
end
|
|
293
|
+
end
|
|
294
|
+
|
|
295
|
+
install_spring
|
|
296
|
+
|
|
297
|
+
application.run! "bundle exec rails g scaffold post title:string"
|
|
298
|
+
application.run! "bundle exec rake db:migrate db:test:clone"
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
def generate_if_missing
|
|
302
|
+
generate unless application.exists?
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
def install_spring
|
|
306
|
+
return if @installed
|
|
307
|
+
|
|
308
|
+
system("gem build spring.gemspec 2>&1")
|
|
309
|
+
application.run! "gem install ../../../spring-#{Spring::VERSION}.gem", timeout: nil
|
|
310
|
+
|
|
311
|
+
application.bundle
|
|
312
|
+
|
|
313
|
+
FileUtils.rm_rf application.path("bin")
|
|
314
|
+
|
|
315
|
+
if application.path("bin_original").exist?
|
|
316
|
+
FileUtils.cp_r application.path("bin_original"), application.path("bin")
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
application.run! "#{application.spring} binstub --all"
|
|
320
|
+
@installed = true
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def copy_to(path)
|
|
324
|
+
system("rm -rf #{path}")
|
|
325
|
+
system("cp -r #{application.root} #{path}")
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'helper'
|
|
2
|
+
require 'spring/client'
|
|
3
|
+
|
|
4
|
+
class VersionTest < ActiveSupport::TestCase
|
|
5
|
+
test "outputs current version number" do
|
|
6
|
+
version = Spring::Client::Version.new 'version'
|
|
7
|
+
|
|
8
|
+
out, err = capture_io do
|
|
9
|
+
version.call
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
assert_equal "Spring version #{Spring::VERSION}", out.chomp
|
|
13
|
+
end
|
|
14
|
+
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.
|
|
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:
|
|
11
|
+
date: 2014-01-17 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activesupport
|
|
@@ -55,7 +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
|
|
60
|
+
- lib/spring/binstub.rb
|
|
61
|
+
- lib/spring/boot.rb
|
|
59
62
|
- lib/spring/client.rb
|
|
60
63
|
- lib/spring/client/binstub.rb
|
|
61
64
|
- lib/spring/client/command.rb
|
|
@@ -64,6 +67,8 @@ files:
|
|
|
64
67
|
- lib/spring/client/run.rb
|
|
65
68
|
- lib/spring/client/status.rb
|
|
66
69
|
- lib/spring/client/stop.rb
|
|
70
|
+
- lib/spring/client/version.rb
|
|
71
|
+
- lib/spring/command_wrapper.rb
|
|
67
72
|
- lib/spring/commands.rb
|
|
68
73
|
- lib/spring/commands/rails.rb
|
|
69
74
|
- lib/spring/commands/rake.rb
|
|
@@ -81,13 +86,15 @@ files:
|
|
|
81
86
|
- lib/spring/watcher/polling.rb
|
|
82
87
|
- spring.gemspec
|
|
83
88
|
- test/acceptance/app_test.rb
|
|
89
|
+
- test/acceptance/helper.rb
|
|
84
90
|
- test/apps/.gitignore
|
|
85
91
|
- test/helper.rb
|
|
86
92
|
- test/unit/client/help_test.rb
|
|
93
|
+
- test/unit/client/version_test.rb
|
|
87
94
|
- test/unit/commands_test.rb
|
|
88
95
|
- test/unit/process_title_updater_test.rb
|
|
89
96
|
- test/unit/watcher_test.rb
|
|
90
|
-
homepage: http://github.com/
|
|
97
|
+
homepage: http://github.com/rails/spring
|
|
91
98
|
licenses:
|
|
92
99
|
- MIT
|
|
93
100
|
metadata: {}
|
|
@@ -113,9 +120,12 @@ specification_version: 4
|
|
|
113
120
|
summary: Rails application preloader
|
|
114
121
|
test_files:
|
|
115
122
|
- test/acceptance/app_test.rb
|
|
123
|
+
- test/acceptance/helper.rb
|
|
116
124
|
- test/apps/.gitignore
|
|
117
125
|
- test/helper.rb
|
|
118
126
|
- test/unit/client/help_test.rb
|
|
127
|
+
- test/unit/client/version_test.rb
|
|
119
128
|
- test/unit/commands_test.rb
|
|
120
129
|
- test/unit/process_title_updater_test.rb
|
|
121
130
|
- test/unit/watcher_test.rb
|
|
131
|
+
has_rdoc:
|