bumbleworks 0.0.25 → 0.0.26

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.
@@ -1,5 +1,6 @@
1
1
  require "bumbleworks/tasks/base"
2
2
  require "bumbleworks/workitem_entity_storage"
3
+ require "bumbleworks/task/finder"
3
4
 
4
5
  module Bumbleworks
5
6
  class Task
@@ -8,6 +9,7 @@ module Bumbleworks
8
9
  class AlreadyClaimed < StandardError; end
9
10
  class MissingWorkitem < StandardError; end
10
11
  class NotCompletable < StandardError; end
12
+ class AvailabilityTimeout < StandardError; end
11
13
 
12
14
  extend Forwardable
13
15
  delegate [:sid, :fei, :fields, :params, :participant_name, :wfid, :wf_name] => :@workitem
@@ -29,24 +31,11 @@ module Bumbleworks
29
31
  end
30
32
  end
31
33
 
32
- def for_role(identifier)
33
- for_roles([identifier])
34
- end
35
-
36
- def for_roles(identifiers)
37
- return [] unless identifiers.is_a?(Array)
38
- workitems = identifiers.collect { |identifier|
39
- storage_participant.by_participant(identifier)
40
- }.flatten.uniq
41
- from_workitems(workitems)
42
- end
43
-
44
- def for_claimant(token)
45
- all.select { |t| t.claimant == token }
46
- end
47
-
48
- def all
49
- from_workitems(storage_participant.all)
34
+ def method_missing(method, *args)
35
+ if Finder.new.respond_to?(method)
36
+ return Finder.new.send(method, *args)
37
+ end
38
+ super
50
39
  end
51
40
 
52
41
  def find_by_id(sid)
@@ -60,12 +49,6 @@ module Bumbleworks
60
49
  def storage_participant
61
50
  Bumbleworks.dashboard.storage_participant
62
51
  end
63
-
64
- def from_workitems(workitems)
65
- workitems.map { |wi|
66
- new(wi) if wi.params['task']
67
- }.compact
68
- end
69
52
  end
70
53
 
71
54
  def initialize(workitem)
@@ -0,0 +1,75 @@
1
+ module Bumbleworks
2
+ class Task
3
+ class Finder
4
+ include Enumerable
5
+
6
+ def initialize(queries = [])
7
+ @queries = queries
8
+ end
9
+
10
+ def by_nickname(nickname)
11
+ @queries << proc { |wi| wi['fields']['params']['task'] == nickname }
12
+ self
13
+ end
14
+
15
+ def for_roles(identifiers)
16
+ identifiers ||= []
17
+ @queries << proc { |wi| identifiers.include?(wi['participant_name']) }
18
+ self
19
+ end
20
+
21
+ def for_role(identifier)
22
+ for_roles([identifier])
23
+ end
24
+
25
+ def for_claimant(token)
26
+ @queries << proc { |wi| wi['fields']['params']['claimant'] == token }
27
+ self
28
+ end
29
+
30
+ def for_entity(entity)
31
+ @queries << proc { |wi|
32
+ (wi['fields'][:entity_type] || wi['fields']['entity_type']) == entity.class.name &&
33
+ (wi['fields'][:entity_id] || wi['fields']['entity_id']) == entity.identifier
34
+ }
35
+ self
36
+ end
37
+
38
+ def all
39
+ workitems = Bumbleworks.dashboard.storage_participant.send(:do_select, {}) { |wi|
40
+ @queries.all? { |q| q.call(wi) }
41
+ }
42
+ from_workitems(workitems)
43
+ end
44
+
45
+ def each(&block)
46
+ all.each(&block)
47
+ end
48
+
49
+ def empty?
50
+ all.empty?
51
+ end
52
+
53
+ def next_available(options = {})
54
+ options[:timeout] ||= 5
55
+
56
+ start_time = Time.now
57
+ while first.nil?
58
+ if (Time.now - start_time) > options[:timeout]
59
+ raise Bumbleworks::Task::AvailabilityTimeout, "No tasks found matching criteria in time"
60
+ end
61
+ sleep 0.1
62
+ end
63
+ first
64
+ end
65
+
66
+ private
67
+
68
+ def from_workitems(workitems)
69
+ workitems.map { |wi|
70
+ Task.new(wi) if wi.params['task']
71
+ }.compact
72
+ end
73
+ end
74
+ end
75
+ end
@@ -1,3 +1,3 @@
1
1
  module Bumbleworks
