bumbleworks 0.0.51 → 0.0.52

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.
@@ -11,6 +11,7 @@ require "bumbleworks/hash_storage"
11
11
  require "bumbleworks/error_handler"
12
12
  require "bumbleworks/entity"
13
13
  require "bumbleworks/participant"
14
+ require "bumbleworks/workitem"
14
15
 
15
16
  # default implementations
16
17
  require "bumbleworks/simple_logger"
@@ -21,6 +22,7 @@ module Bumbleworks
21
22
  class UndefinedSetting < StandardError; end
22
23
  class InvalidSetting < StandardError; end
23
24
  class InvalidEntity < StandardError; end
25
+ class EntityNotFound < StandardError; end
24
26
 
25
27
  class << self
26
28
  extend Forwardable
@@ -5,19 +5,19 @@ module Bumbleworks
5
5
  end
6
6
 
7
7
  def launch_process(process_name, options = {})
8
- identifier_column = self.class.processes[process_name.to_sym][:column]
9
- if (options[:force] == true || (process_identifier = self.send(identifier_column)).nil?)
10
- workitem_fields = process_fields(process_name.to_sym)
11
- variables = process_variables(process_name.to_sym)
8
+ identifier_attribute = attribute_for_process_name(process_name.to_sym)
9
+ if (options[:force] == true || (process_identifier = self.send(identifier_attribute)).nil?)
10
+ workitem_fields = process_fields(process_name.to_sym).merge(options[:fields] || {})
11
+ variables = process_variables(process_name.to_sym).merge(options[:variables] || {})
12
12
  process_identifier = Bumbleworks.launch!(process_name.to_s, workitem_fields, variables).wfid
13
- persist_process_identifier(identifier_column.to_sym, process_identifier)
13
+ persist_process_identifier(identifier_attribute.to_sym, process_identifier)
14
14
  end
15
15
  Bumbleworks::Process.new(process_identifier)
16
16
  end
17
17
 
18
- def persist_process_identifier(identifier_column, process_identifier)
18
+ def persist_process_identifier(identifier_attribute, process_identifier)
19
19
  if self.respond_to?(:update)
20
- update(identifier_column => process_identifier)
20
+ update(identifier_attribute => process_identifier)
21
21
  else
22
22
  raise "Entity must define #persist_process_identifier method if missing #update method."
23
23
  end
@@ -27,7 +27,7 @@ module Bumbleworks
27
27
  return {} unless self.class.processes
28
28
  process_names = self.class.processes.keys
29
29
  process_names.inject({}) do |memo, name|
30
- pid = self.send(self.class.processes[name][:column])
30
+ pid = self.send(attribute_for_process_name(name))
31
31
  memo[name] = pid ? Bumbleworks::Process.new(pid) : nil
32
32
  memo
33
33
  end
@@ -38,11 +38,18 @@ module Bumbleworks
38
38
  end
39
39
 
40
40
  def cancel_all_processes!
41
- processes.each do |process|
41
+ processes_by_name.each do |name, process|
42
+ next unless process
43
+ identifier_attribute = attribute_for_process_name(name)
42
44
  process.cancel!
45
+ persist_process_identifier(identifier_attribute, nil)
43
46
  end
44
47
  end
45
48
 
49
+ def attribute_for_process_name(name)
50
+ self.class.processes[name][:attribute]
51
+ end
52
+
46
53
  def tasks(nickname = nil)
47
54
  finder = Bumbleworks::Task.for_entity(self)
48
55
  finder = finder.by_nickname(nickname) if nickname
@@ -69,7 +76,7 @@ module Bumbleworks
69
76
  attr_reader :processes
70
77
 
71
78
  def process(process_name, options = {})
72
- options[:column] ||= process_identifier_column(process_name)
79
+ options[:attribute] ||= default_process_identifier_attribute(process_name)
73
80
  (@processes ||= {})[process_name.to_sym] = options
74
81
  end
75
82
 
@@ -77,11 +84,11 @@ module Bumbleworks
77
84
  Bumbleworks::Support.tokenize(name)
78
85
  end
79
86
 
