angael 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,15 +1,17 @@
1
+ require 'angael/process_helper'
1
2
  module Angael
2
3
  # A Manager has a number of of worker objects. Starting the Manager simply
3
4
  # calls #start! on each worker, then it goes into an infinite loop, waiting
4
5
  # for SIGINT or SIGTERM. When either of those is received, the manager will
5
- # call #stop! on each Worker.
6
+ # call #stop_with_wait on each Worker.
6
7
  class Manager
8
+ include ProcessHelper
7
9
  attr_reader :workers
8
10
 
9
11
  # Creates a new manager.
10
12
  #
11
13
  # @worker_class [Class] The class to use for the workers. Must respond
12
- # to #new, #start!, and #stop!
14
+ # to #new, #start!, and #stop_with_wait
13
15
  # @worker_count [Integer] The number of workers to manager. Default is 1.
14
16
  # @worker_args [Array] An array of arguments that will be passed to
15
17
  # worker_class.new. The arguments will be splatted
@@ -33,6 +35,8 @@ module Angael
33
35
  @workers = []
34
36
  worker_count.times { workers << worker_class.new(*worker_args) }
35
37
  @restart_after = opts[:restart_after]
38
+ # TODO: Add a spec for this
39
+ raise ArgumentError, ':restart_after must be either an Integer greater than zero or nil' if @restart_after && (@restart_after.to_i != @restart_after || @restart_after == 0)
36
40
  @logger = opts[:logger]
37
41
  if @logger
38
42
  @log_level = opts[:log_level] || begin
@@ -44,13 +48,13 @@ module Angael
44
48
 
45
49
 
46
50
  # Starts workers by calling Worker#start! Loops forever waiting for SIGINT
47
- # or SIGTERM, at which time it calls Worker#stop! on each worker.
51
+ # or SIGTERM, at which time it calls Worker#stop_with_wait on each worker.
48
52
  def start!
49
53
  workers.each { |w| w.start! }
50
54
 
51
55
  trap("CHLD") do
52
56
  workers.each do |w|
53
- result = wait(w.pid)
57
+ result = exit_status(w.pid)
54
58
  #print w.pid.to_s
55
59
  #print "\t"
56
60
  #p result
@@ -62,23 +66,40 @@ module Angael
62
66
  end
63
67
  end
64
68
  trap("INT") do
65
- stop!
69
+ log("SIGINT Received")
70
+ @interrupted = true
66
71
  end
67
72
  trap("TERM") do
68
- stop!
73
+ log("SIGTERM Received")
74
+ @interrupted = true
69
75
  end
70
76
 
71
77
  if @restart_after
78
+ restart_after_counter = @restart_after
72
79
  loop do
73
- # Periodically restart workers, 1 at a time.
74
- sleep @restart_after
75
- w = next_worker_to_restart
76
- w.stop!
77
- w.start!
80
+ stop! if @interrupted
81
+
82
+ # This ensures we are checking @interrupted every 1 second, but we
83
+ # can set @restart_after to something greater than zero.
84
+ if restart_after_counter > 0
85
+ sleep 1
86
+ restart_after_counter -= 1
87
+ else
88
+ # Periodically restart workers, 1 at a time.
89
+ log("Sleeping for #@restart_after seconds")
90
+ w = next_worker_to_restart
91
+ log("Time to restart a worker: Calling #stop_with_wait for worker #{w.inspect}")
92
+ w.stop_with_wait
93
+ log("Worker has been stopped: #{w.inspect}")
94
+ w.start!
95
+ log("Worker has been restarted: #{w.inspect}")
96
+ w = nil
97
+ restart_after_counter = @restart_after
98
+ end
78
99
  end
79
100
  else
80
101
  loop do
81
- # Don't restart workers if nothing is wrong.
102
+ stop! if @interrupted
82
103
  sleep 1
83
104
  end
84
105
  end
@@ -91,11 +112,18 @@ module Angael
91
112
  #########
92
113
 
93
114
  def stop!