2
- VERSION = "0.0.25"
2
+ VERSION = "0.0.26"
3
3
  end
@@ -5,8 +5,8 @@ module Bumbleworks
5
5
  def entity(options = {})
6
6
  @entity = nil if options[:reload] == true
7
7
  @entity ||= if has_entity_fields?
8
- klass = Bumbleworks::Support.constantize(workitem.fields['entity_type'])
9
- entity = klass.first_by_identifier(workitem.fields['entity_id'])
8
+ klass = Bumbleworks::Support.constantize(entity_type)
9
+ entity = klass.first_by_identifier(entity_id)
10
10
  end
11
11
  raise EntityNotFound unless @entity
12
12
  @entity
@@ -19,7 +19,17 @@ module Bumbleworks
19
19
  end
20
20
 
21
21
  def has_entity_fields?
22
- workitem.fields['entity_id'] && workitem.fields['entity_type']
22
+ entity_id && entity_type
23
+ end
24
+
25
+ private
26
+
27
+ def entity_id
28
+ workitem.fields[:entity_id] || workitem.fields['entity_id']
29
+ end
30
+
31
+ def entity_type
32
+ workitem.fields[:entity_type] || workitem.fields['entity_type']
23
33
  end
24
34
  end
25
35
  end
@@ -91,7 +91,7 @@ describe Bumbleworks::Task do
91
91
  it 'logs dispatch' do
92
92
  Bumbleworks.launch!('planting_a_noodle')
93
93
  Bumbleworks.dashboard.wait_for(:horse_feeder)
94
- task = described_class.for_role('horse_feeder').last
94
+ task = described_class.for_role('horse_feeder').first
95
95
  log_entry = Bumbleworks.logger.entries.last[:entry]
96
96
  log_entry[:action].should == :dispatch
97
97
  log_entry[:target_type].should == 'Task'
@@ -230,9 +230,22 @@ describe Bumbleworks::Task do
230
230
  end
231
231
 
232
232
  describe '.for_role' do
233
- it 'delegates to #for_roles with single-item array' do
234
- described_class.should_receive(:for_roles).with(['mister_mystery'])
235
- described_class.for_role('mister_mystery')
233
+ it 'returns all tasks for given role' do
234
+ Bumbleworks.define_process 'chalking' do
235
+ concurrence do
236
+ chalker :task => 'make_chalk_drawings'
237
+ hagrid :task => 'moan_endearingly'
238
+ chalker :task => 'chalk_it_good_baby'
239
+ end
240
+ end
241
+ Bumbleworks.launch!('chalking')
242
+ Bumbleworks.dashboard.wait_for(:chalker)
243
+
244
+ tasks = described_class.for_role('chalker')
245
+ tasks.map(&:nickname).should == [
246
+ 'make_chalk_drawings',
247
+ 'chalk_it_good_baby'
248
+ ]
236
249
  end
237
250
  end
238
251
 
@@ -325,6 +338,39 @@ describe Bumbleworks::Task do
325
338
  end
326
339
  end
327
340
 
