spin 0.6.0 → 0.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.
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  Spin
2
2
  ====
3
3
 
4
+ [![Build Status](https://travis-ci.org/jstorimer/spin.png)](https://travis-ci.org/jstorimer/spin)
5
+
4
6
  Spin speeds up your Rails testing workflow.
5
7
 
6
8
  By preloading your Rails environment in one process and then using fork(2) for each test run you don't load the same code over and over and over...
@@ -133,4 +135,4 @@ If Spin isn't scratching your itch then one of these projects might:
133
135
  * [Spork](https://github.com/sporkrb/spork)
134
136
  * [TestR](https://github.com/sunaku/testr)
135
137
  * [Zeus](https://github.com/burke/zeus)
136
-
138
+ * [Spring](https://github.com/jonleighton/spring)
@@ -1,5 +1,7 @@
1
1
  require 'spin/version'
2
2
  require 'spin/hooks'
3
+ require 'spin/test_process'
4
+ require 'spin/logger'
3
5
  require 'socket'
4
6
  require 'tempfile' # Dir.tmpdir
5
7
  # This lets us hash the parameters we want to include in the filename
@@ -14,6 +16,9 @@ module Spin
14
16
  extend Spin::Hooks
15
17
 
16
18
  PUSH_FILE_SEPARATOR = '|'
19
+ ARGS_SEPARATOR = ' -- '
20
+ # The time window used to detect 2 successive SIGINT (Ctrl+C) signals.
21
+ SIGINT_TIME_WINDOW = 5
17
22
 
18
23
  class << self
19
24
  def serve(options)
@@ -23,13 +28,15 @@ module Spin
23
28
  Dir.chdir(root_path)
24
29
  Spin.parse_hook_file(root_path)
25
30
  else
26
- warn "Could not find #{options[:preload]}. Are you running this from the root of a Rails project?"
31
+ logger.warn "Could not find #{options[:preload]}. Are you running this from the root of a Rails project?"
27
32
  end
28
33
 
34
+ set_server_process_pid
35
+
29
36
  open_socket do |socket|
30
37
  preload(options) if root_path
31
38
 
32
- puts "Pushing test results back to push processes" if options[:push_results]
39
+ logger.info "Pushing test results back to push processes" if options[:push_results]
33
40
 
34
41
  loop do
35
42
  run_pushed_tests(socket, options)
@@ -37,6 +44,20 @@ module Spin
37
44
  end
38
45
  end
39
46
 
47
+ def logger
48
+ @logger ||= Spin::Logger.new
49
+ end
50
+
51
+ # Called by the Spin server process to store its process pid.
52
+ def set_server_process_pid
53
+ @server_process_pid = Process.pid
54
+ end
55
+
56
+ # Returns +true+ if the current process is the Spin server process.
57
+ def server_process?
58
+ @server_process_pid == Process.pid
59
+ end
60
+
40
61
  def push(argv, options)
41
62
  files_to_load = convert_push_arguments_to_files(argv)
42
63
 
@@ -49,19 +70,21 @@ module Spin
49
70
 
50
71
  abort if files_to_load.empty?
51
72
 
52
- puts "Spinning up #{files_to_load.join(" ")}"
53
- send_files_to_serve(files_to_load)
73
+ logger.info "Spinning up #{files_to_load.join(" ")}"
74
+ send_files_to_serve(files_to_load, options[:trailing_pushed_args] || [])
54
75
  end
55
76
 
56
77
  private
57
78
 
58
- def send_files_to_serve(files_to_load)
79
+ def send_files_to_serve(files_to_load, trailing_args)
59
80
  # This is the other end of the socket that `spin serve` opens. At this point
60
81
  # `spin serve` will accept(2) our connection.
61
82
  socket = UNIXSocket.open(socket_file)
62
83
 
63
84
  # We put the filenames on the socket for the server to read and then load.
64
- socket.puts files_to_load.join(PUSH_FILE_SEPARATOR)
85
+ payload = files_to_load.join(PUSH_FILE_SEPARATOR)
86
+ payload += "#{ARGS_SEPARATOR}#{trailing_args.join(PUSH_FILE_SEPARATOR)}" unless trailing_args.empty?
87
+ socket.puts payload
65
88
 
66
89
  while line = socket.readpartial(100)
67
90
  break if line[-1,1] == "\0"
@@ -101,9 +124,9 @@ module Spin
101
124
 
102
125
  "#{file_name}:#{line_number}"
103
126
  else
104
- file_name
127
+ file_name.to_s
105
128
  end
106
- end.reject(&:empty?).uniq
129
+ end.compact.reject(&:empty?).uniq
107
130
  end
108
131
 
109
132
  def make_files_relative(files_to_load, root_path)
@@ -115,11 +138,15 @@ module Spin
115
138
  def run_pushed_tests(socket, options)
116
139
  rerun_last_tests_on_quit(options) unless options[:push_results]
117
140
 
141
+ notify_ready
142
+
118
143
  # Since `spin push` reconnects each time it has new files for us we just
119
144
  # need to accept(2) connections from it.
120
145
  conn = socket.accept
121
146
  # This should be a list of relative paths to files.
122
147
  files = conn.gets.chomp
148
+ files, trailing_args = files.split(ARGS_SEPARATOR)
149
+ options[:trailing_args] = trailing_args.nil? ? [] : trailing_args.split(PUSH_FILE_SEPARATOR)
123
150
  files = files.split(PUSH_FILE_SEPARATOR)
124
151
 
125
152
  # If spin is started with the time flag we will track total execution so
@@ -132,15 +159,9 @@ module Spin
132
159
 
133
160
  fork_and_run(files, conn, options)
134
161
 
135
- # WAIT: We don't want the parent process handling multiple test runs at the same
136
- # time because then we'd need to deal with multiple test databases, and
137
- # that destroys the idea of being simple to use. So we wait(2) until the
138
- # child process has finished running the test.
139
- Process.wait
140
-
141
162
  # If we are tracking time we will output it here after everything has
142
163
  # finished running
143
- puts "Total execution time was #{Time.now - start} seconds" if start
164
+ logger.info "Total execution time was #{Time.now - start} seconds" if start
144
165
 
145
166
  # Tests have now run. If we were pushing results to a push process, we can
146
167
  # now disconnect it.
@@ -154,10 +175,33 @@ module Spin
154
175
  # Trap SIGQUIT (Ctrl+\) and re-run the last files that were pushed
155
176
  # TODO test this
156
177
  def rerun_last_tests_on_quit(options)
157
- trap('QUIT') do
158
- fork_and_run(@last_files_ran, nil, options)
159
- Process.wait
178
+ trap('QUIT') { sigquit_handler(options) }
179
+ end
180
+
181
+ # This method is called when a SIGQUIT ought to be handled.
182
+ def sigquit_handler(options)
183
+ # If the current process is not the Spin server process, ignore the
184
+ # signal by doing nothing.
185
+ return unless server_process?
186
+
187
+ unless @last_files_ran
188
+ logger.fatal "Cannot rerun last tests, please push a file to Spin server first"
189
+ return
190
+ end
191
+
192
+ if test_process.alive?
193
+ logger.fatal "Cannot rerun last tests, test process #{test_process} still alive"
194
+ return
160
195
  end
196
+
197
+ fork_and_run(@last_files_ran, nil, options.merge(:trailing_args => @last_trailing_args_used))
198
+
199
+ notify_ready
200
+ end
201
+
202
+ # Notify the user that Spin server is ready for new tests.
203
+ def notify_ready
204
+ logger.info "Ready"
161
205
  end
162
206
 
163
207
  def preload(options)
@@ -181,7 +225,7 @@ module Spin
181
225
  options[:test_framework] ||= determine_test_framework
182
226
 
183
227
  # Preload RSpec to save some time on each test run
184
- if options[:test_framework]
228
+ if options[:test_framework] == :rspec
185
229
  begin
186
230
  require 'rspec/autorun'
187
231
 
@@ -196,7 +240,7 @@ module Spin
196
240
  end
197
241
  end
198
242
  # This is the amount of time that you'll save on each subsequent test run.
199
- puts "Preloaded Rails env in #{duration}s..."
243
+ logger.info "Preloaded Rails environment in #{duration.round(2)}s"
200
244
  end
201
245
 
202
246
  # This socket is how we communicate with `spin push`.
@@ -208,17 +252,40 @@ module Spin
208
252
  File.delete(file) if File.exist?(file)
209
253
  socket = UNIXServer.open(file)
210
254
 
211
- # Trap SIGINT (Ctrl-C) so that we exit cleanly.
212
- trap('SIGINT') do
213
- socket.close
214
- exit
215
- end
255
+ trap('SIGINT') { sigint_handler(socket) }
216
256
 
217
257
  yield socket
218
258
  ensure
219
259
  File.delete(file) if file && File.exist?(file)
220
260
  end
221
261
 
262
+ # This method is called when a SIGINT ought to be handled.
263
+ def sigint_handler(socket)
264
+ # If the current process is not the Spin server process, allow the signal
265
+ # to "bubble up" by exiting.
266
+ exit unless server_process?
267
+
268
+ if sigint_recently_sent?
269
+ socket.close
270
+ exit
271
+ else
272
+ set_last_sigint_sent
273
+ logger.info "Press Ctrl+C again (within #{SIGINT_TIME_WINDOW}s) to exit"
274
+ end
275
+ end
276
+
277
+ # Updates the timestamp when the last SIGINT was sent.
278
+ def set_last_sigint_sent
279
+ @last_sigint_sent = Time.now
280
+ end
281
+
282
+ # Returns +true+ if a SIGINT has been sent within the time window.
283
+ def sigint_recently_sent?
284
+ return if @last_sigint_sent.nil?
285
+
286
+ (Time.now - SIGINT_TIME_WINDOW) < @last_sigint_sent
287
+ end
288
+
222
289
  def determine_test_framework
223
290
  if defined?(RSpec)
224
291
  :rspec
@@ -241,12 +308,17 @@ module Spin
241
308
  path
242
309
  end
243
310
 
311
+ # Returns (and caches) a TestProcess instance.
312
+ def test_process
313
+ @test_process ||= Spin::TestProcess.new
314
+ end
315
+
244
316
  def fork_and_run(files, conn, options)
245
317
  execute_hook(:before_fork)
246
318
  # We fork(2) before loading the file so that our pristine preloaded
247
319
  # environment is untouched. The child process will load whatever code it
248
320
  # needs to, then it exits and we're back to the baseline preloaded app.
249
- fork do
321
+ test_process.pid = fork do
250
322
  # To push the test results to the push process instead of having them
251
323
  # displayed by the server, we reopen $stdout/$stderr to the open
252
324
  # connection.
@@ -263,8 +335,11 @@ module Spin
263
335
 
264
336
  execute_hook(:after_fork)
265
337
 
266
- puts
267
- puts "Loading #{files.inspect}"
338
+ logger.info "Loading #{files.inspect}"
339
+
340
+ trailing_args = options[:trailing_args]
341
+ logger.info "Will run with: #{trailing_args.inspect}" unless trailing_args.empty?
342
+
268
343
 
269
344
  # Unfortunately rspec's interface isn't as simple as just requiring the
270
345
  # test file that you want to run (suddenly test/unit seems like the less
@@ -272,13 +347,21 @@ module Spin
272
347
  if options[:test_framework] == :rspec
273
348
  # We pretend the filepath came in as an argument and duplicate the
274
349
  # behaviour of the `rspec` binary.
275
- ARGV.concat files
350
+ ARGV.concat(files + trailing_args)
276
351
  else
352
+ # Pass any additional push arguments to the test runner
353
+ ARGV.concat trailing_args
277
354
  # We require the full path of the file here in the child process.
278
355
  files.each { |f| require File.expand_path f }
279
356
  end
280
357
  end
281
358
  @last_files_ran = files
359
+ @last_trailing_args_used = options[:trailing_args]
360
+
361
+ # WAIT: We don't want the parent process handling multiple test runs at the same
362
+ # time because then we'd need to deal with multiple test databases, and
363
+ # that destroys the idea of being simple to use.
364
+ test_process.wait
282
365
  end
283
366
 
284
367
  def socket_file
@@ -18,8 +18,8 @@ module Spin
18
18
  $LOAD_PATH.concat(dirs.split(File::PATH_SEPARATOR))
19
19
  end
20
20
 
21
- opts.on("--rspec", "Force the selected test framework to RSpec") { options[:test_framework] = :testunit }
22
- opts.on("--test-unit", "Force the selected test framework to Test::Unit") { options[:test_framework] = :rspec }
21
+ opts.on("--rspec", "Force the selected test framework to RSpec") { options[:test_framework] = :rspec }
22
+ opts.on("--test-unit", "Force the selected test framework to Test::Unit") { options[:test_framework] = :testunit }
23
23
  opts.on("-t", "--time", "See total execution time for each test run") { options[:time] = true }
24
24
  opts.on("--push-results", "Push test results to the push process") { options[:push_results] = true }
25
25
  opts.on("--preload FILE", "Preload this file instead of #{options[:preload]}") { |v| options[:preload] = v }
@@ -27,13 +27,22 @@ module Spin
27
27
  opts.on("-e", "Stub to keep kicker happy")
28
28
  opts.on("-v", "--version", "Show Version") { puts Spin::VERSION; exit }
29
29
  opts.on("-h", "--help") { $stderr.puts opts; exit }
30
+ opts.on('--', 'Separates trailing arguments to be forwarded to the test runner') do |v|
31
+ trailing_pushed_args = []
32
+ while opt = ARGV.shift
33
+ trailing_pushed_args << opt
34
+ end
35
+ options[:trailing_pushed_args] = trailing_pushed_args
36
+ end
30
37
  end
31
38
  parser.parse!
32
39
 
33
40
  subcommand = argv.shift
34
41
  case subcommand
35
- when "serve" then Spin.serve(options)
36
- when "push" then Spin.push(argv, options)
42
+ when "serve", "s"
43
+ then Spin.serve(options)
44
+ when "push", "p"
45
+ then Spin.push(argv, options)
37
46
  else
38
47
  $stderr.puts parser
39
48
  exit 1
@@ -0,0 +1,42 @@
1
+ require 'logger'
2
+ require 'forwardable'
3
+
4
+ module Spin
5
+ class Logger
6
+ extend Forwardable
7
+
8
+ attr_reader :logger
9
+ def_delegators :logger, :fatal,
10
+ :error,
11
+ :warn,
12
+ :info,
13
+ :debug
14
+
15
+ def initialize
16
+ @logger = ::Logger.new($stdout)
17
+ @logger.level = level
18
+ @logger.formatter = formatter
19
+ end
20
+
21
+ private
22
+
23
+ def level
24
+ ::Logger::INFO
25
+ end
26
+
27
+ def formatter
28
+ proc { |_, _, _, message| "[#{caller}] #{message}\n" }
29
+ end
30
+
31
+ # Returns a "Spin" label for log entries, with color, if supported.
32
+ def caller
33
+ name = "Spin"
34
+ $stdout.isatty ? cyan(name) : name
35
+ end
36
+
37
+ # Uses ANSI escape codes to create cyan-colored output.
38
+ def cyan(string)
39
+ "\e[36m#{string}\e[0m"
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,21 @@
1
+ module Spin
2
+ class TestProcess
3
+ attr_accessor :pid
4
+
5
+ # Use wait(2) to block execution until the test process has finished. When
6
+ # finished, reset the assigned pid.
7
+ def wait
8
+ Process.wait
9
+ @pid = nil
10
+ end
11
+
12
+ # Returns +true+ if the test process is alive.
13
+ def alive?
14
+ !@pid.nil?
15
+ end
16
+
17
+ def to_s
18
+ @pid.to_s
19
+ end
20
+ end
21
+ end
@@ -1,3 +1,3 @@
1
1
  module Spin
2
- VERSION = "0.6.0"
2
+ VERSION = "0.7.0"
3
3
  end
@@ -1,10 +1,5 @@
1
1
 
2
2
  describe "Spin" do
3
- before do
4
- # kill all Threads that might be hanging around
5
- Thread.list.each { |thread| thread.exit unless thread == Thread.current }
6
- end
7
-
8
3
  around do |example|
9
4
  folder = File.expand_path("../tmp", __FILE__)
10
5
  `rm -rf #{folder}`
@@ -15,61 +10,18 @@ describe "Spin" do
15
10
  `rm -rf #{folder}`
16
11
  end
17
12
 
18
- def root
19
- File.expand_path '../..', __FILE__
20
- end
21
-
22
- def spin(command, options={})
23
- command = spin_command(command)
24
- result = `#{command}`
25
- raise "FAILED #{command}\n#{result}" if $?.success? == !!options[:fail]
26
- result
27
- end
28
-
29
- def spin_command(command)
30
- "ruby -I #{root}/lib #{root}/bin/spin #{command} 2>&1"
31
- end
32
-
33
- def record_serve(output, command)
34
- IO.popen(spin_command("serve #{command}")) do |pipe|
35
- while str = pipe.readpartial(100)
36
- output << str
37
- end rescue EOFError
38
- end
39
- end
40
-
41
- def write(file, content)
42
- ensure_folder File.dirname(file)
43
- File.open(file, 'w'){|f| f.write content }
44
- end
45
-
46
- def read(file)
47
- File.read file
48
- end
49
-
50
- def delete(file)
51
- `rm #{file}`
52
- end
53
-
54
- def ensure_folder(folder)
55
- `mkdir -p #{folder}` unless File.exist?(folder)
56
- end
57
-
58
- def serve_and_push(serve_command, push_commands)
59
- serve_output = ""
60
- t1 = Thread.new { record_serve(serve_output, serve_command) }
61
- sleep 0.1
62
- push_output = [*push_commands].map{ |cmd| spin("push #{cmd}") }
63
- sleep 0.2
64
- t1.kill
65
- [serve_output, push_output]
13
+ after do
14
+ kill_all_threads
15
+ kill_all_children
66
16
  end
67
17
 
68
18
  context "with simple setup" do
69
19
  before do
70
20
  write "config/application.rb", "$xxx = 1234"
71
21
  write "test/foo_test.rb", "puts $xxx * 2"
72
- @default_pushed = "Spinning up test/foo_test.rb\n"
22
+ @log_label = "[Spin]"
23
+ @default_pushed = "#{@log_label} Spinning up test/foo_test.rb\n"
24
+ @preloaded_message = "Preloaded Rails environment in "
73
25
  end
74
26
 
75
27
  it "shows help when no arguments are given" do
@@ -78,14 +30,14 @@ describe "Spin" do
78
30
 
79
31
  it "can serve and push" do
80
32
  served, pushed = serve_and_push("", "test/foo_test.rb")
81
- served.should include "Preloaded Rails env in "
33
+ served.should include @preloaded_message
82
34
  served.should include "2468"
83
35
  pushed.first.should == @default_pushed
84
36
  end
85
37
 
86
38
  it "can run files without .rb extension" do
87
39
  served, pushed = serve_and_push("", "test/foo_test")
88
- served.should include "Preloaded Rails env in "
40
+ served.should include @preloaded_message
89
41
  served.should include "2468"
90
42
  pushed.first.should == @default_pushed
91
43
  end
@@ -93,7 +45,7 @@ describe "Spin" do
93
45
  it "can run multiple times" do
94
46
  write "test/foo_test.rb", "puts $xxx *= 2"
95
47
  served, pushed = serve_and_push("", ["test/foo_test.rb", "test/foo_test.rb", "test/foo_test.rb"])
96
- served.should include "Preloaded Rails env in "
48
+ served.should include @preloaded_message
97
49
  served.scan("2468").size.should == 3
98
50
  pushed.size.should == 3
99
51
  pushed.each{|x| x.should == @default_pushed }
@@ -102,17 +54,17 @@ describe "Spin" do
102
54
  it "can run multiple files at once" do
103
55
  write "test/bar_test.rb", "puts $xxx / 2"
104
56
  served, pushed = serve_and_push("", "test/foo_test.rb test/bar_test.rb")
105
- served.should include "Preloaded Rails env in "
57
+ served.should include @preloaded_message
106
58
  served.should include "2468"
107
59
  served.should include "617"
108
- pushed.first.should == "Spinning up test/foo_test.rb test/bar_test.rb\n"
60
+ pushed.first.should == "#{@log_label} Spinning up test/foo_test.rb test/bar_test.rb\n"
109
61
  end
110
62
 
111
63
  it "complains when the preloaded file cannot be found" do
112
64
  delete "config/application.rb"
113
65
  write "test/foo_test.rb", "puts 2468"
114
66
  served, pushed = serve_and_push("", "test/foo_test.rb")
115
- served.should_not include "Preloaded Rails env in "
67
+ served.should_not include @preloaded_message
116
68
  served.should include "Could not find config/application.rb. Are you running"
117
69
  served.should include "2468"
118
70
  pushed.first.should == @default_pushed
@@ -142,6 +94,19 @@ describe "Spin" do
142
94
  served.should include "BBB"
143
95
  served.should_not include "CCC"
144
96
  end
97
+
98
+ it "can pass trailing arguments to the spec runner" do
99
+ write "spec/foo_spec.rb", <<-RUBY
100
+ describe "x" do
101
+ it("a"){ puts "AAA" }
102
+ it("b"){ puts "BBB" }
103
+ it("c"){ puts "CCC" }
104
+ end
105
+ RUBY
106
+ served, pushed = serve_and_push("", ["spec/foo_spec.rb -- --profile"])
107
+ served.should include 'Will run with: ["--profile"]'
108
+ served.should include 'Top 3 slowest examples'
109
+ end
145
110
  end
146
111
 
147
112
  context "options" do
@@ -155,7 +120,7 @@ describe "Spin" do
155
120
 
156
121
  it "can --push-results" do
157
122
  served, pushed = serve_and_push("--push-results", "test/foo_test.rb")
158
- served.should include "Preloaded Rails env in "
123
+ served.should include @preloaded_message
159
124
  served.should_not include "2468"
160
125
  pushed.first.should include "2468"
161
126
  end
@@ -164,7 +129,7 @@ describe "Spin" do
164
129
  write "config/application.rb", "raise"
165
130
  write "config/environment.rb", "$xxx = 1234"
166
131
  served, pushed = serve_and_push("--preload config/environment.rb", "test/foo_test.rb")
167
- served.should include "Preloaded Rails env in "
132
+ served.should include @preloaded_message
168
133
  served.should include "2468"
169
134
  pushed.first.should == @default_pushed
170
135
  end
@@ -179,7 +144,7 @@ describe "Spin" do
179
144
 
180
145
  it "ignores -e" do
181
146
  served, pushed = serve_and_push("-e", "test/foo_test.rb -e")
182
- served.should include "Preloaded Rails env in "
147
+ served.should include @preloaded_message
183
148
  served.should include "2468"
184
149
  pushed.first.should == @default_pushed
185
150
  end
@@ -190,6 +155,12 @@ describe "Spin" do
190
155
  served.should include "Total execution time was 0."
191
156
  pushed.first.should == @default_pushed
192
157
  end
158
+
159
+ it "can pass trailing arguments to the test runner" do
160
+ served, pushed = serve_and_push("", ["test/foo_test.rb -- -n /validates/"])
161
+ served.should include 'Will run with: ["-n", "/validates/"]'
162
+ pushed.first.should == @default_pushed
163
+ end
193
164
  end
194
165
 
195
166
  context "hooks" do
@@ -231,4 +202,74 @@ describe "Spin" do
231
202
  end
232
203
  end
233
204
  end
205
+
206
+ private
207
+
208
+ def root
209
+ File.expand_path '../..', __FILE__
210
+ end
211
+
212
+ def spin(command, options={})
213
+ command = spin_command(command)
214
+ result = `#{command}`
215
+ raise "FAILED #{command}\n#{result}" if $?.success? == !!options[:fail]
216
+ result
217
+ end
218
+
219
+ def spin_command(command)
220
+ "ruby -I #{root}/lib #{root}/bin/spin #{command} 2>&1"
221
+ end
222
+
223
+ def record_serve(output, command)
224
+ IO.popen(spin_command("serve #{command}")) do |pipe|
225
+ while str = pipe.readpartial(100)
226
+ output << str
227
+ end rescue EOFError
228
+ end
229
+ end
230
+
231
+ def write(file, content)
232
+ ensure_folder File.dirname(file)
233
+ File.open(file, 'w'){|f| f.write content }
234
+ end
235
+
236
+ def read(file)
237
+ File.read file
238
+ end
239
+
240
+ def delete(file)
241
+ `rm #{file}`
242
+ end
243
+
244
+ def ensure_folder(folder)
245
+ `mkdir -p #{folder}` unless File.exist?(folder)
246
+ end
247
+
248
+ def serve_and_push(serve_command, push_commands)
249
+ serve_output = ""
250
+ t1 = Thread.new { record_serve(serve_output, serve_command) }
251
+ sleep 0.1
252
+ push_output = [*push_commands].map{ |cmd| spin("push #{cmd}") }
253
+ sleep 0.2
254
+ t1.kill
255
+ [serve_output, push_output]
256
+ end
257
+
258
+ def kill_all_threads
259
+ Thread.list.each { |thread| thread.exit unless thread == Thread.current }
260
+ end
261
+
262
+ def kill_all_children
263
+ children = child_pids
264
+ `kill -9 #{children.join(" ")}` unless children.empty?
265
+ end
266
+
267
+ def child_pids
268
+ pid = Process.pid
269
+ pipe = IO.popen("ps -ef | grep #{pid}")
270
+ pipe.readlines.map do |line|
271
+ parts = line.split(/\s+/)
272
+ parts[2] if parts[3] == pid.to_s and parts[2] != pipe.pid.to_s
273
+ end.compact
274
+ end
234
275
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: spin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,8 +9,40 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-13 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2013-07-25 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rake
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 2.13.0
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 2.13.0
14
46
  description: ! 'Spin preloads your Rails environment to speed up your autotest(ish)
15
47
  workflow.
16
48
 
@@ -27,6 +59,8 @@ files:
27
59
  - README.md
28
60
  - lib/spin/cli.rb
29
61
  - lib/spin/hooks.rb
62
+ - lib/spin/logger.rb
63
+ - lib/spin/test_process.rb
30
64
  - lib/spin/version.rb
31
65
  - lib/spin.rb
32
66
  - spec/integration_spec.rb
@@ -51,7 +85,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
51
85
  version: '0'
52
86
  requirements: []
53
87
  rubyforge_project:
54
- rubygems_version: 1.8.25
88
+ rubygems_version: 1.8.23
55
89
  signing_key:
56
90
  specification_version: 3
57
91
  summary: Spin preloads your Rails environment to speed up your autotest(ish) workflow.