94
- log("SIGINT Received")
115
+ log("Attempting to gracefully stopping worker manager")
116
+ # Tell each worker to stop, without waiting to see if it worked.
117
+ workers.each { |w|
118
+ log("Calling #stop_without_wait for worker #{w.inspect}")
119
+ w.stop_without_wait
120
+ log("Finished call to #stop_without_wait for worker #{w.inspect}")
121
+ }
122
+ # Wait for each worker to stop, one at a time.
95
123
  workers.each { |w|
96
- log("Calling #stop! for worker #{w.inspect}")
97
- w.stop!
98
- log("Finished call to #stop! for worker #{w.inspect}")
124
+ log("Calling #stop_with_wait for worker #{w.inspect}")
125
+ w.stop_with_wait
126
+ log("Finished call to #stop_with_wait for worker #{w.inspect}")
99
127
  }
100
128
  exit 0
101
129
  end
@@ -104,20 +132,6 @@ module Angael
104
132
  @logger.add(@log_level, "#{Time.now.utc} - #{self.class} (pid #{$$}): #{msg}") if @logger
105
133
  end
106
134
 
107
- # Returns immediately. If the process is still running, it returns nil.
108
- # If the process is a zombie, it returns an array with the pid as the
109
- # first element and a Process::Status object as the 2nd element, i.e.
110
- # it returns the same thing as Process.wait2. If the process does not
111
- # exist (i.e. it is completely gone) then it returns an array with the
112
- # pid as the first element and nil as the 2nd element (because there
113
- # is no Process::Status object to return).
114
- def wait(pid)
115
- begin
116
- Process.wait2(pid, Process::WNOHANG)
117
- rescue Errno::ECHILD
118
- [pid, nil] # It did exit, but we don't know the exit status.
119
- end
120
- end
121
135
 
122
136
  def next_worker_to_restart
123
137
  @worker_count ||= workers.size
@@ -0,0 +1,37 @@
1
+ module Angael
2
+ module ProcessHelper
3
+ # Returns immediately. If the process is still running, it returns nil.
4
+ # If the process is a zombie, it returns an array with the pid as the
5
+ # first element and a Process::Status object as the 2nd element, i.e.
6
+ # it returns the same thing as Process.wait2. If the process does not
7
+ # exist (i.e. it is completely gone) or it is not accessible to the
8
+ # current process (i.e. an Errno::EPERM error is raised), then this method
9
+ # returns an array with the pid as the first element and nil as the 2nd
10
+ # element (because there is no Process::Status object to return).
11
+ # If the argument is nil, then it returns [nil, nil] because there can't be
12
+ # a process running with the pid nil.
13
+ def exit_status(pid)
14
+ return [nil, nil] if pid.nil?
15
+
16
+ # Sometimes wait2 returns nil even when the process has exited. This
17
+ # raises an Errno::ESRCH error in that case.
18
+ raise "Unexpected return value from Process.kill(0, pid)" unless Process.kill(0, pid) == 1
19
+
20
+ Process.wait2(pid, Process::WNOHANG)
21
+ rescue Errno::ECHILD, Errno::ESRCH, Errno::EPERM
22
+ # There is no longer any record of this PID.
23
+ # It did exit, but we don't know the exit status.
24
+ [pid, nil]
25
+ end
26
+
27
+
28
+ # Will return nil instead of raising Errno::ESRCH when the process does
29
+ # not exists.
30
+ # TODO: Add explicit tests for this.
31
+ def send_signal(signal, pid)
32
+ Process.kill(signal, pid)
33
+ rescue Errno::ESRCH
34
+ nil
35
+ end
36
+ end
37
+ end
@@ -1,3 +1,3 @@
1
1
  module Angael
2
- VERSION = "0.0.7"
2
+ VERSION = "0.0.8"
3
3
  end
data/lib/angael/worker.rb CHANGED
@@ -1,4 +1,4 @@
1
- require 'timeout'
1
+ require 'angael/process_helper'
2
2
  module Angael
3
3
  # Usage
4
4
  # include Angael::Worker
@@ -17,6 +17,7 @@ module Angael
17
17
  # it is sent SIGINT. If you don't define this method, it waits
18
18
  # 60 seconds.
19
19
  module Worker
20
+ include ProcessHelper
20
21
  class ChildProcessNotStoppedError < StandardError; end
21
22
 
22
23
  attr_reader :pid
@@ -64,9 +65,13 @@ module Angael
64
65
  start!
65
66
  end
66
67
 
67
- def stop!
68
+ # Returns true if SIGINT was sent to the child process, even if the child
69
+ # process does not exists.
70
+ # Returns false if started? is true.
71
+ # Sets stopping? to false.
72
+ def stop_without_wait
68
73
  unless started?