341
+ context '.for_entity' do
342
+ it 'returns all tasks associated with given entity' do
343
+ fake_sandwich = OpenStruct.new(:identifier => 'rubies')
344
+ Bumbleworks.define_process 'existential_pb_and_j' do
345
+ concurrence do
346
+ sandwich :task => 'be_made'
347
+ sandwich :task => 'contemplate_being'
348
+ end
349
+ end
350
+ Bumbleworks.launch!('existential_pb_and_j', :entity => fake_sandwich)
351
+ Bumbleworks.dashboard.wait_for(:sandwich)
352
+ tasks = described_class.for_entity(fake_sandwich)
353
+ tasks.should have(2).items
354
+ end
355
+ end
356
+
357
+ context '.by_nickname' do
358
+ it 'returns all tasks with given nickname' do
359
+ Bumbleworks.define_process 'animal_disagreements' do
360
+ concurrence do
361
+ turtle :task => 'be_a_big_jerk'
362
+ goose :task => 'punch_turtle'
363
+ rabbit :task => 'punch_turtle'
364
+ end
365
+ end
366
+ Bumbleworks.launch!('animal_disagreements')
367
+ Bumbleworks.dashboard.wait_for(:rabbit)
368
+ tasks = described_class.by_nickname('punch_turtle')
369
+ tasks.should have(2).items
370
+ tasks.map(&:role).should =~ ['goose', 'rabbit']
371
+ end
372
+ end
373
+
328
374
  context 'claiming things' do
329
375
  before :each do
330
376
  Bumbleworks.define_process 'planting_a_noodle' do
@@ -337,7 +383,7 @@ describe Bumbleworks::Task do
337
383
  end
338
384
 
339
385
  describe '#claim' do
340
- it 'sets token on "claimant" param' do
386
+ it 'sets token on "claimant" param' do
341
387
  @task.params['claimant'].should == 'boss'
342
388
  end
343
389
 
@@ -515,4 +561,92 @@ describe Bumbleworks::Task do
515
561
  end
516
562
  end
517
563
  end
564
+
565
+ describe 'chained queries' do
566
+ it 'allows for AND-ed chained finders' do
567
+ Bumbleworks.define_process 'the_big_kachunko' do
568
+ concurrence do
569
+ red :task => 'be_really_mad'
570
+ blue :task => 'be_a_bit_sad'
571
+ yellow :task => 'be_scared'
572
+ green :task => 'be_envious'
573
+ green :task => 'be_proud'
574
+ pink :task => 'be_proud'
575
+ end
576
+ end
577
+ Bumbleworks.launch!('the_big_kachunko')
578
+ Bumbleworks.dashboard.wait_for(:pink)
579
+ described_class.by_nickname('be_really_mad').first.claim('crayon_box')
580
+ described_class.by_nickname('be_a_bit_sad').first.claim('crayon_box')
581
+ described_class.by_nickname('be_scared').first.claim('crayon_box')
582
+
583
+ tasks = described_class.
584
+ for_roles(['green', 'pink']).
585
+ by_nickname('be_proud')
586
+ tasks.should have(2).items
587
+ tasks.map(&:nickname).should =~ ['be_proud', 'be_proud']
588
+
589
+ tasks = described_class.
590
+ for_claimant('crayon_box').
591
+ for_roles(['red', 'yellow', 'green'])
592
+ tasks.should have(2).items
593
+ tasks.map(&:nickname).should =~ ['be_really_mad', 'be_scared']
594
+
595
+ tasks = described_class.
596
+ for_claimant('crayon_box').
597
+ by_nickname('be_a_bit_sad').
598
+ for_role('blue')
599
+ tasks.should have(1).item
600
+ tasks.first.nickname.should == 'be_a_bit_sad'
601
+ end
602
+ end
603
+
604
+ describe 'method missing' do
605
+ it 'calls method on new Finder object' do
606
+ described_class::Finder.any_instance.stub(:shabam!).with(:yay).and_return(:its_a_me)
607
+ described_class.shabam!(:yay).should == :its_a_me
608
+ end
609
+
610
+ it 'falls back to method missing if no finder method' do
611
+ expect {
612
+ described_class.kerplunk!(:oh_no)
613
+ }.to raise_error
614
+ end
615
+ end
616
+
617
+ describe '.next_available' do
618
+ it 'waits for one task to show up and returns it' do
619
+ Bumbleworks.define_process "lazy_bum_and_cool_guy" do
620
+ concurrence do
621
+ cool_guy :task => 'get_it_going_man'
622
+ sequence do
623
+ wait '2s'
624
+ bum :task => 'finally_get_a_round_tuit'
625
+ end
626
+ end
627
+ end
628
+ start_time = Time.now
629
+ Bumbleworks.launch!('lazy_bum_and_cool_guy')
630
+ task = described_class.for_role('bum').next_available
631
+ end_time = Time.now
632
+ task.nickname.should == 'finally_get_a_round_tuit'
633
+ (end_time - start_time).should >= 2
634
+ end
635
+
636
+ it 'times out if task does not appear in time' do
637
+ Bumbleworks.define_process "really_lazy_bum_and_cool_guy" do
638
+ concurrence do
639
+ cool_guy :task => 'good_golly_never_mind_you'
640
+ sequence do
641
+ wait '2s'
642
+ bum :task => 'whatever_these_socks_are_tasty'
643
+ end
644
+ end
645
+ end
646
+ Bumbleworks.launch!('really_lazy_bum_and_cool_guy')
647
+ expect {
648
+ described_class.for_role('bum').next_available(:timeout => 0.5)
649
+ }.to raise_error(Bumbleworks::Task::AvailabilityTimeout)
650
+ end
651
+ end
518
652
  end