80
- def process_identifier_column(process_name)
81
- identifier_column = "#{process_name}_process_identifier"
82
- identifier_column.gsub!(/^#{entity_type}_/, '')
83
- identifier_column.gsub!(/process_process/, 'process')
84
- identifier_column.to_sym
87
+ def default_process_identifier_attribute(process_name)
88
+ identifier_attribute = "#{process_name}_process_identifier"
89
+ identifier_attribute.gsub!(/^#{entity_type}_/, '')
90
+ identifier_attribute.gsub!(/process_process/, 'process')
91
+ identifier_attribute.to_sym
85
92
  end
86
93
  end
87
94
  end
@@ -1,5 +1,7 @@
1
1
  module Bumbleworks
2
2
  class Process
3
+ class EntityConflict < StandardError; end
4
+
3
5
  attr_reader :id
4
6
 
5
7
  def initialize(wfid)
@@ -12,6 +14,16 @@ module Bumbleworks
12
14
  wfid == other.wfid
13
15
  end
14
16
 
17
+ def entity
18
+ return nil unless process_status
19
+ workitems = leaves.map(&:applied_workitem).map { |wi| Bumbleworks::Workitem.new(wi) }
20
+ if workitems.map(&:entity_fields).uniq.length == 1
21
+ workitems.first.entity
22
+ else
23
+ raise EntityConflict
24
+ end
25
+ end
26
+
15
27
  def tasks
16
28
  Bumbleworks::Task.for_process(wfid)
17
29
  end
@@ -11,6 +11,32 @@ module Bumbleworks
11
11
  @wfids = nil
12
12
  end
13
13
 
14
+ def where(filters)
15
+ key_method_map = {
16
+ :available => :available,
17
+ :nickname => :by_nickname,
18
+ :roles => :for_roles,
19
+ :role => :for_role,
20
+ :unclaimed => :unclaimed,
21
+ :claimed => :claimed,
22
+ :fields => :with_fields,
23
+ :claimant => :for_claimant,
24
+ :entity => :for_entity,
25
+ :processes => :for_processes,
26
+ :process => :for_process,
27
+ :completable => :completable
28
+ }
29
+ fields = filters.select { |k,v| !key_method_map.keys.include? k }
30
+ methods = filters.select { |k,v| key_method_map.keys.include? k }
31
+ query = methods.inject(self) { |query, (method, args)|
32
+ query.send(key_method_map[method], args)
33
+ }
34
+ unless fields.empty?
35
+ query.with_fields(fields)
36
+ end
37
+ query
38
+ end
39
+
14
40
  def available
15
41
  unclaimed.completable
16
42
  end
@@ -39,6 +65,11 @@ module Bumbleworks
39
65
  unclaimed(false)
40
66
  end
41
67
 
68
+ def with_fields(field_hash)
69
+ @queries << proc { |wi| field_hash.all? { |k, v| wi['fields'][k.to_s] == v } }
70
+ self
71
+ end
72
+
42
73
  def for_claimant(token)
43
74
  @queries << proc { |wi| wi['fields']['params']['claimant'] == token }
44
75
  self
@@ -75,8 +106,8 @@ module Bumbleworks
75
106
  from_workitems(workitems)
76
107
  end
77
108
 
78
- def completable
79
- @task_filters << proc { |task| task.completable? }
109
+ def completable(true_or_false = true)
110
+ @task_filters << proc { |task| task.completable? == true_or_false }
80
111
  self
81
112
  end
82
113
 
@@ -1,3 +1,3 @@
1
1
  module Bumbleworks
2
- VERSION = "0.0.51"
2
+ VERSION = "0.0.52"
3
3
  end
@@ -0,0 +1,52 @@
1
+ module Bumbleworks
2
+ class Workitem
3
+ def initialize(raw_workitem)
4
+ @raw_workitem = raw_workitem
5
+ end
6
+
7
+ def entity(options = {})
8
+ @entity = nil if options[:reload] == true
9
+ @entity ||= if has_entity_fields?
10
+ klass = Bumbleworks::Support.constantize(entity_type)
11
+ entity = klass.first_by_identifier(entity_id)
12
+ end
13
+ raise EntityNotFound, {:entity_id => entity_id, :entity_type => entity_type} unless @entity
14
+ @entity
15
+ end
16
+
17
+ def has_entity?
18
+ !entity.nil?
19
+ rescue EntityNotFound
20
+ false
21
+ end
22
+
23
+ def has_entity_fields?
24
+ entity_id && entity_type
25
+ end
26
+
27
+ def entity_fields(options = {})
28
+ return {} unless has_entity_fields?
29
+ type = if options[:humanize] == true
30
+ Bumbleworks::Support.humanize(entity_type)
31
+ elsif options[:titleize] == true
32
+ Bumbleworks::Support.titleize(entity_type)
33
+ else
34
+ entity_type
35
+ end
36
+ {
37
+ :type => type,
38
+ :identifier => entity_id
39
+ }
40
+ end
41
+
42
+ private
43
+
44
+ def entity_id
45
+ @raw_workitem.fields[:entity_id] || @raw_workitem.fields['entity_id']
46
+ end
47
+
48
+ def entity_type
49
+ @raw_workitem.fields[:entity_type] || @raw_workitem.fields['entity_type']
50
+ end
51
+ end
52
+ end
@@ -1,50 +1,11 @@
1
1
  module Bumbleworks
2
2
  module WorkitemEntityStorage
3
- class EntityNotFound < StandardError; end
3
+ extend Forwardable
4
4
 
5
- def entity(options = {})
6
- @entity = nil if options[:reload] == true
7
- @entity ||= if has_entity_fields?
8
- klass = Bumbleworks::Support.constantize(entity_type)
9
- entity = klass.first_by_identifier(entity_id)
10
- end
11
- raise EntityNotFound, {:entity_id => entity_id, :entity_type => entity_type} unless @entity
12
- @entity
13
- end
14
-
15
- def has_entity?
16
- !entity.nil?
17
- rescue EntityNotFound
18
- false
19
- end
20
-
21
- def has_entity_fields?
22
- entity_id && entity_type
23
- end
24
-
25
- def entity_fields(options = {})
26
- return {} unless has_entity_fields?
27
- type = if options[:humanize] == true
28
- Bumbleworks::Support.humanize(entity_type)
29
- elsif options[:titleize] == true
30
- Bumbleworks::Support.titleize(entity_type)
31
- else
32
- entity_type
33
- end
34
- {
35
- :type => type,
36
- :identifier => entity_id
37
- }
38
- end
39
-
40
- private
41
-
42
- def entity_id
43
- workitem.fields[:entity_id] || workitem.fields['entity_id']
44
- end
5
+ delegate [:entity, :has_entity?, :has_entity_fields?, :entity_fields] => :entity_storage_workitem
45
6
 
46
- def entity_type
47
- workitem.fields[:entity_type] || workitem.fields['entity_type']
7
+ def entity_storage_workitem
8
+ @entity_storage_workitem ||= Bumbleworks::Workitem.new(workitem)
48
9
  end
49
10
  end
50
11
  end
@@ -1,10 +1,12 @@
1
- class Furby
1
+ class RainbowLoom
2
2
  include Bumbleworks::Entity
3
3
 
4
4
  process :make_honey
5
+ process :make_molasses, :attribute => :molasses_pid
5
6
 
6
- attr_reader :identifier
7
+ attr_accessor :identifier
7
8
  attr_accessor :make_honey_process_identifier
9
+ attr_accessor :molasses_pid
8
10
 
9
11
  def initialize(identifier)
10
12
  @identifier = identifier
@@ -24,6 +26,7 @@ class Furby
24
26
 
25
27
  class << self
26
28
  def first_by_identifier(identifier)
29
+ return nil unless identifier
27
30
  new(identifier)
28
31
  end
29
32
  end
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.join(fixtures_path, 'entities', 'furby'))
1
+ require File.expand_path(File.join(fixtures_path, 'entities', 'rainbow_loom'))
2
2
 
3
3
  describe 'Entity Module' do
4
4
  let(:app_root) {
@@ -10,27 +10,29 @@ describe 'Entity Module' do
10
10
  load File.join(app_root, 'full_initializer.rb')
11
11
  end
12
12
 
13
- describe 'launching a process' do
14
- it 'assigns entity to process and subsequent tasks' do
15
- furby = Furby.new('12345')
16
- furby.launch_process(:make_honey)
13
+ describe 'process control' do
14
+ it 'launching assigns entity to process and subsequent tasks' do
15
+ rainbow_loom = RainbowLoom.new('12345')
16
+ process = rainbow_loom.launch_process(:make_honey)
17
17
  Bumbleworks.dashboard.wait_for(:dave)
18
18
  task = Bumbleworks::Task.for_role('dave').first
19
- task.entity.should == furby
19
+ task.entity.should == rainbow_loom
20
+ process.entity.should == rainbow_loom
20
21
  end
21
22
 
22
- it 'links processes with identifiers' do
23
- furby = Furby.new('12345')
24
- process = furby.launch_process(:make_honey)
25
- furby.processes_by_name.should == {
26
- :make_honey => process
23
+ it 'launching links processes with identifiers' do
24
+ rainbow_loom = RainbowLoom.new('12345')
25
+ process = rainbow_loom.launch_process(:make_honey)
26
+ rainbow_loom.processes_by_name.should == {
27
+ :make_honey => process,
28
+ :make_molasses => nil
27
29
  }
28
30
  end
29
31
 
30
32
  it 'persists process identifier' do
31
- furby = Furby.new('12345')
32
- process = furby.launch_process(:make_honey)
33
- furby.make_honey_process_identifier.should == process.wfid
33
+ rainbow_loom = RainbowLoom.new('12345')
34
+ process = rainbow_loom.launch_process(:make_honey)
35
+ rainbow_loom.make_honey_process_identifier.should == process.wfid
34
36
  end
35
37
  end
36
38
 
@@ -40,8 +42,8 @@ describe 'Entity Module' do
40
42
  tell_entity :to => 'cook_it_up', :with => [2, 'orange'], :and_save_as => 'yum'
41
43
  critic :task => 'checkit_that_foods'
42
44
  end
43
- furby = Furby.new('12345')
44
- process = Bumbleworks.launch!('here_we_go', :entity => furby)
45
+ rainbow_loom = RainbowLoom.new('12345')
46
+ process = Bumbleworks.launch!('here_we_go', :entity => rainbow_loom)
45
47
  Bumbleworks.dashboard.wait_for(:critic)
46
48
  process.tasks.first['yum'].should == "2 and orange"
47
49
  end
@@ -5,7 +5,7 @@ describe Bumbleworks::Entity do
5
5
  it 'returns hash of process names and process instances' do
6
6
  [:zoom, :foof, :nook].each do |pname|
7
7
  entity_class.send(:attr_accessor, :"#{pname}_pid")
8
- entity_class.process pname, :column => :"#{pname}_pid"
8
+ entity_class.process pname, :attribute => :"#{pname}_pid"
9
9
  end
10
10
 
11
11
  entity = entity_class.new
@@ -27,7 +27,7 @@ describe Bumbleworks::Entity do
27
27
  it 'returns array of process instances for all running processes' do
28
28
  [:zoom, :foof, :nook].each do |pname|
29
29
  entity_class.send(:attr_accessor, :"#{pname}_pid")
30
- entity_class.process pname, :column => :"#{pname}_pid"
30
+ entity_class.process pname, :attribute => :"#{pname}_pid"
31
31
  end
32
32
  entity = entity_class.new
33
33
  entity.foof_pid = '1234'
@@ -45,12 +45,21 @@ describe Bumbleworks::Entity do
45
45
 
46
46
  describe '#cancel_all_processes!' do
47
47
  it 'cancels all processes with registered identifiers' do
48
+ [:zoom, :foof, :nook].each do |pname|
49
+ entity_class.send(:attr_accessor, :"#{pname}_pid")
50
+ entity_class.process pname, :attribute => :"#{pname}_pid"
51
+ end
48
52
  entity = entity_class.new
49
- bp1 = Bumbleworks::Process.new('1234')
50
- bp2 = Bumbleworks::Process.new('pickles')
51
- entity.stub(:processes).and_return([bp1, bp2])
53
+ entity.foof_pid = '1234'
54
+ entity.nook_pid = 'pickles'
55
+ entity.stub(:processes_by_name => {
56
+ :foof => bp1 = Bumbleworks::Process.new('1234'),
57
+ :nook => bp2 = Bumbleworks::Process.new('pickles')
58
+ })
52
59
  bp1.should_receive(:cancel!)
53
60
  bp2.should_receive(:cancel!)
61
+ entity.should_receive(:update).with(:foof_pid => nil)
62
+ entity.should_receive(:update).with(:nook_pid => nil)
54
63
  entity.cancel_all_processes!
55
64
  end
56
65
  end
@@ -80,8 +89,8 @@ describe Bumbleworks::Entity do
80
89
  describe '#persist_process_identifier' do
81
90
  it 'calls #update if method exists' do
82
91
  entity = entity_class.new
83
- entity.should_receive(:update).with(:a_column => :a_value)
84
- entity.persist_process_identifier(:a_column, :a_value)
92
+ entity.should_receive(:update).with(:a_attribute => :a_value)
93
+ entity.persist_process_identifier(:a_attribute, :a_value)
85
94
  end
86
95
 
87
96
  it 'raises exception if #update method does not exist' do
@@ -89,7 +98,7 @@ describe Bumbleworks::Entity do
89
98
  entity.stub(:respond_to?).with(:update).and_return(false)
90
99
  entity.should_receive(:update).never
91
100
  expect {
92
- entity.persist_process_identifier(:a_column, :a_value)
101
+ entity.persist_process_identifier(:a_attribute, :a_value)
93
102
  }.to raise_error("Entity must define #persist_process_identifier method if missing #update method.")
94
103
  end
95
104
  end
@@ -97,34 +106,72 @@ describe Bumbleworks::Entity do
97
106
  describe '#launch_process' do
98
107
  it 'launches process and return process if identifier not set' do
99
108
  bp = Bumbleworks::Process.new('12345')
100
- entity_class.process :noodles, :column => :noodles_pid
109
+ entity_class.process :noodles, :attribute => :noodles_pid
101
110
  entity = entity_class.new
102
111
  entity.stub(:noodles_pid)
103
- entity.stub(:process_fields).with(:noodles).and_return('the_fields')
104
- entity.stub(:process_variables).with(:noodles).and_return('the_variables')
105
- Bumbleworks.stub(:launch!).with('noodles', 'the_fields', 'the_variables').and_return(bp)
112
+ entity.stub(:process_fields).with(:noodles).and_return({:f => 1})
113
+ entity.stub(:process_variables).with(:noodles).and_return({:v => 2})
114
+ Bumbleworks.stub(:launch!).with('noodles', {:f => 1}, {:v => 2}).and_return(bp)
106
115
  entity.should_receive(:persist_process_identifier).with(:noodles_pid, '12345')
107
116
  entity.launch_process('noodles').should == bp
108
117
  end
109
118
 
110
- it 'does nothing but returns existing process if identifier column already set' do
119
+ it 'does nothing but returns existing process if identifier attribute already set' do
111
120
  bp = Bumbleworks::Process.new('already set')
112
- entity_class.process :noodles, :column => :noodles_pid
121
+ entity_class.process :noodles, :attribute => :noodles_pid
113
122
  entity = entity_class.new
114
123
  entity.stub(:noodles_pid => 'already set')
115
124
  Bumbleworks.should_receive(:launch!).never
116
125
  entity.launch_process('noodles').should == bp
117
126
  end
118
127
 
119
- it 'launches new process anyway if identifier column already set but force is true' do
128
+ it 'launches new process anyway if identifier attribute already set but force is true' do
120
129
  bp = Bumbleworks::Process.new('12345')
121
- entity_class.process :noodles, :column => :noodles_pid
130
+ entity_class.process :noodles, :attribute => :noodles_pid
122
131
  entity = entity_class.new
123
132
  entity.stub(:noodles_pid => 'already set')
124
133
  Bumbleworks.stub(:launch! => bp)
125
134
  entity.should_receive(:persist_process_identifier).with(:noodles_pid, '12345')
126
135
  entity.launch_process('noodles', :force => true).should == bp
127
136
  end
137
+
138
+ it 'sends additional fields and variables to launch' do
139
+ entity_class.process :noodles, :attribute => :noodles_pid
140
+ entity = entity_class.new
141
+ entity.stub(:noodles_pid)
142
+ entity.stub(:persist_process_identifier)
143
+ Bumbleworks.should_receive(:launch!).with(
144
+ 'noodles',
145
+ { :entity => entity, :drink => 'apathy smoothie', :berry => 'black' },
146
+ { :so_you_said => :well_so_did_i }
147
+ ).and_return(Bumbleworks::Process.new(1))
148
+ entity.launch_process(:noodles,
149
+ :fields => { :drink => 'apathy smoothie', :berry => 'black' },
150
+ :variables => { :so_you_said => :well_so_did_i }
151
+ )
152
+ end
153
+ end
154
+
155
+ describe '#attribute_for_process_name' do
156
+ it 'returns attribute set for given process' do
157
+ entity_class.stub(:processes).and_return({
158
+ :goose => { :attribute => :goose_pid },
159
+ :the_punisher => { :attribute => :your_skin }
160
+ })
161
+ entity = entity_class.new
162
+ entity.attribute_for_process_name(:goose).should == :goose_pid
163
+ entity.attribute_for_process_name(:the_punisher).should == :your_skin
164
+ end
165
+
166
+ it 'raises exception if no process found for given name' do
167
+ entity_class.stub(:processes).and_return({
168
+ :goose => { :attribute => :goose_pid },
169
+ })
170
+ entity = entity_class.new
171
+ expect {
172
+ entity.attribute_for_process_name(:the_punisher)
173
+ }.to raise_error
174
+ end
128
175
  end
129
176
 
130
177
  describe '#subscribed_events' do
@@ -174,28 +221,28 @@ describe Bumbleworks::Entity do
174
221
 
175
222
  describe '.process' do
176
223
  it 'registers a new process' do
177
- entity_class.stub(:process_identifier_column).with(:whatever).and_return('loob')
224
+ entity_class.stub(:default_process_identifier_attribute).with(:whatever).and_return('loob')
178
225
  entity_class.process :whatever
179
226
  entity_class.processes.should == {
180
227
  :whatever => {
181
- :column => 'loob'
228
+ :attribute => 'loob'
182
229
  }
183
230
  }
184
231
  end
185
232
  end
186
233
 
187
- describe '.process_identifier_column' do
234
+ describe '.default_process_identifier_attribute' do
188
235
  it 'adds _process_identifier to end of given process name' do
189
- entity_class.process_identifier_column('zoof').should == :zoof_process_identifier
236
+ entity_class.default_process_identifier_attribute('zoof').should == :zoof_process_identifier
190
237
  end
191
238
 
192
239
  it 'ensures no duplication of _process' do
193
- entity_class.process_identifier_column('zoof_process').should == :zoof_process_identifier
240
+ entity_class.default_process_identifier_attribute('zoof_process').should == :zoof_process_identifier
194
241
  end
195
242
 
196
243
  it 'removes entity_type from beginning of identifier' do
197
244
  entity_class.stub(:entity_type).and_return('zoof')
198
- entity_class.process_identifier_column('zoof_process').should == :process_identifier
245
+ entity_class.default_process_identifier_attribute('zoof_process').should == :process_identifier
199
246
  end
200
247
  end
201
248
 
@@ -1,3 +1,5 @@
1
+ require File.expand_path(File.join(fixtures_path, 'entities', 'rainbow_loom'))
2
+
1
3
  describe Bumbleworks::Process do
2
4
  before :each do
3
5
  Bumbleworks.reset!
@@ -119,6 +121,45 @@ describe Bumbleworks::Process do
119
121
  end
120
122
  end
121
123
 
124
+ describe '#entity' do
125
+ it 'returns nil if process not ready yet' do
126
+ bp = described_class.new('nothing')
127
+ bp.entity.should be_nil
128
+ end
129
+
130
+ it 'returns entity provided at launch' do
131
+ rainbow_loom = RainbowLoom.new('1234')
132
+ bp = Bumbleworks.launch!('going_to_the_dance', :entity => rainbow_loom)
133
+ wait_until { bp.trackers.count > 0 }
134
+ bp.entity.should == rainbow_loom
135
+ end
136
+
137
+ it 'raises exception if multiple workitems have conflicting entity info' do
138
+ Bumbleworks.define_process 'conflict_this' do
139
+ concurrence do
140
+ sequence do
141
+ set 'entity_id' => 'swoo'
142
+ just_wait
143
+ end
144
+ sequence do
145
+ set 'entity_id' => 'fwee'
146
+ just_wait
147
+ end
148
+ end
149
+ end
150
+ bp = Bumbleworks.launch!('conflict_this', :entity => RainbowLoom.new('1234'))
151
+ Bumbleworks.dashboard.wait_for(:just_wait)
152
+ expect {
153
+ bp.entity
154
+ }.to raise_error(Bumbleworks::Process::EntityConflict)
155
+ end
156
+
157
+ it 'returns nil if no entity' do
158
+ bp = Bumbleworks.launch!('going_to_the_dance')
159
+ bp.entity.should be_nil
160
+ end
161
+ end
162
+
122
163
  describe '#process_status' do
123
164
  it 'returns a process_status instance for the wfid' do
124
165
  bp = described_class.new('frogheads')
@@ -40,4 +40,31 @@ describe Bumbleworks::Task::Finder do
40
40
  query.available
41
41
  end
42
42
  end
43
+
44
+ describe '#where' do
45
+ it 'compiles a finder' do
46
+ query = Bumbleworks::Task::Finder.new
47
+ query.should_receive(:available).and_return(query)
48
+ query.should_receive(:by_nickname).with(:nicholas).and_return(query)
49
+ query.should_receive(:for_roles).with([:dinner, :barca]).and_return(query)
50
+ query.should_receive(:unclaimed).and_return(query)
51
+ query.should_receive(:for_claimant).with(:dr_clam).and_return(query)
52
+ query.should_receive(:for_entity).with(:a_luffly_pirate).and_return(query)
53
+ query.should_receive(:for_processes).with([:jasmine, :mulan]).and_return(query)
54
+ query.should_receive(:completable).with(true).and_return(query)
55
+ query.should_receive(:with_fields).with({ :horse => :giant_pony, :pie => :silly_cake }).and_return(query)
56
+ query.where({
57
+ :available => true,
58
+ :nickname => :nicholas,
59
+ :roles => [:dinner, :barca],
60
+ :unclaimed => true,
61
+ :claimant => :dr_clam,
62
+ :entity => :a_luffly_pirate,
63
+ :processes => [:jasmine, :mulan],
64
+ :completable => true,
65
+ :horse => :giant_pony,
66
+ :pie => :silly_cake
67
+ })
68
+ end
69
+ end
43
70
  end
@@ -371,7 +371,7 @@ describe Bumbleworks::Task do
371
371
  end
372
372
 
373
373
  describe '.completable' do
374
- it 'returns only completable tasks' do
374
+ it 'filters by completability' do
375
375
  module WuggleHandsTask
376
376
  def completable?
377
377
  false
@@ -393,6 +393,11 @@ describe Bumbleworks::Task do
393
393
  ['a_fella', 'waggle_hands'],
394
394
  ['a_lady', 'wiggle_hands']
395
395
  ]
396
+ tasks = described_class.completable(false)
397
+ tasks.should have(1).item
398
+ tasks.map { |t| [t.role, t.nickname] }.should == [
399
+ ['a_monkey', 'wuggle_hands']
400
+ ]
396
401
  end
397
402
  end
398
403
 
@@ -469,7 +474,7 @@ describe Bumbleworks::Task do
469
474
  end
470
475
  end
471
476
 
472
- context '.for_claimant' do
477
+ describe '.for_claimant' do
473
478
  it 'returns all tasks claimed by given claimant' do
474
479
  Bumbleworks.define_process 'dog-lifecycle' do
475
480
  concurrence do
@@ -493,7 +498,34 @@ describe Bumbleworks::Task do
493
498
  end
494
499
  end
495
500
 
496
- context '.for_entity' do
501
+ describe '.with_fields' do
502
+ it 'returns all tasks with given fields' do
503
+ Bumbleworks.define_process 'divergination' do
504
+ concurrence do
505
+ sequence do
506
+ set 'bumby' => 'fancy'
507
+ bumber :task => 'wear_monocle'
508
+ end
509
+ sequence do
510
+ set 'bumby' => 'not_fancy'
511
+ concurrence do
512
+ bumber :task => 'wear_natties'
513
+ loofer :task => 'snuffle'
514
+ end
515
+ end
516
+ end
517
+ end
518
+ Bumbleworks.launch!('divergination', :grumbles => true)
519
+ Bumbleworks.dashboard.wait_for(:loofer)
520
+ described_class.with_fields(:grumbles => true).count.should == 3
521
+ described_class.with_fields(:bumby => 'fancy').count.should == 1
522
+ described_class.with_fields(:bumby => 'not_fancy').count.should == 2
523
+ described_class.with_fields(:grumbles => false, :bumby => 'not_fancy').should be_empty
524
+ described_class.with_fields(:what => 'ever').should be_empty
525
+ end
526
+ end
527
+
528
+ describe '.for_entity' do
497
529
  it 'returns all tasks associated with given entity' do
498
530
  fake_sandwich = OpenStruct.new(:identifier => 'rubies')
499
531
  Bumbleworks.define_process 'existential_pb_and_j' do
@@ -3,121 +3,33 @@ require "bumbleworks/workitem_entity_storage"
3
3
  describe Bumbleworks::WorkitemEntityStorage do
4
4
  class FakeEntityHolder
5
5
  include Bumbleworks::WorkitemEntityStorage
6
- def initialize(fields = {})
7
- @fields = fields
8
- end
9
-
10
- def workitem
11
- OpenStruct.new(:fields => @fields)
12
- end
13
- end
14
-
15
- describe '#has_entity_fields?' do
16
- it 'returns true if workitem fields include entity fields' do
17
- feh = FakeEntityHolder.new('entity_id' => '1', 'entity_type' => 'SomeEntity')
18
- feh.should have_entity_fields
19
- end
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
-
26
- it 'returns false if workitem fields do not include entity fields' do
27
- feh = FakeEntityHolder.new
28
- feh.should_not have_entity_fields
6
+ attr_reader :workitem
7
+ def initialize(workitem)
8
+ @workitem = workitem
29
9
  end
30
10
  end
31
11
 
32
- describe '#has_entity?' do
33
- it 'returns true if entity is not nil' do
34
- feh = FakeEntityHolder.new
35
- feh.stub(:entity).and_return(:a_real_boy_not_a_puppet)
36
- feh.has_entity?.should be_true
12
+ describe '#entity_storage_workitem' do
13
+ it 'returns new Bumbleworks::Workitem instance with workitem' do
14
+ Bumbleworks::Workitem.stub(:new).with(:a_workitem).and_return(:the_workitem)
15
+ feh = FakeEntityHolder.new(:a_workitem)
16
+ feh.entity_storage_workitem.should == :the_workitem
37
17
  end
38
18
 
39
- it 'returns false if EntityNotFound' do
40
- feh = FakeEntityHolder.new
41
- feh.stub(:entity).and_raise(Bumbleworks::WorkitemEntityStorage::EntityNotFound)
42
- feh.has_entity?.should be_false
19
+ it 'is memoized' do
20
+ feh = FakeEntityHolder.new(:a_workitem)
21
+ esw = feh.entity_storage_workitem
22
+ feh.entity_storage_workitem.should be esw
43
23
  end
44
24
  end
45
25
 
46
- describe '#entity' do
47
- before :all do
48
- class LovelyEntity
49
- attr_accessor :identifier
50
- def initialize(identifier)
51
- @identifier = identifier
52
- end
53
-
54
- def self.first_by_identifier(identifier)
55
- return nil unless identifier
56
- new(identifier)
57
- end
26
+ [:has_entity_fields?, :has_entity?, :entity, :entity_fields].each do |method|
27
+ describe "##{method}" do
28
+ it 'delegates to entity storage workitem' do
29
+ feh = FakeEntityHolder.new(:a_workitem)
30
+ feh.entity_storage_workitem.stub(method).with(1, 2, 3).and_return(:yay_for_bikes)
31
+ feh.send(method, 1, 2, 3).should == :yay_for_bikes
58
32
  end
59
33
  end
60
-
61
- after :all do
62
- Object.send(:remove_const, :LovelyEntity)
63
- end
64
-
65
- it 'attempts to instantiate business entity from _id and _type fields' do
66
- feh = FakeEntityHolder.new('entity_id' => '15', 'entity_type' => 'LovelyEntity')
67
- feh.entity.identifier.should == '15'
68
- end
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
-
75
- it 'throw exception if entity fields not present' do
76
- feh = FakeEntityHolder.new
77
- expect {
78
- feh.entity
79
- }.to raise_error Bumbleworks::WorkitemEntityStorage::EntityNotFound
80
- end
81
-
82
- it 'throw exception if entity returns nil' do
83
- feh = FakeEntityHolder.new('entity_id' => nil, 'entity_type' => 'LovelyEntity')
84
- expect {
85
- feh.entity
86
- }.to raise_error Bumbleworks::WorkitemEntityStorage::EntityNotFound, '{:entity_id=>nil, :entity_type=>"LovelyEntity"}'
87
- end
88
-
89
- it 'returns same instance when called twice' do
90
- feh = FakeEntityHolder.new('entity_id' => '15', 'entity_type' => 'LovelyEntity')
91
- feh.entity.identifier = 'pickles'
92
- feh.entity.identifier.should == 'pickles'
93
- end
94
-
95
- it 'reloads instance when called with reload option' do
96
- feh = FakeEntityHolder.new('entity_id' => '15', 'entity_type' => 'LovelyEntity')
97
- feh.entity.identifier = 'pickles'
98
- feh.entity(:reload => true).identifier.should == '15'
99
- end
100
- end
101
-
102
- describe "#entity_fields" do
103
- it 'returns empty hash if no entity' do
104
- feh = FakeEntityHolder.new
105
- feh.entity_fields.should == {}
106
- end
107
-
108
- it 'returns class name and identifier by default' do
109
- feh = FakeEntityHolder.new('entity_id' => '15', 'entity_type' => 'LovelyEntity')
110
- feh.entity_fields.should == { :type => 'LovelyEntity', :identifier => '15' }
111
- end
112
-
113
- it 'humanizes class name when requested' do
114
- feh = FakeEntityHolder.new('entity_id' => '15', 'entity_type' => 'LovelyEntity')
115
- feh.entity_fields(:humanize => true).should == { :type => 'Lovely entity', :identifier => '15' }
116
- end
117
-
118
- it 'titleizes class name when requested' do
119
- feh = FakeEntityHolder.new('entity_id' => '15', 'entity_type' => 'LovelyEntity')
120
- feh.entity_fields(:titleize => true).should == { :type => 'Lovely Entity', :identifier => '15' }
121
- end
122
34
  end
123
35
  end
@@ -0,0 +1,88 @@
1
+ require File.expand_path(File.join(fixtures_path, 'entities', 'rainbow_loom'))
2
+
3
+ describe Bumbleworks::Workitem do
4
+ let(:ruote_workitem) { Ruote::Workitem.new('fields' => {'entity_id' => '123', 'entity_type' => 'RainbowLoom'} ) }
5
+ let(:workitem) { Bumbleworks::Workitem.new(ruote_workitem)}
6
+
7
+ describe '#has_entity_fields?' do
8
+ it 'returns true if workitem fields include entity fields' do
9
+ workitem.should have_entity_fields
10
+ end
11
+
12
+ it 'returns true if workitem fields include symbolized version of entity fields' do
13
+ ruote_workitem.fields = { :entity_id => '123', :entity_type => 'RainbowLoom' }
14
+ workitem.should have_entity_fields
15
+ end
16
+
17
+ it 'returns false if workitem fields do not include entity fields' do
18
+ ruote_workitem.fields = {}
19
+ workitem.should_not have_entity_fields
20
+ end
21
+ end
22
+
23
+ describe '#has_entity?' do
24
+ it 'returns true if entity is not nil' do
25
+ workitem.stub(:entity).and_return(:a_real_boy_not_a_puppet)
26
+ workitem.has_entity?.should be_true
27
+ end
28
+
29
+ it 'returns false if EntityNotFound' do
30
+ workitem.stub(:entity).and_raise(Bumbleworks::EntityNotFound)
31
+ workitem.has_entity?.should be_false
32
+ end
33
+ end
34
+
35
+ describe '#entity' do
36
+ it 'attempts to instantiate business entity from _id and _type fields' do
37
+ workitem.entity.identifier.should == '123'
38
+ end
39
+
40
+ it 'works with symbolized _id and _type fields' do
41
+ ruote_workitem.fields = { :entity_id => '125', :entity_type => 'RainbowLoom' }
42
+ workitem.entity.identifier.should == '125'
43
+ end
44
+
45
+ it 'throw exception if entity fields not present' do
46
+ ruote_workitem.fields = {}
47
+ expect {
48
+ workitem.entity
49
+ }.to raise_error Bumbleworks::EntityNotFound
50
+ end
51
+
52
+ it 'throw exception if entity returns nil' do
53
+ ruote_workitem.fields['entity_id'] = nil
54
+ expect {
55
+ workitem.entity
56
+ }.to raise_error Bumbleworks::EntityNotFound, '{:entity_id=>nil, :entity_type=>"RainbowLoom"}'
57
+ end
58
+
59
+ it 'returns same instance when called twice' do
60
+ workitem.entity.identifier = 'nerfus'
61
+ workitem.entity.identifier.should == 'nerfus'
62
+ end
63
+
64
+ it 'reloads instance when called with reload option' do
65
+ workitem.entity.identifier = 'pickles'
66
+ workitem.entity(:reload => true).identifier.should == '123'
67
+ end
68
+ end
69
+
70
+ describe "#entity_fields" do
71
+ it 'returns empty hash if no entity' do
72
+ ruote_workitem.fields = {}
73
+ workitem.entity_fields.should == {}
74
+ end
75
+
76
+ it 'returns class name and identifier by default' do
77
+ workitem.entity_fields.should == { :type => 'RainbowLoom', :identifier => '123' }
78
+ end
79
+
80
+ it 'humanizes class name when requested' do
81
+ workitem.entity_fields(:humanize => true).should == { :type => 'Rainbow loom', :identifier => '123' }
82
+ end
83
+
84
+ it 'titleizes class name when requested' do
85
+ workitem.entity_fields(:titleize => true).should == { :type => 'Rainbow Loom', :identifier => '123' }
86
+ end
87
+ end
88
+ 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.51
4
+ version: 0.0.52
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: bin
14
14
  cert_chain: []
15
- date: 2013-12-21 00:00:00.000000000 Z
15
+ date: 2013-12-23 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: ruote
@@ -192,6 +192,7 @@ files:
192
192
  - lib/bumbleworks/task/finder.rb
193
193
  - lib/bumbleworks/tree_builder.rb
194
194
  - lib/bumbleworks/version.rb
195
+ - lib/bumbleworks/workitem.rb
195
196
  - lib/bumbleworks/workitem_entity_storage.rb
196
197
  - lib/tasks/bumbleworks.rake
197
198
  - spec/fixtures/apps/with_default_directories/config_initializer.rb
@@ -209,7 +210,7 @@ files:
209
210
  - spec/fixtures/definitions/a_list_of_jams.rb
210
211
  - spec/fixtures/definitions/nested_folder/test_nested_process.rb
211
212
  - spec/fixtures/definitions/test_process.rb
212
- - spec/fixtures/entities/furby.rb
213
+ - spec/fixtures/entities/rainbow_loom.rb
213
214
  - spec/integration/entity_spec.rb
214
215
  - spec/integration/example_configurations_spec.rb
215
216
  - spec/integration/history_storage_spec.rb
@@ -236,6 +237,7 @@ files:
236
237
  - spec/lib/bumbleworks/task_spec.rb
237
238
  - spec/lib/bumbleworks/tree_builder_spec.rb
238
239
  - spec/lib/bumbleworks/workitem_entity_storage_spec.rb
240
+ - spec/lib/bumbleworks/workitem_spec.rb
239
241
  - spec/lib/bumbleworks_spec.rb
240
242
  - spec/spec_helper.rb
241
243
  - spec/support/path_helpers.rb
@@ -256,7 +258,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
256
258
  version: '0'
257
259
  segments:
258
260
  - 0
259
- hash: 3654012293200384530
261
+ hash: -2295219259305146267
260
262
  required_rubygems_version: !ruby/object:Gem::Requirement
261
263
  none: false
262
264
  requirements:
@@ -265,7 +267,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
265
267
  version: '0'
266
268
  segments:
267
269
  - 0
268
- hash: 3654012293200384530
270
+ hash: -2295219259305146267
269
271
  requirements: []
270
272
  rubyforge_project:
271
273
  rubygems_version: 1.8.23
@@ -288,7 +290,7 @@ test_files:
288
290
  - spec/fixtures/definitions/a_list_of_jams.rb
289
291
  - spec/fixtures/definitions/nested_folder/test_nested_process.rb
290
292
  - spec/fixtures/definitions/test_process.rb
291
- - spec/fixtures/entities/furby.rb
293
+ - spec/fixtures/entities/rainbow_loom.rb
292
294
  - spec/integration/entity_spec.rb
293
295
  - spec/integration/example_configurations_spec.rb
294
296
  - spec/integration/history_storage_spec.rb
@@ -315,6 +317,7 @@ test_files:
315
317
  - spec/lib/bumbleworks/task_spec.rb
316
318
  - spec/lib/bumbleworks/tree_builder_spec.rb
317
319
  - spec/lib/bumbleworks/workitem_entity_storage_spec.rb
320
+ - spec/lib/bumbleworks/workitem_spec.rb
318
321
  - spec/lib/bumbleworks_spec.rb
319
322
  - spec/spec_helper.rb
320
323
  - spec/support/path_helpers.rb