69
- __log("Called stop for worker with PID #{pid} but it is not started")
74
+ __log("Tried to stop worker with PID #{pid} but it is not started")
70
75
  return false
71
76
  end
72
77
 
@@ -74,28 +79,38 @@ module Angael
74
79
  # stopped the child process.
75
80
  @stopping = true
76
81
 
77
- begin
78
- __log("Sending SIGINT to child process with pid #{pid}.")
79
- Timeout::timeout(timeout) do
80
- Process.kill('INT', pid)
81
- wait_for_child
82
- end
83
- rescue Timeout::Error
84
- begin
85
- __log("Child process with pid #{pid} did not stop within #{timeout} seconds of SIGINT. Sending SIGKILL to child process.")
86
- # This only leaves 1 second for the SIGKILL to take effect. I don't
87
- # know if that is enough time (or maybe too much time).
88
- Timeout::timeout(1) do
89
- Process.kill('KILL', pid)
90
- wait_for_child
91
- end
92
- rescue Timeout::Error
93
- if pid_running?
94
- msg = "Unable to kill child process with PID: #{pid}"
95
- __log(msg)
96
- raise ChildProcessNotStoppedError, msg
97
- end
98
- end
82
+ __log("Sending SIGINT to child process with pid #{pid}.")
83
+ send_signal('INT', pid)
84
+ true
85
+ end
86
+
87
+ # Keeps sending SIGINT until the child process exits. If #timeout seconds
88
+ # pass, then it sends 1 SIGKILL. If that also fails, it raises ChildProcessNotStoppedError.
89
+ def stop_with_wait
90
+ return false unless stop_without_wait
91
+
92
+ __log("Waiting for child process with pid #{pid} to stop.")
93
+
94
+ counter = 0
95
+
96
+ while pid_running? && counter < timeout
97
+ sleep 1
98
+ counter += 1
99
+ __log("Sending SIGINT to child process with pid #{pid}. Attempt Count: #{counter}.")
100
+ send_signal('INT', pid)
101
+ end
102
+
103
+ if pid_running?
104
+ __log("Child process with pid #{pid} did not stop within #{timeout} seconds of SIGINT. Sending SIGKILL to child process.")
105
+ send_signal('KILL', pid)
106
+ sleep 1
107
+ end
108
+
109
+ if pid_running?
110
+ # SIGKILL didn't work.
111
+ msg = "Unable to kill child process with PID: #{pid}"
112
+ __log(msg)
113
+ raise ChildProcessNotStoppedError, msg
99
114
  end
100
115
  end
101
116
 
@@ -132,34 +147,10 @@ module Angael
132
147
  end
133
148
 
134
149
 
135
- # Note: if the pid is running, but this process doesn't have permissions to
136
- # access it, then this will return false.
137
150
  def pid_running?
138
- begin
139
- Process.kill(0, pid) == 1
140
- rescue Errno::ESRCH, Errno::EPERM
141
- false
142
- end
151
+ !exit_status(pid)
143
152
  end
144
153
 
145
- # Will just return if the child process is not running.
146
- def wait_for_child(opts={})
147
- begin
148
- __log("Waiting for child with pid #{pid}.")
149
- if opts[:dont_block]
150
- # When this is called as the result of a SIGCHLD
151
- # we need to pass in Process::WNOHANG as the 2nd argument, otherwise when
152
- # there are multiple workers, some workers will trap SIGCHLD when other
153
- # workers' child processes die. Without this argument, those workers will
154
- # hang forever, which also hangs the worker manager.
155
- Process.wait(pid, Process::WNOHANG)
156
- else
157
- Process.wait(pid)
158
- end
159
- rescue Errno::ECHILD
160
- # The child process has already been reaped.
161
- end
162
- end
163
154
 
164
155
  # This is the standard/default way of doing it. Overwrite this if you want
165
156
  # to wrap it in an exception handler, for example.
@@ -40,40 +40,33 @@ describe Angael::Manager do
40
40
  end
41
41
 
42
42
  context "when :restart_after is set to 0.5" do
43
- subject { Angael::Manager.new(Angael::TestSupport::SampleWorker, 3, [], :restart_after => 0.5) }
43
+ after do
44
+ clean_up_pid(@pid)
45
+ end
46
+
47
+ subject { Angael::Manager.new(Angael::TestSupport::SampleWorker, 3, [], :restart_after => 1) }
48
+
44
49
  it "should restart workers 1 at a time, at 1 second intervals" do
45
50
  subject.workers.each do |w|
46
- w.stub(:start!) # We don't actually need the workers to fork.
51
+ # This isn't used for restarts.
52
+ w.stub(:stop_without_wait)
47
53
  end
48
54
 
49
- subject.workers[0].should_receive(:stop!).exactly(1).times
50
- subject.workers[1].should_receive(:stop!).exactly(2).times # This is the worker that got restarted.
51
- subject.workers[2].should_receive(:stop!).exactly(1).times
52
-
53
- subject.workers[0].should_receive(:start!).exactly(1).times
54
- subject.workers[1].should_receive(:start!).exactly(2).times # This is the worker that got restarted.
55
- subject.workers[2].should_receive(:start!).exactly(1).times
56
-
57
-
58
- # As an alternative to should_receive_in_child_process, we
59
- # fork a process which will send SIGINT to this current process.
60
- # Then we start the Manager in this process and wait for it to
61
- # get the SIGINT. Finally we rescue SystemExit so that this
62
- # process doesn't exit with the Manager stops.
63
- # TODO: Be consistent in my use of this technique vs. should_receive_in_child_process.
64
- current_pid = $$
65
- pid = Process.fork do
66
- sleep 0.6 # Add a 0.1 second buffer to the value of :restart_after to give the process a chance to start.
67
- Process.kill('INT', current_pid)
68
- exit 0
69
- end
70
- begin
55
+ subject.workers[0].should_receive_in_child_process(:stop_with_wait).exactly(1).times
56
+ subject.workers[1].should_receive_in_child_process(:stop_with_wait).exactly(2).times # This is the worker that got restarted.
57
+ subject.workers[2].should_receive_in_child_process(:stop_with_wait).exactly(1).times
58
+
59
+ subject.workers[0].should_receive_in_child_process(:start!).exactly(1).times
60
+ subject.workers[1].should_receive_in_child_process(:start!).exactly(2).times # This is the worker that got restarted.
61
+ subject.workers[2].should_receive_in_child_process(:start!).exactly(1).times
62
+
63
+ @pid = Process.fork do
71
64
  subject.start!
72
- rescue SystemExit
73
- nil
74
65
  end
75
66
 
76
- clean_up_pid(pid)
67
+ sleep 1.1 # Add a 0.1 second buffer to the value of :restart_after to give the process a chance to start.
68
+ Process.kill('INT', @pid)
69
+
77
70
  end
78
71
  end
79
72
 
@@ -94,7 +87,7 @@ describe Angael::Manager do
94
87
  end
95
88
 
96
89
  sleep 0.1 # Give the process a chance to start.
97
- # This sends stop! to all the workers.
90
+ # This sends stop_with_wait to all the workers.
98
91
  Process.kill('INT', @pid)
99
92
  sleep 0.1 # Give the TempFile a chance to flush
100
93
  end
@@ -157,11 +150,29 @@ describe Angael::Manager do
157
150
 
158
151
  %w(INT TERM).each do |sig|
159
152
  context "when it receives a SIG#{sig}" do
160
- it "should call #stop! on each Worker" do
153
+ it "should call #stop_without_wait on each Worker" do
154
+ subject.workers.each do |w|
155
+ w.stub(:start!) # We don't care about the sub-process, so don't start it.
156
+
157
+ w.should_receive_in_child_process(:stop_without_wait).at_least(1).times
158
+ end
159
+
160
+ pid = Process.fork do
161
+ subject.start!
162
+ end
163
+ sleep 0.1 # Give the process a chance to start.
164
+ Process.kill(sig, pid)
165
+ sleep 0.1 # Give the TempFile a chance to flush
166
+
167
+ # Clean up
168
+ Process.wait(pid)
169
+ end
170
+
171
+ it "should call #stop_with_wait on each Worker" do
161
172
  subject.workers.each do |w|
162
173
  w.stub(:start!) # We don't care about the sub-process, so don't start it.
163
174
 
164
- w.should_receive_in_child_process(:stop!)
175
+ w.should_receive_in_child_process(:stop_with_wait)
165
176
  end
166
177
 
167
178
  pid = Process.fork do
@@ -0,0 +1,72 @@
1
+ require 'spec_helper'
2
+
3
+ describe Angael::ProcessHelper do
4
+ include Angael::ProcessHelper
5
+
6
+ describe "exit_status" do
7
+ context "when passed the argument nil" do
8
+ it "should return an array of [nil, nil]" do
9
+ exit_status(nil).should == [nil, nil]
10
+ end
11
+ end
12
+
13
+ context "when Process.kill raises Errno::ESRCH" do
14
+ before { Process.stub(:kill) { raise Errno::ESRCH } }
15
+ it "should return an array of [pid, nil]" do
16
+ exit_status(123456).should == [123456, nil]
17
+ end
18
+ end
19
+
20
+ context "when Process.kill raises Errno::EPERM" do
21
+ before { Process.stub(:kill) { raise Errno::EPERM } }
22
+ it "should return an array of [pid, nil]" do
23
+ exit_status(123456).should == [123456, nil]
24
+ end
25
+ end
26
+
27
+ context "when Process.kill return 1" do
28
+ before { Process.stub(:kill).and_return(1) }
29
+
30
+ context "when Process.wait2 raises Errno::ECHILD" do
31
+ before { Process.stub(:wait2) { raise Errno::ECHILD } }
32
+ it "should return an array of [pid, nil]" do
33
+ exit_status(123456).should == [123456, nil]
34
+ end
35
+ end
36
+
37
+ context "when Process.wait2 returns nil" do
38
+ before { Process.stub(:wait2).and_return(nil) }
39
+ it "should return nil" do
40
+ exit_status(123456).should be_nil
41
+ end
42
+ end
43
+
44
+ context "when Process.wait2 returns an array of [pid, exitstatus]" do
45
+ before do
46
+ @exitstatus = mock(Process::Status)
47
+ Process.stub(:wait2).and_return([123456, @exitstatus])
48
+ end
49
+ it "should return an array of [pid, exitstatus]" do
50
+ exit_status(123456).should == [123456, @exitstatus]
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+
57
+ describe "send_signal" do
58
+ it "should call kill" do
59
+ pid = 123456
60
+ sig = 'INT'
61
+ Process.should_receive(:kill).with(sig, pid)
62
+ send_signal(sig, pid)
63
+ end
64
+
65
+ context "when Process.kill raises Errno::ESRCH" do
66
+ it "should return nil" do
67
+ Process.stub(:kill) { raise Errno::ESRCH }
68
+ send_signal('INT', 123456).should be_nil
69
+ end
70
+ end
71
+ end
72
+ end
@@ -7,7 +7,7 @@ describe Angael::Worker do
7
7
 
8
8
  describe "#start!" do
9
9
  before { subject.stub(:work => nil) }
10
- after { subject.stop! if subject.started? }
10
+ after { subject.stop_with_wait if subject.started? }
11
11
 
12
12
  it "should set #pid" do
13
13
  subject.pid.should be_nil
@@ -85,24 +85,68 @@ describe Angael::Worker do
85
85
 
86
86
 
87
87
 
88
- describe "#stop!" do
88
+ describe "#stop_without_wait" do
89
89
  before { subject.stub(:work => nil) }
90
- after { subject.stop! }
90
+ after { subject.stop_with_wait }
91
91
 
92
92
  context "when stopped" do
93
93
  it "should return false" do
94
94
  subject.should_not be_started
95
- subject.stop!.should be_false
95
+ subject.stop_without_wait.should be_false
96
96
  end
97
97
 
98
98
  it "should not be stopping" do
99
- subject.stop!
99
+ subject.stop_without_wait
100
100
  subject.should_not be_stopping
101
101
  end
102
102
 
103
103
  it "should not send a SIGINT to the child process" do
104
104
  should_not_receive_and_run(Process, :kill, 'INT', subject.pid)