@@ -18,6 +18,11 @@ describe Bumbleworks::WorkitemEntityStorage do
18
18
  feh.should have_entity_fields
19
19
  end
20
20
 
21
+ it 'returns true if workitem fields include symbolized version of entity fields' do
22
+ feh = FakeEntityHolder.new(:entity_id => '1', :entity_type => 'SomeEntity')
23
+ feh.should have_entity_fields
24
+ end
25
+
21
26
  it 'returns false if workitem fields do not include entity fields' do
22
27
  feh = FakeEntityHolder.new
23
28
  feh.should_not have_entity_fields
@@ -62,6 +67,11 @@ describe Bumbleworks::WorkitemEntityStorage do
62
67
  feh.entity.identifier.should == '15'
63
68
  end
64
69
 
70
+ it 'works with symbolized _id and _type fields' do
71
+ feh = FakeEntityHolder.new(:entity_id => '15', :entity_type => 'LovelyEntity')
72
+ feh.entity.identifier.should == '15'
73
+ end
74
+
65
75
  it 'throw exception if entity fields not present' do
66
76
  feh = FakeEntityHolder.new
67
77
  expect {
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.25
4
+ version: 0.0.26
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -178,6 +178,7 @@ files:
178
178
  - lib/bumbleworks/storage_participant.rb
179
179
  - lib/bumbleworks/support.rb
180
180
  - lib/bumbleworks/task.rb
181
+ - lib/bumbleworks/task/finder.rb
181
182
  - lib/bumbleworks/tasks/base.rb
182
183
  - lib/bumbleworks/tree_builder.rb
183
184
  - lib/bumbleworks/version.rb
@@ -227,18 +228,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
227
228
  - - ! '>='
228
229
  - !ruby/object:Gem::Version
229
230
  version: '0'
230
- segments:
231
- - 0
232
- hash: -2155630126504321546
233
231
  required_rubygems_version: !ruby/object:Gem::Requirement
234
232
  none: false
235
233
  requirements:
236
234
  - - ! '>='
237
235
  - !ruby/object:Gem::Version
238
236
  version: '0'
239
- segments:
240
- - 0
241
- hash: -2155630126504321546
242
237
  requirements: []
243
238
  rubyforge_project:
244
239
  rubygems_version: 1.8.23
@@ -277,3 +272,4 @@ test_files:
277
272
  - spec/lib/bumbleworks_spec.rb
278
273
  - spec/spec_helper.rb
279
274
  - spec/support/path_helpers.rb
275
+ has_rdoc: