bumbleworks 0.0.90 → 0.0.91

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: 28e8c92dc9de4fb9204a5b2348565a1d47d960b0
4
- data.tar.gz: 6fb4c218cbe093862c9a61b418aa32b2a4594238
3
+ metadata.gz: a41e2e146849e9de9752fcc88353008da72e6c94
4
+ data.tar.gz: 776a60a147c9cbd1512cae7d81519667ec514c2a
5
5
  SHA512:
6
- metadata.gz: abb2292ed00b147314a70f633619c62e77a20e5ccd7bd73c4cb91e2131c8e58b5764dc3a38a4bb840079cc51859618a654b1ef5ae32270ea8f58a7429f5a8fce
7
- data.tar.gz: b5fee4533318cea0829dcb9e691d6ccea143e94157730e89fccae34dbeac3513f1620d39b8f46f876f18230d0361fbb53cd5817c5ed861224b6fc78241019285
6
+ metadata.gz: 5f1d34125e457ae2b08b599d80cddd4e2f51b7ae332636d14c52cfb0fa2dd8f40e92ccc9336c30356d43bf17593338c5018bb5e1cc0d5a0e90ce0a5fd7d8a030
7
+ data.tar.gz: 01636c8cf9af7e4b5f452282f663962ef16afe8520ccb469a5f1deb8730bbcd76f810acb39aae6eaf13e3e436e41ea14f017910a479994c11414abe0c30c94ca
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.5
5
+ - 2.2.0
@@ -1,3 +1,3 @@
1
1
  module Bumbleworks
2
- VERSION = "0.0.90"
2
+ VERSION = "0.0.91"
3
3
  end
@@ -8,12 +8,13 @@ class Bumbleworks::Worker < Ruote::Worker
8
8
 
9
9
  class << self
10
10
  def info
11
- Bumbleworks.dashboard.worker_info || {}
11
+ Bumbleworks::Worker::Info || {}
12
12
  end
13
13
 
14
14
  def shutdown_all(options = {})
15
15
  # First, send all running workers a message to stop
16
16
  change_worker_state('stopped', options)
17
+ ensure
17
18
  # Now ensure that future started workers will be started
18
19
  # in "running" mode instead of automatically stopped
19
20
  change_worker_state('running', options)
@@ -27,10 +28,10 @@ class Bumbleworks::Worker < Ruote::Worker
27
28
  change_worker_state('running', options)
28
29
  end
29
30
 