105
- subject.stop!
105
+ subject.stop_without_wait
106
+ end
107
+
108
+ it "should return false" do
109
+ subject.stop_without_wait.should be_false
110
+ end
111
+ end
112
+
113
+ context "when started" do
114
+ before { subject.start! }
115
+ it "should send a SIGINT to the child process" do
116
+ should_receive_and_run(Process, :kill, 'INT', subject.pid)
117
+ subject.stop_without_wait
118
+ end
119
+
120
+ it "should be stopping" do
121
+ subject.stop_without_wait
122
+ subject.should be_stopping
123
+ end
124
+
125
+ it "should return true" do
126
+ subject.stop_without_wait.should be_true
127
+ end
128
+ end
129
+ end
130
+
131
+
132
+ describe "#stop_with_wait" do
133
+ before { subject.stub(:work => nil) }
134
+ after { subject.stop_with_wait }
135
+
136
+ context "when stopped" do
137
+ it "should return false" do
138
+ subject.should_not be_started
139
+ subject.stop_with_wait.should be_false
140
+ end
141
+
142
+ it "should not be stopping" do
143
+ subject.stop_with_wait
144
+ subject.should_not be_stopping
145
+ end
146
+
147
+ it "should not send a SIGINT to the child process" do
148
+ should_not_receive_and_run(Process, :kill, 'INT', subject.pid)
149
+ subject.stop_with_wait
106
150
  end
107
151
  end
108
152
 
@@ -110,12 +154,12 @@ describe Angael::Worker do
110
154
  it "should send a SIGINT to the child process" do
111
155
  subject.start!
112
156
  should_receive_and_run(Process, :kill, 'INT', subject.pid)
113
- subject.stop!
157
+ subject.stop_with_wait
114
158
  end
115
159
 
116
160
  it "should be stopping" do
117
161
  subject.start!
118
- subject.stop!
162
+ subject.stop_with_wait
119
163
  subject.should be_stopping
120
164
  end
121
165
 
@@ -130,23 +174,23 @@ describe Angael::Worker do
130
174
  subject.start!
131
175
  end
132
176
  it "should be stopped" do
133
- subject.stop!
177
+ subject.stop_with_wait
134
178
  subject.should be_stopped
135
179
  end
136
180
 
137
181
  it "should not have a child process with the pid #pid" do
138
- subject.stop!
182
+ subject.stop_with_wait
139
183
  pid_running?(subject.pid).should be_false
140
184
  end
141
185
 
142
186
  it "should not send a SIGKILL to the child process" do
143
187
  should_not_receive_and_run(Process, :kill, 'KILL', subject.pid)
144
- subject.stop!
188
+ subject.stop_with_wait
145
189
  end
146
190
 
147
191
  it "should have the (now dead) child process' PID as #pid" do
148
192
  pid = subject.pid
149
- subject.stop!
193
+ subject.stop_with_wait
150
194
  subject.pid.should == pid
151
195
  end
152
196
  end
@@ -164,18 +208,18 @@ describe Angael::Worker do
164
208
  end
165
209
 
166
210
  it "should be stopped" do
167
- subject.stop!
211
+ subject.stop_with_wait
168
212
  subject.should be_stopped
169
213
  end
170
214
 
171
215
  it "should not have a child process with the pid #pid" do
172
- subject.stop!
216
+ subject.stop_with_wait
173
217
  pid_running?(subject.pid).should be_false
174
218
  end
175
219
 
176
220
  it "should send a SIGKILL to the child process" do
177
221
  should_receive_and_run(Process, :kill, 'KILL', subject.pid)
178
- subject.stop!
222
+ subject.stop_with_wait
179
223
  end
180
224
 
181
225
  context "child process does not die after receiving SIGKILL" do
@@ -204,7 +248,7 @@ describe Angael::Worker do
204
248
  it "should raise an error with the child process' pid in the message" do
205
249
  pid_running?(subject.pid).should be_true
206
250
  lambda do
207
- subject.stop!
251
+ subject.stop_with_wait
208
252
  end.should raise_error(Angael::Worker::ChildProcessNotStoppedError, /#{subject.pid}/)
209
253
 
210
254
  # Confirm the PID is still running
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: angael
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.7
5
+ version: 0.0.8
6
6
  platform: ruby
7
7
  authors:
8
8
  - Paul Cortens
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-06-14 00:00:00 -07:00
13
+ date: 2011-06-20 00:00:00 -07:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -51,9 +51,11 @@ files:
51
51
  - angael.gemspec
52
52
  - lib/angael.rb
53
53
  - lib/angael/manager.rb
54
+ - lib/angael/process_helper.rb
54
55
  - lib/angael/version.rb
55
56
  - lib/angael/worker.rb
56
57
  - spec/lib/angael/manager_spec.rb
58
+ - spec/lib/angael/process_helper_spec.rb
57
59
  - spec/lib/angael/worker_spec.rb
58
60
  - spec/spec_helper.rb
59
61
  - spec/support/io_helpers.rb