30
- def worker_states
31
+ def active_worker_states
31
32
  info.inject({}) { |hsh, info|
32
- id, state = info[0], info[1]['state']
33
- if state && state != 'stopped'
33
+ id, state = info.id, info.state
34
+ unless info.state.nil? || info.in_stopped_state?
34
35
  hsh[id] = state
35
36
  end
36
37
  hsh
@@ -41,21 +42,20 @@ class Bumbleworks::Worker < Ruote::Worker
41
42
  with_worker_state_enabled do
42
43
  Bumbleworks.dashboard.worker_state = new_state
43
44
  Bumbleworks::Support.wait_until(options) do
44
- worker_states.values.all? { |state| state == new_state }
45
+ active_worker_states.values.all? { |state| state == new_state }
45
46
  end
46
47
  end
47
48
  return true
48
49
  rescue Bumbleworks::Support::WaitTimeout
49
- raise WorkerStateNotChanged, "Worker states: #{worker_states.inspect}"
50
+ raise WorkerStateNotChanged, "Worker states: #{active_worker_states.inspect}"
50
51
  end
51
52
 
52
53
  def refresh_worker_info(options = {})
53
54
  with_worker_state_enabled do
54
- Bumbleworks::Support.wait_until(options) do
55
- info.all? { |id, worker_info|
56
- worker_info['state'] == 'stopped' ||
57
- Time.parse(worker_info['put_at']) > Time.now - 1
58
- }
55
+ info.each do |worker_info|
56
+ if !worker_info.in_stopped_state? && worker_info.stalling?
57
+ worker_info.record_new_state("stalled")
58
+ end
59
59
  end
60
60
  end
61
61
  end
@@ -65,6 +65,22 @@ class Bumbleworks::Worker < Ruote::Worker
65
65
  yield
66
66
  Bumbleworks.dashboard.context['worker_state_enabled'] = false
67
67
  end
68
+
69
+ def control_document
70
+ doc = Bumbleworks.dashboard.storage.get('variables', 'worker_control') || {}
71
+ doc['type'] = 'variables'
72
+ doc['_id'] = 'worker_control'
73
+ doc['workers'] ||= {}
74
+ doc
75
+ end
76
+
77
+ def info_document
78
+ doc = Bumbleworks.dashboard.storage.get('variables', 'workers') || {}
79
+ doc['type'] = 'variables'
80
+ doc['_id'] = 'workers'
81
+ doc['workers'] ||= {}
82
+ doc
83
+ end
68
84
  end
69
85
 
70
86
  def initialize(*args, &block)
@@ -83,6 +99,10 @@ class Bumbleworks::Worker < Ruote::Worker
83
99
  end
84
100
  end
85
101
 
102
+ def class_name
103
+ self.class.to_s
104
+ end
105
+
86
106
  def save_info
87
107
  @info.save if @info
88
108
  end
@@ -92,10 +112,23 @@ class Bumbleworks::Worker < Ruote::Worker
92
112
  save_info
93
113
  end
94
114
 
115
+ def worker_control_variable
116
+ self.class.control_document["workers"][id]
117
+ end
118
+
119
+ def desired_state
120
+ control_hash = worker_control_variable ||
121
+ @storage.get("variables", "worker") ||
122
+ { "state" => "running" }
123
+ control_hash["state"]
124
+ end
125
+
95
126
  def determine_state
96
- if @context['worker_state_enabled']
97
- super
98
- save_info
127
+ @state_mutex.synchronize do
128
+ if @state != "stopped" && @context["worker_state_enabled"]
129
+ @state = desired_state
130
+ save_info
131
+ end
99
132
  end
100
133
  end
101
134
 
@@ -3,18 +3,27 @@ require_relative "proxy"
3
3
  class Bumbleworks::Worker < Ruote::Worker
4
4
  class Info < Ruote::Worker::Info
5
5
  attr_reader :worker
6
+ extend Forwardable
7
+ extend Enumerable
8
+
9
+ def_delegators :worker,
10
+ :id, :pid, :name, :state, :ip, :hostname, :system, :launched_at
6
11
 
7
12
  class << self
8
13
  def raw_hash
9
14
  Bumbleworks.dashboard.worker_info || {}
10
15
  end
11
16
 
12
- def all
13
- raw_hash.map { |k, v|
14
- from_hash(v.merge('id' => k))
17
+ def each
18
+ raw_hash.each { |k, v|
19
+ yield from_hash(v)
15
20
  }
16
21
  end
17
22
 
23
+ def all
24
+ to_a
25
+ end
26
+
18
27
  def where(options)
19
28
  filter_proc = proc { |worker|
20
29
  options.all? { |k, v|
@@ -26,11 +35,11 @@ class Bumbleworks::Worker < Ruote::Worker
26
35
 
27
36
  def filter
28
37
  return [] unless block_given?
29
- all.select { |info| yield info.worker }
38
+ select { |info| yield info.worker }
30
39
  end
31
40
 
32
41
  def [](worker_id)
33
- from_hash(raw_hash[worker_id].merge('id' => worker_id))
42
+ from_hash(raw_hash[worker_id])
34
43
  end
35
44
 
36
45
  def from_hash(hash)
@@ -61,6 +70,13 @@ class Bumbleworks::Worker < Ruote::Worker
61
70
  end
62
71
  end
63
72
 
73
+ def initialize(worker)
74
+ @worker = worker
75
+ @last_save = Time.now - 2 * 60
76
+
77
+ @msgs = [] unless worker.is_a?(Bumbleworks::Worker::Proxy)
78
+ end
79
+
64
80
  def ==(other)
65
81
  other.is_a?(Bumbleworks::Worker::Info) &&
66
82
  other.worker == worker
@@ -70,6 +86,31 @@ class Bumbleworks::Worker < Ruote::Worker
70
86
  self.class.raw_hash[worker.id]
71
87
  end
72
88
 
89
+ def reload
90
+ @worker = Bumbleworks::Worker::Proxy.new(raw_hash)
91
+ end
92
+
93
+ def record_new_state(state)
94
+ worker.state = state
95
+ save
96
+ end
97
+
98
+ def worker_class_name
99
+ worker.class_name
100
+ end
101
+
102
+ def uptime
103
+ if in_stopped_state? && worker.respond_to?(:uptime)
104
+ worker.uptime
105
+ else
106
+ Time.now - worker.launched_at
107
+ end
108
+ end
109
+
110
+ def in_stopped_state?
111
+ worker.state.nil? || ["stopped", "stalled"].include?(worker.state)
112
+ end
113
+
73
114
  def updated_at
74
115
  Time.parse(raw_hash['put_at'])
75
116
  end
@@ -104,39 +145,76 @@ class Bumbleworks::Worker < Ruote::Worker
104
145
  @worker.storage || Bumbleworks.dashboard.storage
105
146
  end
106
147
 
107
- def initialize(worker)
108
- @worker = worker
109
- @last_save = Time.now - 2 * 60
148
+ def shutdown(options = {})
149
+ send_command("stopped", options)
150
+ end
110
151
 
111
- @msgs = [] unless worker.is_a?(Bumbleworks::Worker::Proxy)
152
+ def pause(options = {})
153
+ send_command("paused", options)
154
+ end
155
+
156
+ def unpause(options = {})
157
+ send_command("running", options)
112
158
  end
113
159
 
114
- def worker_info_document
115
- doc = storage.get('variables', 'workers') || {}
160
+ alias_method :run, :unpause
161
+
162
+ def send_command(command, options = {})
163
+ save_control_message(command)
164
+ Bumbleworks::Worker.with_worker_state_enabled do
165
+ Bumbleworks::Support.wait_until(options) do
166
+ raw_hash["state"] == command
167
+ end
168
+ end
169
+ reload
170
+ end
171
+
172
+ def save_control_message(message)
173
+ doc = Bumbleworks::Worker.control_document
174
+ doc["workers"][id] ||= {}
175
+ doc["workers"][id]["state"] = message
176
+ storage.put(doc)
177
+ end
178
+
179
+ def processed_last_minute
180
+ raw_hash["processed_last_minute"]
181
+ end
182
+
183
+ def wait_time_last_minute
184
+ raw_hash["wait_time_last_minute"]
185
+ end
186
+
187
+ def processed_last_hour
188
+ raw_hash["processed_last_hour"]
189
+ end
190
+
191
+ def wait_time_last_hour
192
+ raw_hash["wait_time_last_hour"]
193
+ end
116
194
 
117
- doc['type'] = 'variables'
118
- doc['_id'] = 'workers'
119
- doc['workers'] ||= {}
120
- doc
195
+ def constant_worker_info_hash
196
+ {
197
+ "id" => @worker.id,
198
+ "class" => @worker.class_name,
199
+ "name" => @worker.name,
200
+ "ip" => @worker.ip,
201
+ "hostname" => @worker.hostname,
202
+ "pid" => @worker.pid,
203
+ "system" => @worker.system,
204
+ "launched_at" => @worker.launched_at,
205
+ "state" => @worker.state
206
+ }
121
207
  end
122
208
 
123
209
  def save
124
- doc = worker_info_document
210
+ doc = Bumbleworks::Worker.info_document
125
211
 
126
212
  worker_info_hash = doc['workers'][@worker.id] || {}
127
213
 
214
+ worker_info_hash.merge!(constant_worker_info_hash)
128
215
  worker_info_hash.merge!({
129
- 'worker_id' => @worker.id,
130
- 'class' => @worker.class.to_s,
131
- 'name' => @worker.name,
132
- 'ip' => @worker.ip,
133
- 'hostname' => @worker.hostname,
134
- 'pid' => @worker.pid,
135
- 'system' => @worker.system,
136
216
  'put_at' => Ruote.now_to_utc_s,
137
- 'uptime' => Time.now - @worker.launched_at,
138
- 'launched_at' => @worker.launched_at,
139
- 'state' => @worker.state
217
+ 'uptime' => uptime,
140
218
  })
141
219
 
142
220
  if defined?(@msgs)
@@ -1,11 +1,12 @@
1
1
  class Bumbleworks::Worker < Ruote::Worker
2
2
  class Proxy
3
3
  ProxiedAttributes = [
4
- :id, :pid, :ip, :hostname, :system, :launched_at, :state, :name, :class
4
+ :id, :pid, :ip, :hostname, :system, :launched_at, :state, :name, :class, :uptime
5
5
  ]
6
6
 
7
7
  attr_reader *(ProxiedAttributes - [:launched_at])
8
8
  attr_reader :raw_hash
9
+ attr_writer :state
9
10
 
10
11
  def initialize(attributes)
11
12
  @raw_hash = attributes
@@ -14,8 +15,22 @@ class Bumbleworks::Worker < Ruote::Worker
14
15
  end
15
16
  end
16
17
 
18
+ # Allow storage to revert to the default for this Bumbleworks
19
+ # instance.
20
+ def storage
21
+ nil
22
+ end
23
+
24
+ def class_name
25
+ @class.to_s
26
+ end
27
+
17
28
  def launched_at
18
- Time.parse(@launched_at)
29
+ if @launched_at.is_a?(String)
30
+ Time.parse(@launched_at)
31
+ else
32
+ @launched_at
33
+ end
19
34
  end
20
35
 
21
36
  def ==(other)
@@ -2,19 +2,51 @@ describe Bumbleworks::Worker::Info do
2
2
  let(:context) { Bumbleworks.dashboard.context }
3
3
  let(:proxy) {
4
4
  Bumbleworks::Worker::Proxy.new(
5
- 'class' => :f_class,
6
- 'pid' => :f_pid,
7
- 'name' => :f_name,
8
- 'id' => :f_id,
9
- 'state' => :f_state,
10
- 'ip' => :f_ip,
11
- 'hostname' => :f_hostname,
12
- 'system' => :f_system,
13
- 'launched_at' => :f_launched_at
5
+ "class" => "f_class",
6
+ "pid" => "f_pid",
7
+ "name" => "f_name",
8
+ "id" => "f_id",
9
+ "state" => "f_state",
10
+ "ip" => "f_ip",
11
+ "hostname" => "f_hostname",
12
+ "system" => "f_system",
13
+ "uptime" => "f_uptime",
14
+ "launched_at" => "2010-10-10 10:10:10"
14
15
  )
15
16
  }
16
17
  subject { described_class.new(proxy) }
17
18
 
19
+
20
+ describe "delegation to hash" do
21
+ let(:raw_hash) {
22
+ {
23
+ "processed_last_minute" => 20,
24
+ "wait_time_last_minute" => 300.5,
25
+ "processed_last_hour" => 540,
26
+ "wait_time_last_hour" => 15.6
27
+ }
28
+ }
29
+ before(:each) do
30
+ allow(subject).to receive(:raw_hash).and_return(raw_hash)
31
+ end
32
+
33
+ it { is_expected.to fetch(:processed_last_minute).from(raw_hash) }
34
+ it { is_expected.to fetch(:wait_time_last_minute).from(raw_hash) }
35
+ it { is_expected.to fetch(:processed_last_hour).from(raw_hash) }
36
+ it { is_expected.to fetch(:wait_time_last_hour).from(raw_hash) }
37
+ end
38
+
39
+ describe "delegation to worker/proxy" do
40
+ it { is_expected.to delegate(:id).to(proxy) }
41
+ it { is_expected.to delegate(:pid).to(proxy) }
42
+ it { is_expected.to delegate(:name).to(proxy) }
43
+ it { is_expected.to delegate(:state).to(proxy) }
44
+ it { is_expected.to delegate(:ip).to(proxy) }
45
+ it { is_expected.to delegate(:hostname).to(proxy) }
46
+ it { is_expected.to delegate(:system).to(proxy) }
47
+ it { is_expected.to delegate(:launched_at).to(proxy) }
48
+ end
49
+
18
50
  describe '.raw_hash' do
19
51
  it 'returns Bumbleworks.dashboard.worker_info' do
20
52
  allow(Bumbleworks.dashboard).to receive(:worker_info).and_return(:bontron)
@@ -30,15 +62,16 @@ describe Bumbleworks::Worker::Info do
30
62
  describe '.from_hash' do
31
63
  it 'returns an info object using a proxy worker with given attributes' do
32
64
  hash = {
33
- 'class' => :f_class,
34
- 'pid' => :f_pid,
35
- 'name' => :f_name,
36
- 'id' => :f_id,
37
- 'state' => :f_state,
38
- 'ip' => :f_ip,
39
- 'hostname' => :f_hostname,
40
- 'system' => :f_system,
41
- 'launched_at' => :f_launched_at
65
+ "class" => "f_class",
66
+ "pid" => "f_pid",
67
+ "name" => "f_name",
68
+ "id" => "f_id",
69
+ "state" => "f_state",
70
+ "ip" => "f_ip",
71
+ "hostname" => "f_hostname",
72
+ "system" => "f_system",
73
+ "uptime" => "f_uptime",
74
+ "launched_at" => "2010-10-10 10:10:10"
42
75
  }
43
76
  allow(described_class).to receive(:new).
44
77
  with(proxy).
@@ -60,7 +93,7 @@ describe Bumbleworks::Worker::Info do
60
93
  describe '.[]' do
61
94
  it 'calls from_hash with raw data for given worker id' do
62
95
  allow(described_class).to receive(:from_hash).
63
- with({ 'foo' => 1, 'id' => 'f_id' }).and_return(:foo_info)
96
+ with({ 'foo' => 1 }).and_return(:foo_info)
64
97
  expect(described_class['f_id']).to eq(:foo_info)
65
98
  end
66
99
  end
@@ -68,9 +101,9 @@ describe Bumbleworks::Worker::Info do
68
101
  describe '.all' do
69
102
  it 'converts raw hash into info instances' do
70
103
  allow(described_class).to receive(:from_hash).
71
- with({ 'foo' => 1, 'id' => 'f_id'}).and_return(:foo_info)
104
+ with({ 'foo' => 1 }).and_return(:foo_info)
72
105
  allow(described_class).to receive(:from_hash).
73
- with({ 'bar' => 1, 'id' => 'b_id'}).and_return(:bar_info)
106
+ with({ 'bar' => 1 }).and_return(:bar_info)
74
107
  expect(described_class.all).to match_array([:foo_info, :bar_info])
75
108
  end
76
109
  end
@@ -128,6 +161,26 @@ describe Bumbleworks::Worker::Info do
128
161
  end
129
162
  end
130
163
 
164
+ describe "#worker_class_name" do
165
+ it "returns class_name from worker/proxy" do
166
+ allow(proxy).to receive(:class_name).and_return('barkle rutfut')
167
+ expect(subject.worker_class_name).to eq('barkle rutfut')
168
+ end
169
+ end
170
+
171
+ describe "#uptime" do
172
+ it "returns difference between now and updated_at" do
173
+ frozen_time = Time.now
174
+ allow(Time).to receive(:now).and_return(frozen_time)
175
+ expect(subject.uptime).to eq(frozen_time - subject.launched_at)
176
+ end
177
+
178
+ it "returns previous persisted uptime if stopped" do
179
+ allow(proxy).to receive(:state).and_return("stopped")
180
+ expect(subject.uptime).to eq(proxy.uptime)
181
+ end
182
+ end
183
+
131
184
  describe "#updated_at" do
132
185
  it "returns parsed put_at from raw hash for worker" do
133
186
  allow(subject).to receive(:raw_hash).and_return({
@@ -175,8 +228,8 @@ describe Bumbleworks::Worker::Info do
175
228
  describe "#raw_hash" do
176
229
  it "returns value from worker_info hash at key of worker id" do
177
230
  allow(described_class).to receive(:raw_hash).and_return({
178
- :f_id => :foo_hash,
179
- :b_id => :bar_hash
231
+ "f_id" => :foo_hash,
232
+ "b_id" => :bar_hash
180
233
  })
181
234
  expect(subject.raw_hash).to eq(:foo_hash)
182
235
  end
@@ -194,6 +247,28 @@ describe Bumbleworks::Worker::Info do
194
247
  end
195
248
  end
196
249
 
250
+ describe "#in_stopped_state?" do
251
+ it "returns true if state is stopped" do
252
+ allow(proxy).to receive(:state).and_return("stopped")
253
+ expect(subject).to be_in_stopped_state
254
+ end
255
+
256
+ it "returns true if state is stalled" do
257
+ allow(proxy).to receive(:state).and_return("stalled")
258
+ expect(subject).to be_in_stopped_state
259
+ end
260
+
261
+ it "returns true if state is nil" do
262
+ allow(proxy).to receive(:state).and_return(nil)
263
+ expect(subject).to be_in_stopped_state
264
+ end
265
+
266
+ it "returns false if state is running" do
267
+ allow(proxy).to receive(:state).and_return("running")
268
+ expect(subject).not_to be_in_stopped_state
269
+ end
270
+ end
271
+
197
272
  describe "#stalling?" do
198
273
  it "returns inverse of #responding?" do
199
274
  allow(subject).to receive(:responding?).and_return(true)
@@ -202,4 +277,96 @@ describe Bumbleworks::Worker::Info do
202
277
  expect(subject).to be_stalling
203
278
  end
204
279
  end
280
+
281
+ context "worker control commands" do
282
+ subject { Bumbleworks::Worker::Info.first }
283
+ let(:other_worker) { Bumbleworks::Worker::Info.all[1] }
284
+ before(:each) do
285
+ 2.times { Bumbleworks.start_worker! }
286
+ end
287
+
288
+ describe "#shutdown" do
289
+ it "shuts down just this worker" do
290
+ subject.shutdown
291
+ expect(subject.state).to eq("stopped")
292
+ expect(other_worker.state).to eq("running")
293
+ end
294
+ end
295
+
296
+ describe "#pause" do
297
+ it "pauses just this worker" do
298
+ subject.pause
299
+ expect(subject.state).to eq("paused")
300
+ expect(other_worker.state).to eq("running")
301
+ end
302
+ end
303
+
304
+ describe "#unpause" do
305
+ it "unpauses just this worker" do
306
+ [subject, other_worker].map(&:pause)
307
+ subject.unpause
308
+ expect(subject.state).to eq("running")
309
+ expect(other_worker.state).to eq("paused")
310
+ end
311
+ end
312
+
313
+ describe "#run" do
314
+ it "is an alias for unpause" do
315
+ expect(subject.method(:run)).to eq(subject.method(:unpause))
316
+ end
317
+ end
318
+ end
319
+
320
+ describe "#reload" do
321
+ it "generates a new proxy from the current raw hash" do
322
+ Bumbleworks.start_worker!
323
+ subject = Bumbleworks::Worker::Info.first
324
+ expect(Bumbleworks::Worker::Proxy).to receive(:new).
325
+ with(subject.raw_hash).and_return(:a_worker)
326
+ subject.reload
327
+ expect(subject.worker).to eq(:a_worker)
328
+ end
329
+ end
330
+
331
+ describe "#record_new_state" do
332
+ it "saves new state" do
333
+ expect(subject.worker).to receive(:state=).with("an awesome state").ordered
334
+ expect(subject).to receive(:save).ordered
335
+ subject.record_new_state("an awesome state")
336
+ end
337
+ end
338
+
339
+ describe "#save" do
340
+ before(:each) { Bumbleworks.start_worker! }
341
+ subject { Bumbleworks::Worker::Info.first }
342
+
343
+ it "persists all data unchanged to the engine storage" do
344
+ expected_hash = subject.constant_worker_info_hash
345
+ subject.save
346
+ expect(described_class[subject.id.to_s].constant_worker_info_hash).to eq(expected_hash)
347
+ end
348
+
349
+ it "updates uptime and updated_at even without changed data" do
350
+ uptime, updated_at = subject.uptime, subject.updated_at
351
+ subject.save
352
+ expect(described_class[subject.id.to_s].uptime).not_to eq(uptime)
353
+ expect(described_class[subject.id.to_s].updated_at).not_to eq(updated_at)
354
+ end
355
+
356
+ it "persists changed data" do
357
+ expected_hash = subject.constant_worker_info_hash
358
+ subject.worker.instance_variable_set(:@pid, "12345")
359
+ subject.save
360
+ expect(described_class[subject.id.to_s].id).to eq(expected_hash["id"])
361
+ expect(described_class[subject.id.to_s].pid).to eq("12345")
362
+ end
363
+
364
+ it "does not update uptime if worker is stopped" do
365
+ allow_any_instance_of(Bumbleworks::Worker::Proxy).to receive(:state).
366
+ and_return("stopped")
367
+ uptime = subject.uptime
368
+ subject.save
369
+ expect(described_class[subject.id.to_s].uptime).to eq(uptime)
370
+ end
371
+ end
205
372
  end
@@ -9,14 +9,39 @@ describe Bumbleworks::Worker::Proxy do
9
9
  'ip' => :f_ip,
10
10
  'hostname' => :f_hostname,
11
11
  'system' => :f_system,
12
- 'launched_at' => :f_launched_at
12
+ 'launched_at' => '2010-10-10 10:10:10'
13
13
  )
14
14
  }
15
15
 
16
16
  describe "#launched_at" do
17
+ let(:expected_time) { Time.parse('2010-10-10 10:10:10') }
18
+
17
19
  it "returns initialized launched_at string parsed as Time" do
18
- allow(Time).to receive(:parse).with(:f_launched_at).and_return(:a_time)
19
- expect(subject.launched_at).to eq(:a_time)
20
+ expect(subject.launched_at).to eq(expected_time)
21
+ end
22
+
23
+ it "returns launched_at directly if already Time" do
24
+ subject.instance_variable_set(:@launched_at, expected_time)
25
+ expect(subject.launched_at).to eq(expected_time)
26
+ end
27
+ end
28
+
29
+ describe "#class_name" do
30
+ it "returns class passed in from hash" do
31
+ expect(subject.class_name).to eq("f_class")
32
+ end
33
+ end
34
+
35
+ describe "#storage" do
36
+ it "returns nil to allow default" do
37
+ expect(subject.storage).to be_nil
38
+ end
39
+ end
40
+
41
+ describe "#state=" do
42
+ it "changes the proxy's recorded state" do
43
+ subject.state = "flummoxing"
44
+ expect(subject.state).to eq("flummoxing")
20
45
  end
21
46
  end
22
47
  end
@@ -17,42 +17,45 @@ describe Bumbleworks::Worker do
17
17
 
18
18
  context 'with multiple workers' do
19
19
  let!(:workers) {
20
- 2.times.map { |i|
20
+ 3.times.map { |i|
21
21
  worker = described_class.new(context)
22
22
  worker.run_in_thread
23
23
  worker
24
24
  }
25
25
  }
26
26
 
27
- describe '.worker_states' do
27
+ describe '.active_worker_states' do
28
28
  it 'returns the states of all active workers' do
29
29
  subject.run_in_thread
30
- expect(described_class.worker_states).to eq({
30
+ expect(described_class.active_worker_states).to eq({
31
31
  subject.id => 'running',
32
32
  workers[0].id => 'running',
33
- workers[1].id => 'running'
33
+ workers[1].id => 'running',
34
+ workers[2].id => 'running'
34
35
  })
35
36
  end
36
37
 
37
- it 'does not include stopped or nil states' do
38
+ it 'does not include stopped, stalled, or nil states' do
38
39
  subject.run_in_thread
39
40
  workers[0].shutdown
40
41
  workers[1].instance_variable_set(:@state, nil)
41
42
  workers[1].instance_variable_get(:@info).save
42
- expect(described_class.worker_states).to eq({
43
+ workers[2].instance_variable_set(:@state, "stalled")
44
+ workers[2].instance_variable_get(:@info).save
45
+ expect(described_class.active_worker_states).to eq({
43
46
  subject.id => 'running'
44
47
  })
45
48
  end
46
49
  end
47
50
 
48
51
  describe '.refresh_worker_info' do
49
- it 'times out if info is not updated in time' do
50
- allow(described_class).to receive(:info).and_return({
51
- :dummy_id => { 'put_at' => (Time.now - 60).to_s }
52
- })
53
- expect {
54
- described_class.refresh_worker_info(:timeout => 0.1)
55
- }.to raise_error(Bumbleworks::Support::WaitTimeout)
52
+ it 'times out and marks worker stalled if stalling' do
53
+ info1 = double(:in_stopped_state? => false, :stalling? => true)
54
+ info2 = double(:in_stopped_state? => false, :stalling? => false)
55
+ allow(described_class).to receive(:info).and_return([info1, info2])
56
+ expect(info1).to receive(:record_new_state).with("stalled")
57
+ expect(info2).to receive(:record_new_state).never
58
+ described_class.refresh_worker_info(:timeout => 0.1)
56
59
  end
57
60
 
58
61
  it 'refreshes worker info' do
@@ -61,11 +64,11 @@ describe Bumbleworks::Worker do
61
64
  doc['workers'][subject.id]['put_at'] = (Time.now - 30).to_s
62
65
  subject.storage.put(doc)
63
66
  described_class.refresh_worker_info
64
- expect(Time.parse(subject.info['put_at'])).to be_within(1).of(Time.now)
67
+ expect(subject.info.updated_at).to be_within(1).of(Time.now)
65
68
  end
66
69
 
67
70
  it 'returns without issue if info empty' do
68
- allow(described_class).to receive(:info).and_return({})
71
+ allow(described_class).to receive(:info).and_return([])
69
72
  expect {
70
73
  described_class.refresh_worker_info
71
74
  }.not_to raise_error
@@ -87,12 +90,14 @@ describe Bumbleworks::Worker do
87
90
  }.to raise_error(described_class::WorkerStateNotChanged)
88
91
  end
89
92
 
90
- it 'ignores already stopped workers' do
93
+ it 'ignores already stopped or stalled workers' do
91
94
  described_class.shutdown_all
95
+ workers[2].instance_variable_set(:@state, "stalled")
96
+ workers[2].instance_variable_get(:@info).save
92
97
  subject.run_in_thread
93
98
  described_class.change_worker_state('paused')
94
99
  expect(subject.state).to eq('paused')
95
- expect(workers.map(&:state)).to eq(['stopped', 'stopped'])
100
+ expect(workers.map(&:state)).to eq(['stopped', 'stopped', 'stalled'])
96
101
  end
97
102
 
98
103
  it 'does nothing if no worker info' do
@@ -110,6 +115,13 @@ describe Bumbleworks::Worker do
110
115
  expect(described_class).to receive(:change_worker_state).with('running', {}).ordered
111
116
  described_class.shutdown_all
112
117
  end
118
+
119
+ it 'resets to running even if error occurred when attempting stop' do
120
+ expect(described_class).to receive(:change_worker_state).with('stopped', {}).ordered.
121
+ and_raise('an interruption')
122
+ expect(described_class).to receive(:change_worker_state).with('running', {}).ordered
123
+ expect { described_class.shutdown_all }.to raise_error('an interruption')
124
+ end
113
125
  end
114
126
 
115
127
  describe '.pause_all' do
@@ -138,4 +150,10 @@ describe Bumbleworks::Worker do
138
150
  expect(subject.info).to eq(described_class.info[subject.id])
139
151
  end
140
152
  end
153
+
154
+ describe "#class_name" do
155
+ it "returns string version of class" do
156
+ expect(subject.class_name).to eq(subject.class.to_s)
157
+ end
158
+ end
141
159
  end
@@ -0,0 +1,9 @@
1
+ RSpec::Matchers.define :delegate do |method|
2
+ match do |actual|
3
+ actual.send(method) == @receiving_object.send(method)
4
+ end
5
+
6
+ chain :to do |receiving_object|
7
+ @receiving_object = receiving_object
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ RSpec::Matchers.define :fetch do |method|
2
+ match do |actual|
3
+ actual.send(method) == @receiving_hash.fetch(method.to_s)
4
+ end
5
+
6
+ chain :from do |receiving_hash|
7
+ @receiving_hash = receiving_hash
8
+ end
9
+ end
@@ -1,20 +1,3 @@
1
- shared_examples "an entity holder" do
2
- [:has_entity_fields?, :has_entity?, :entity, :entity_fields, :entity_name].each do |method|
3
- describe "##{method}" do
4
- it 'delegates to entity storage workitem' do
5
- allow(holder.entity_storage_workitem).to receive(method).with(1, 2, 3).and_return(:yay_for_bikes)
6
- expect(holder.send(method, 1, 2, 3)).to eq(:yay_for_bikes)
7
- end
8
- end
9
- end
10
-
11
- describe "#entity_storage_workitem" do
12
- it 'returns the expected workitem' do
13
- expect(holder.entity_storage_workitem).to eq(storage_workitem)
14
- end
15
- end
16
- end
17
-
18
1
  shared_examples "comparable" do
19
2
  describe "#hash" do
20
3
  it 'returns hash of identifier_for_comparison' do
@@ -0,0 +1,16 @@
1
+ shared_examples "an entity holder" do
2
+ [:has_entity_fields?, :has_entity?, :entity, :entity_fields, :entity_name].each do |method|
3
+ describe "##{method}" do
4
+ it 'delegates to entity storage workitem' do
5
+ allow(holder.entity_storage_workitem).to receive(method).with(1, 2, 3).and_return(:yay_for_bikes)
6
+ expect(holder.send(method, 1, 2, 3)).to eq(:yay_for_bikes)
7
+ end
8
+ end
9
+ end
10
+
11
+ describe "#entity_storage_workitem" do
12
+ it 'returns the expected workitem' do
13
+ expect(holder.entity_storage_workitem).to eq(storage_workitem)
14
+ end
15
+ end
16
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bumbleworks
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.90
4
+ version: 0.0.91
5
5
  platform: ruby
6
6
  authors:
7
7
  - Maher Hawash
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2015-03-11 00:00:00.000000000 Z
14
+ date: 2015-03-17 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: ruote
@@ -139,6 +139,7 @@ files:
139
139
  - ".gitignore"
140
140
  - ".rspec"
141
141
  - ".ruby-version"
142
+ - ".travis.yml"
142
143
  - ".watchr"
143
144
  - Gemfile
144
145
  - LICENSE.txt
@@ -245,9 +246,12 @@ files:
245
246
  - spec/lib/bumbleworks/workitem_spec.rb
246
247
  - spec/lib/bumbleworks_spec.rb
247
248
  - spec/spec_helper.rb
249
+ - spec/support/matchers/delegation_matcher.rb
250
+ - spec/support/matchers/fetch_matcher.rb
248
251
  - spec/support/path_helpers.rb
249
252
  - spec/support/process_testing_helpers.rb
250
- - spec/support/shared_examples.rb
253
+ - spec/support/shared_examples/comparable.rb
254
+ - spec/support/shared_examples/entity_holder.rb
251
255
  - spec/support/tracer.rb
252
256
  homepage: ''
253
257
  licenses:
@@ -331,7 +335,10 @@ test_files:
331
335
  - spec/lib/bumbleworks/workitem_spec.rb
332
336
  - spec/lib/bumbleworks_spec.rb
333
337
  - spec/spec_helper.rb
338
+ - spec/support/matchers/delegation_matcher.rb
339
+ - spec/support/matchers/fetch_matcher.rb
334
340
  - spec/support/path_helpers.rb
335
341
  - spec/support/process_testing_helpers.rb
336
- - spec/support/shared_examples.rb
342
+ - spec/support/shared_examples/comparable.rb
343
+ - spec/support/shared_examples/entity_holder.rb
337
344
  - spec/support/tracer.rb