bumbleworks 0.0.71 → 0.0.72

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.
@@ -4,6 +4,7 @@ require "bumbleworks/configuration"
4
4
  require "bumbleworks/support"
5
5
  require "bumbleworks/process_definition"
6
6
  require "bumbleworks/process"
7
+ require "bumbleworks/expression"
7
8
  require "bumbleworks/task"
8
9
  require "bumbleworks/participant_registration"
9
10
  require "bumbleworks/ruote"
@@ -169,6 +169,13 @@ module Bumbleworks
169
169
  #
170
170
  define_setting :store_history
171
171
 
172
+ # The set of registered entity classes. This can be manually set in configuration,
173
+ # but if you include Bumbleworks::Entity in any class, that class will automatically
174
+ # be loaded into this array at the time of module inclusion.
175
+ #
176
+ # default: []
177
+ define_setting :entity_classes
178
+
172
179
  def initialize
173
180
  @storage_adapters = []
174
181
  @storage_options = {}
@@ -226,6 +233,11 @@ module Bumbleworks
226
233
  @store_history.nil? ? true : @store_history
227
234
  end
228
235
 
236
+ # Default entity_classes to empty array
237
+ def entity_classes
238
+ @entity_classes ||= []
239
+ end
240
+
229
241
  # Root folder where Bumbleworks looks for ruote assets (participants,
230
242
  # process_definitions, etc.) The root path must be absolute.
231
243
  # It can be defined through a configuration block:
@@ -1,6 +1,7 @@
1
1
  module Bumbleworks
2
2
  module Entity
3
3
  def self.included(klass)
4
+ Bumbleworks.entity_classes << klass
4
5
  klass.extend ClassMethods
5
6
  end
6
7
 
@@ -83,11 +84,13 @@ module Bumbleworks
83
84
  end
84
85
 
85
86
  module ClassMethods
86
- attr_reader :processes
87
-
88
87
  def process(process_name, options = {})
89
88
  options[:attribute] ||= default_process_identifier_attribute(process_name)
90
- (@processes ||= {})[process_name.to_sym] = options
89
+ processes[process_name.to_sym] = options
90
+ end
91
+
92
+ def processes
93
+ @processes ||= {}
91
94
  end
92
95
 
93
96
  def entity_type
@@ -0,0 +1,62 @@
1
+ module Bumbleworks
2
+ class Expression
3
+ attr_reader :expid
4
+
5
+ def initialize(flow_expression)
6
+ @flow_expression = flow_expression
7
+ @fei = @flow_expression.fei
8
+ @expid = @fei.expid
9
+ end
10
+
11
+ # Returns a Bumbleworks::Process instance for the expression's
12
+ # wfid; effectively, the process instance this expression is
13
+ # a part of.
14
+ def process
15
+ @process ||= Bumbleworks::Process.new(@fei.wfid)
16
+ end
17
+
18
+ # Returns the process tree at this expression.
19
+ def tree
20
+ @flow_expression.tree
21
+ end
22
+
23
+ # Returns a Bumbleworks::Process::ErrorRecord instance for the
24
+ # expression's error, if there is one. If no error was raised
25
+ # during the execution of this expression, returns nil.
26
+ #
27
+ # Note that what is returned is not the exception itself that
28
+ # was raised during execution, but rather a *record* of that error.
29
+ # If you want a re-created instance of the actual exception,
30
+ # you can call #reify on the ErrorRecord instance returned.
31
+ def error
32
+ @error ||= ruote_error
33
+ end
34
+
35
+ # Cancel this expression. The process will then move on to the
36
+ # next expression.
37
+ def cancel!
38
+ Bumbleworks.dashboard.cancel_expression(@fei)
39
+ end
40
+
41
+ # Kill this expression. The process will then move on to the
42
+ # next expression.
43
+ #
44
+ # WARNING: Killing an expression will not trigger any 'on_cancel'
45
+ # callbacks. It's preferable to #cancel! the expression if you
46
+ # can.
47
+ def kill!
48
+ Bumbleworks.dashboard.kill_expression(@fei)
49
+ end
50
+
51
+ # Returns the workitem as it was applied to this expression.
52
+ def workitem
53
+ Workitem.new(@flow_expression.applied_workitem)
54
+ end
55
+
56
+ private
57
+
58
+ def ruote_error
59
+ process.errors.detect { |err| err.fei == @fei }
60
+ end
61
+ end
62
+ end
@@ -1,4 +1,5 @@
1
1
  require "bumbleworks/workitem_entity_storage"
2
+ require "bumbleworks/process/error_record"
2
3
 
3
4
  module Bumbleworks
4
5
  class Process
@@ -9,14 +10,18 @@ module Bumbleworks
9
10
  attr_reader :id
10
11
 
11
12
  class << self
12
- def all
13
- ids.map do |wfid|
13
+ def all(options = {})
14
+ ids(options).map do |wfid|
14
15
  new(wfid)
15
16
  end
16
17
  end
17
18
 
18
- def ids
19
- Bumbleworks.dashboard.process_wfids
19
+ def ids(options = {})
20
+ wfids = Bumbleworks.dashboard.process_wfids
21
+ wfids.reverse! if options[:reverse]
22
+ limit = options[:limit] || wfids.count
23
+ offset = options[:offset] || 0
24
+ wfids[offset, limit]
20
25
  end
21
26
 
22
27
  def count
@@ -37,6 +42,10 @@ module Bumbleworks
37
42
 
38
43
  alias_method :wfid, :id
39
44
 
45
+ def <=>(other)
46
+ wfid <=> other.wfid
47
+ end
48
+
40
49
  def ==(other)
41
50
  wfid == other.wfid
42
51
  end
@@ -54,31 +63,33 @@ module Bumbleworks
54
63
  end
55
64
 
56
65
  def expressions
57
- @expressions ||= begin
58
- context = Bumbleworks.dashboard.context
59
- raw_expressions = context.storage.get_many('expressions', [wfid])
60
- raw_expressions.collect { |e|
61
- ::Ruote::Exp::FlowExpression.from_h(context, e)
62
- }.sort_by { |e|
63
- e.fei.expid
64
- }
65
- end
66
+ @expressions ||= ruote_expressions.map { |rexp|
67
+ Bumbleworks::Expression.new(rexp)
68
+ }
69
+ end
70
+
71
+ def expression_at_position(position)
72
+ expressions.detect { |exp| exp.expid == position }
66
73
  end
67
74
 
68
75
  def errors
69
76
  @errors ||= Bumbleworks.dashboard.context.storage.get_many('errors', [wfid]).map { |err|
70
- ::Ruote::ProcessError.new(err)
77
+ Bumbleworks::Process::ErrorRecord.new(
78
+ ::Ruote::ProcessError.new(err)
79
+ )
71
80
  }
72
81
  end
73
82
 
74
83
  def leaves
75
- @leaves ||= expressions.inject([]) { |a, exp|
84
+ @leaves ||= ruote_expressions.inject([]) { |a, exp|
76
85
  a.select { |e| ! exp.ancestor?(e.fei) } + [ exp ]
86
+ }.map { |leaf|
87
+ Bumbleworks::Expression.new(leaf)
77
88
  }
78
89
  end
79
90
 
80
91
  def workitems
81
- @workitems ||= leaves.map(&:applied_workitem).map { |wi| Bumbleworks::Workitem.new(wi) }
92
+ @workitems ||= leaves.map(&:workitem)
82
93
  end
83
94
 
84
95
  def tasks
@@ -131,5 +142,19 @@ module Bumbleworks
131
142
  end
132
143
  super
133
144
  end
145
+
146
+ private
147
+
148
+ def ruote_expressions
149
+ @ruote_expressions ||= begin
150
+ context = Bumbleworks.dashboard.context
151
+ raw_expressions = context.storage.get_many('expressions', [wfid])
152
+ raw_expressions.collect { |e|
153
+ ::Ruote::Exp::FlowExpression.from_h(context, e)
154
+ }.sort_by { |e|
155
+ e.fei.expid
156
+ }
157
+ end
158
+ end
134
159
  end
135
160
  end
@@ -0,0 +1,67 @@
1
+ module Bumbleworks
2
+ class Process
3
+ class ErrorRecord
4
+ # The initializer takes a Ruote::ProcessError instance.
5
+ def initialize(process_error)
6
+ @process_error = process_error
7
+ end
8
+
9
+ # Returns the FlowExpressionId of the expression where this error
10
+ # occurred.
11
+ def fei
12
+ @process_error.fei
13
+ end
14
+
15
+ # Returns the class name of the error that was actually raised
16
+ # during execution of the process.
17
+
18
+ # Be aware that this class may not exist in the current binding
19
+ # when you instantiate the ErrorRecord; if it does not, calling
20
+ # #reify will throw an exception.
21
+ def error_class_name
22
+ @process_error.h['class']
23
+ end
24
+
25
+ # Returns the original error's backtrace.
26
+ #
27
+ # The original backtrace will be returned in standard backtrace
28
+ # format: an array of strings with paths and line numbers.
29
+ def backtrace
30
+ @process_error.h['trace'].split(/\n/)
31
+ end
32
+
33
+ # Returns the original error message.
34
+ #
35
+ # Ruote's error logging has a strange issue; the message recorded
36
+ # and returned via the Ruote::ProcessError instance is the full
37
+ # #inspect of the error. This method strips away the resulting
38
+ # cruft (if it exists) and leaves behind just the message itself.
39
+ def message
40
+ @message ||= @process_error.message.
41
+ gsub(/\#\<#{error_class_name}: (.*)\>$/, '\1')
42
+ end
43
+
44
+ # Re-instantiates the original exception.
45
+ #
46
+ # If you wish to re-create the actual exception that was raised
47
+ # during process execution, this method will attempt to return
48
+ # an instance of the error class, with the message and backtrace
49
+ # restored.
50
+ #
51
+ # In order for this to work, the error class itself must be a
52
+ # defined constant in the current binding; if it's not, you'll get
53
+ # an exception. Be cognizant of this caveat if you choose to use
54
+ # this feature; Bumbleworks makes no attempt to protect you.
55
+ #
56
+ # This is not because Bumbleworks doesn't love you. It just wants
57
+ # you to spread your wings, and the only way to truly experience
58
+ # flight is to first taste the ground.
59
+ def reify
60
+ klass = Bumbleworks::Support.constantize(error_class_name)
61
+ err = klass.new(message)
62
+ err.set_backtrace(backtrace)
63
+ err
64
+ end
65
+ end
66
+ end
67
+ end
@@ -8,6 +8,10 @@ module Bumbleworks
8
8
  new(tid, attrs)
9
9
  end
10
10
  end
11
+
12
+ def count
13
+ all.count
14
+ end
11
15
  end
12
16
 
13
17
  def initialize(id, original_hash = nil)
@@ -43,7 +47,7 @@ module Bumbleworks
43
47
 
44
48
  def waiting_expression
45
49
  return nil unless fei
46
- process.expressions.detect { |e| e.fei.expid == fei['expid'] }.tree
50
+ process.expression_at_position(fei['expid']).tree
47
51
  end
48
52
 
49
53
  def where_clause
@@ -1,3 +1,3 @@
1
1
  module Bumbleworks
2
- VERSION = "0.0.71"
2
+ VERSION = "0.0.72"
3
3
  end
@@ -0,0 +1,8 @@
1
+ class NaughtyParticipant
2
+ class StupidError < StandardError; end
3
+ include Bumbleworks::LocalParticipant
4
+
5
+ def on_workitem
6
+ raise StupidError, 'Oh crumb.'
7
+ end
8
+ end
@@ -10,6 +10,15 @@ describe 'Entity Module' do
10
10
  load File.join(app_root, 'full_initializer.rb')
11
11
  end
12
12
 
13
+ describe 'including' do
14
+ it 'registers entity with Bumbleworks' do
15
+ Bumbleworks.entity_classes = [:geese]
16
+ FirstNewClass = Class.new { include Bumbleworks::Entity }
17
+ SecondNewClass = Class.new { include Bumbleworks::Entity }
18
+ Bumbleworks.entity_classes.should == [:geese, FirstNewClass, SecondNewClass]
19
+ end
20
+ end
21
+
13
22
  describe 'process control' do
14
23
  it 'launching assigns entity to process and subsequent tasks' do
15
24
  rainbow_loom = RainbowLoom.new('12345')
@@ -218,6 +218,18 @@ describe Bumbleworks::Configuration do
218
218
  end
219
219
  end
220
220
 
221
+ describe '#entity_classes' do
222
+ it 'is empty by default' do
223
+ configuration.entity_classes.should be_empty
224
+ end
225
+
226
+ it 'returns the registered entity classes' do
227
+ configuration.entity_classes = [:fumpin, :nuffin]
228
+ configuration.entity_classes << :summin
229
+ configuration.entity_classes.should == [:fumpin, :nuffin, :summin]
230
+ end
231
+ end
232
+
221
233
  describe "#storage" do
222
234
  it 'can set storage directly' do
223
235
  storage = double("Storage")
@@ -253,6 +253,12 @@ describe Bumbleworks::Entity do
253
253
  end
254
254
  end
255
255
 
256
+ describe '.processes' do
257
+ it 'returns empty hash if no registered processes' do
258
+ expect(entity_class.processes).to eq({})
259
+ end
260
+ end
261
+
256
262
  describe '.default_process_identifier_attribute' do
257
263
  it 'adds _process_identifier to end of given process name' do
258
264
  entity_class.default_process_identifier_attribute('zoof').should == :zoof_process_identifier
@@ -0,0 +1,69 @@
1
+ describe Bumbleworks::Expression do
2
+ let(:fei) { double({ :expid => '1_2_3', :wfid => 'snooks' }) }
3
+ let(:fexp) { double('FlowExpression', :fei => fei, :tree => :a_tree) }
4
+ subject { described_class.new(fexp) }
5
+
6
+ describe '#expid' do
7
+ it 'returns expid from fei' do
8
+ subject.expid.should == '1_2_3'
9
+ end
10
+ end
11
+
12
+ describe '#process' do
13
+ it 'returns process for expression wfid' do
14
+ subject.process.should == Bumbleworks::Process.new('snooks')
15
+ end
16
+ end
17
+
18
+ describe '#tree' do
19
+ it 'returns tree from flow expression' do
20
+ expect(subject.tree).to eq :a_tree
21
+ end
22
+ end
23
+
24
+ describe '#error' do
25
+ it 'returns error from process that matches fei' do
26
+ process = double
27
+ process.stub(:errors => [
28
+ double(:fei => :not_me, :message => 'alarming!'),
29
+ double(:fei => fei, :message => 'boo!'),
30
+ double(:fei => :also_not_me, :message => 'yippee!')
31
+ ])
32
+ subject.stub(:process => process)
33
+ subject.error.message.should == 'boo!'
34
+ end
35
+
36
+ it 'returns nil if no error during this expression' do
37
+ process = double
38
+ process.stub(:errors => [
39
+ double(:fei => :not_me, :message => 'alarming!'),
40
+ double(:fei => :also_not_me, :message => 'yippee!')
41
+ ])
42
+ subject.stub(:process => process)
43
+ subject.error.should be_nil
44
+ end
45
+ end
46
+
47
+ describe '#cancel!' do
48
+ it 'cancels the expression' do
49
+ Bumbleworks.storage = {}
50
+ Bumbleworks.dashboard.should_receive(:cancel_expression).with(fei)
51
+ subject.cancel!
52
+ end
53
+ end
54
+
55
+ describe '#kill!' do
56
+ it 'kills the expression' do
57
+ Bumbleworks.storage = {}
58
+ Bumbleworks.dashboard.should_receive(:kill_expression).with(fei)
59
+ subject.kill!
60
+ end
61
+ end
62
+
63
+ describe '#workitem' do
64
+ it 'returns the workitem as applied to this expression' do
65
+ fexp.stub(:applied_workitem).and_return(:something_raw)
66
+ subject.workitem.should == Bumbleworks::Workitem.new(:something_raw)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,62 @@
1
+ require File.expand_path(File.join(fixtures_path, 'participants', 'naughty_participant'))
2
+
3
+ describe Bumbleworks::Process::ErrorRecord do
4
+ before :each do
5
+ Bumbleworks.reset!
6
+ Bumbleworks.storage = {}
7
+ Bumbleworks::Ruote.register_participants do
8
+ fall_apart NaughtyParticipant
9
+ end
10
+ Bumbleworks.start_worker!
11
+
12
+ Bumbleworks.define_process 'a_pain_in_the_tush' do
13
+ fall_apart
14
+ end
15
+ @process = Bumbleworks.launch!('a_pain_in_the_tush')
16
+ wait_until { @process.reload.errors.count == 1 }
17
+ @error = @process.errors.first
18
+ end
19
+
20
+ describe '#error_class_name' do
21
+ it 'returns the class name of the recorded error (as a string)' do
22
+ expect(@error.error_class_name).to eq 'NaughtyParticipant::StupidError'
23
+ end
24
+ end
25
+
26
+ describe '#backtrace' do
27
+ it 'returns the recorded error backtrace as array of strings' do
28
+ expect(@error.backtrace).to be_an Array
29
+ expect(@error.backtrace.first).to match(/in `on_workitem'$/)
30
+ end
31
+ end
32
+
33
+ describe '#fei' do
34
+ it 'returns the fei from when the error occurred' do
35
+ fei = @error.fei
36
+ expect(fei.wfid).to eq @process.wfid
37
+ expect(fei.expid).to eq '0_0'
38
+ end
39
+ end
40
+
41
+ describe '#message' do
42
+ it 'returns the original error message' do
43
+ expect(@error.message).to eq 'Oh crumb.'
44
+ end
45
+ end
46
+
47
+ describe '#reify' do
48
+ it 're-instantiates the original exception' do
49
+ original_error = @error.reify
50
+ expect(original_error).to be_a NaughtyParticipant::StupidError
51
+ expect(original_error.backtrace).to eq @error.backtrace
52
+ expect(original_error.message).to eq @error.message
53
+ end
54
+
55
+ it 'raises exception if exception class not defined' do
56
+ @error.instance_variable_get(:@process_error).h['class'] = 'Goose'
57
+ expect {
58
+ @error.reify
59
+ }.to raise_error(NameError, 'uninitialized constant Goose')
60
+ end
61
+ end
62
+ end
@@ -7,6 +7,14 @@ describe Bumbleworks::Process do
7
7
  Bumbleworks::Ruote.register_participants
8
8
  Bumbleworks.start_worker!
9
9
 
10
+ Bumbleworks.define_process 'food_is_an_illusion' do
11
+ noop :tag => 'oh_boy_are_we_hungry'
12
+ concurrence do
13
+ admin :task => 'eat_a_hat'
14
+ hatter :task => 'weep'
15
+ end
16
+ end
17
+
10
18
  Bumbleworks.define_process 'going_to_the_dance' do
11
19
  concurrence do
12
20
  wait_for_event :an_invitation
@@ -25,30 +33,53 @@ describe Bumbleworks::Process do
25
33
  end
26
34
  end
27
35
 
28
- context 'aggregate methods' do
36
+ describe '.all' do
37
+ it 'returns sorted and filtered array of instances for all processes' do
38
+ expect(described_class).to receive(:ids).with(:some_options).and_return([:a, :b, :c])
39
+ expect(described_class.all(:some_options)).to eq [
40
+ Bumbleworks::Process.new(:a),
41
+ Bumbleworks::Process.new(:b),
42
+ Bumbleworks::Process.new(:c)
43
+ ]
44
+ end
45
+ end
46
+
47
+ describe '.ids' do
29
48
  before(:each) do
30
- @bp1 = Bumbleworks.launch!('going_to_the_dance')
31
- @bp2 = Bumbleworks.launch!('going_to_the_dance')
32
- @bp3 = Bumbleworks.launch!('straightening_the_rocks')
33
- wait_until { Bumbleworks.dashboard.process_wfids.count == 3 }
49
+ bp1 = Bumbleworks.launch!('going_to_the_dance')
50
+ bp2 = Bumbleworks.launch!('going_to_the_dance')
51
+ bp3 = Bumbleworks.launch!('going_to_the_dance')
52
+ bp4 = Bumbleworks.launch!('going_to_the_dance')
53
+ bp5 = Bumbleworks.launch!('straightening_the_rocks')
54
+ @sorted_processes = [bp1, bp2, bp3, bp4, bp5].sort
55
+ wait_until { Bumbleworks.dashboard.process_wfids.count == 5 }
34
56
  end
35
57
 
36
- describe '.all' do
37
- it 'returns instances for all process wfids' do
38
- described_class.all.should =~ [@bp1, @bp2, @bp3]
39
- end
58
+ it 'returns all process wfids' do
59
+ described_class.ids.should == @sorted_processes.map(&:wfid)
40
60
  end
41
61
 
42
- describe '.ids' do
43
- it 'returns all process wfids' do
44
- described_class.ids.should =~ [@bp1.wfid, @bp2.wfid, @bp3.wfid]
45
- end
62
+ it 'allows pagination options' do
63
+ described_class.ids(:limit => 2).should == @sorted_processes[0, 2].map(&:wfid)
64
+ described_class.ids(:offset => 2).should == @sorted_processes[2, 5].map(&:wfid)
65
+ described_class.ids(:limit => 2, :offset => 1).should == @sorted_processes[1, 2].map(&:wfid)
46
66
  end
47
67
 
48
- describe '.count' do
49
- it 'returns number of processes' do
50
- described_class.count.should == 3
51
- end
68
+ it 'allows reverse order' do
69
+ described_class.ids(:reverse => true).should == @sorted_processes.reverse.map(&:wfid)
70
+ end
71
+
72
+ it 'allows combined reverse and pagination' do
73
+ described_class.ids(:reverse => true, :limit => 2).should == @sorted_processes.reverse[0, 2].map(&:wfid)
74
+ described_class.ids(:reverse => true, :offset => 2).should == @sorted_processes.reverse[2, 5].map(&:wfid)
75
+ described_class.ids(:reverse => true, :limit => 2, :offset => 1).should == @sorted_processes.reverse[1, 2].map(&:wfid)
76
+ end
77
+ end
78
+
79
+ describe '.count' do
80
+ it 'returns number of processes' do
81
+ allow(described_class).to receive(:ids).and_return([:a, :b, :c, :d])
82
+ described_class.count.should == 4
52
83
  end
53
84
  end
54
85
 
@@ -67,7 +98,7 @@ describe Bumbleworks::Process do
67
98
  end
68
99
 
69
100
  describe '#errors' do
70
- it 'returns all process errors' do
101
+ it 'returns all process errors as ErrorRecord instances' do
71
102
  Bumbleworks.define_process 'error_process' do
72
103
  concurrence do
73
104
  error 'first error'
@@ -77,25 +108,24 @@ describe Bumbleworks::Process do
77
108
  bp = Bumbleworks.launch!('error_process')
78
109
  Bumbleworks.dashboard.wait_for('error_intercepted')
79
110
  errors = bp.errors
111
+ errors.map(&:class).uniq.should == [
112
+ Bumbleworks::Process::ErrorRecord
113
+ ]
80
114
  errors.map(&:message).should =~ [
81
- '#<Ruote::ForcedError: first error>',
82
- '#<Ruote::ForcedError: second error>'
115
+ 'first error',
116
+ 'second error'
83
117
  ]
84
118
  end
85
119
  end
86
120
 
87
121
  describe '#workitems' do
88
- it 'returns array of applied workitems from each leaf' do
122
+ it 'returns array of workitems from each leaf' do
89
123
  bp = described_class.new('chumpy')
90
- l1 = double(:applied_workitem => 'aw1')
91
- l2 = double(:applied_workitem => 'aw2')
92
- l3 = double(:applied_workitem => 'aw3')
124
+ l1 = double(:workitem => 'w1')
125
+ l2 = double(:workitem => 'w2')
126
+ l3 = double(:workitem => 'w3')
93
127
  bp.stub(:leaves => [l1, l2, l3])
94
- bp.workitems.should == [
95
- Bumbleworks::Workitem.new('aw1'),
96
- Bumbleworks::Workitem.new('aw2'),
97
- Bumbleworks::Workitem.new('aw3')
98
- ]
128
+ bp.workitems.should == ['w1','w2','w3']
99
129
  end
100
130
  end
101
131
 
@@ -109,6 +139,49 @@ describe Bumbleworks::Process do
109
139
  let(:storage_workitem) { entity_workitem }
110
140
  end
111
141
 
142
+ describe '#expressions' do
143
+ it 'returns all expressions as array of Expression instances' do
144
+ bp = Bumbleworks.launch!('food_is_an_illusion')
145
+ Bumbleworks.dashboard.wait_for(:admin)
146
+ expect(bp.expressions.map(&:expid)).to eq [
147
+ '0', '0_1', '0_1_0', '0_1_1'
148
+ ]
149
+ expect(bp.expressions.map(&:class).uniq).to eq [
150
+ Bumbleworks::Expression
151
+ ]
152
+ end
153
+ end
154
+
155
+ describe '#expression_at_position' do
156
+ before(:each) do
157
+ @bp = Bumbleworks.launch!('food_is_an_illusion')
158
+ Bumbleworks.dashboard.wait_for(:admin)
159
+ end
160
+
161
+ it 'returns the expression whose expid matches the given position' do
162
+ expression = @bp.expression_at_position('0_1_0')
163
+ expect(expression).to be_a Bumbleworks::Expression
164
+ expect(expression.expid).to eq '0_1_0'
165
+ end
166
+
167
+ it 'returns nil if no expression at given position' do
168
+ expect(@bp.expression_at_position('0_2_1')).to be_nil
169
+ end
170
+ end
171
+
172
+ describe '#leaves' do
173
+ it 'returns only expressions being worked on' do
174
+ bp = Bumbleworks.launch!('food_is_an_illusion')
175
+ Bumbleworks.dashboard.wait_for(:admin)
176
+ expect(bp.leaves.map(&:expid)).to eq [
177
+ '0_1_0', '0_1_1'
178
+ ]
179
+ expect(bp.leaves.map(&:class).uniq).to eq [
180
+ Bumbleworks::Expression
181
+ ]
182
+ end
183
+ end
184
+
112
185
  describe '#entity_workitem' do
113
186
  it 'returns first workitem from workitems array, if no entity fields conflict' do
114
187
  bp = described_class.new('nothing')
@@ -260,6 +333,17 @@ describe Bumbleworks::Process do
260
333
  end
261
334
  end
262
335
 
336
+ describe '#<=>' do
337
+ it 'compares processes by wfid' do
338
+ bp1 = described_class.new(1)
339
+ bp2 = described_class.new(2)
340
+ bp3 = described_class.new(1)
341
+ expect(bp1 <=> bp2).to eq -1
342
+ expect(bp2 <=> bp3).to eq 1
343
+ expect(bp3 <=> bp1).to eq 0
344
+ end
345
+ end
346
+
263
347
  describe '#process_status' do
264
348
  it 'returns a process_status instance for the wfid' do
265
349
  bp = described_class.new('frogheads')
@@ -22,6 +22,12 @@ describe Bumbleworks::Tracker do
22
22
  end
23
23
  end
24
24
 
25
+ describe '.count' do
26
+ it 'returns count of current trackers' do
27
+ expect(described_class.count).to eq 5
28
+ end
29
+ end
30
+
25
31
  describe '.new' do
26
32
  it 'sets tracker id and fetches original_hash from dashboard' do
27
33
  tr = described_class.new('global_tracker')
@@ -113,8 +119,8 @@ describe Bumbleworks::Tracker do
113
119
 
114
120
  it 'returns expression awaiting reply' do
115
121
  process = Bumbleworks::Process.new('my_wfid')
116
- expression1 = double(:fei => double(:expid => '0_0_0'), :tree => :a_global_expression)
117
- expression2 = double(:fei => double(:expid => '0_0_1'), :tree => :a_local_expression)
122
+ expression1 = double(:expid => '0_0_0', :tree => :a_global_expression)
123
+ expression2 = double(:expid => '0_0_1', :tree => :a_local_expression)
118
124
  process.stub(:expressions => [expression1, expression2])
119
125
 
120
126
  tracker1 = described_class.new('global_tracker')
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.71
4
+ version: 0.0.72
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: 2014-02-20 00:00:00.000000000 Z
15
+ date: 2014-03-03 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: ruote
@@ -170,6 +170,7 @@ files:
170
170
  - lib/bumbleworks/entity.rb
171
171
  - lib/bumbleworks/error_handler.rb
172
172
  - lib/bumbleworks/error_logger.rb
173
+ - lib/bumbleworks/expression.rb
173
174
  - lib/bumbleworks/hash_storage.rb
174
175
  - lib/bumbleworks/participant.rb
175
176
  - lib/bumbleworks/participant_registration.rb
@@ -179,6 +180,7 @@ files:
179
180
  - lib/bumbleworks/participants/participant.rb
180
181
  - lib/bumbleworks/participants/storage_participant.rb
181
182
  - lib/bumbleworks/process.rb
183
+ - lib/bumbleworks/process/error_record.rb
182
184
  - lib/bumbleworks/process_definition.rb
183
185
  - lib/bumbleworks/rake_tasks.rb
184
186
  - lib/bumbleworks/ruote.rb
@@ -215,6 +217,7 @@ files:
215
217
  - spec/fixtures/definitions/nested_folder/test_nested_process.rb
216
218
  - spec/fixtures/definitions/test_process.rb
217
219
  - spec/fixtures/entities/rainbow_loom.rb
220
+ - spec/fixtures/participants/naughty_participant.rb
218
221
  - spec/fixtures/trackers.rb
219
222
  - spec/integration/entity_spec.rb
220
223
  - spec/integration/example_configurations_spec.rb
@@ -224,12 +227,14 @@ files:
224
227
  - spec/lib/bumbleworks/entity_spec.rb
225
228
  - spec/lib/bumbleworks/error_handler_spec.rb
226
229
  - spec/lib/bumbleworks/error_logger_spec.rb
230
+ - spec/lib/bumbleworks/expression_spec.rb
227
231
  - spec/lib/bumbleworks/hash_storage_spec.rb
228
232
  - spec/lib/bumbleworks/participant/base_spec.rb
229
233
  - spec/lib/bumbleworks/participant/entity_interactor_spec.rb
230
234
  - spec/lib/bumbleworks/participant/error_dispatcher_spec.rb
231
235
  - spec/lib/bumbleworks/participant/local_participant_spec.rb
232
236
  - spec/lib/bumbleworks/participant_registration_spec.rb
237
+ - spec/lib/bumbleworks/process/error_record_spec.rb
233
238
  - spec/lib/bumbleworks/process_definition_spec.rb
234
239
  - spec/lib/bumbleworks/process_spec.rb
235
240
  - spec/lib/bumbleworks/ruote/exp/broadcast_event_expression_spec.rb
@@ -266,7 +271,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
266
271
  version: '0'
267
272
  segments:
268
273
  - 0
269
- hash: 53431836910523847
274
+ hash: -3659689790798964412
270
275
  required_rubygems_version: !ruby/object:Gem::Requirement
271
276
  none: false
272
277
  requirements:
@@ -275,7 +280,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
275
280
  version: '0'
276
281
  segments:
277
282
  - 0
278
- hash: 53431836910523847
283
+ hash: -3659689790798964412
279
284
  requirements: []
280
285
  rubyforge_project:
281
286
  rubygems_version: 1.8.23
@@ -300,6 +305,7 @@ test_files:
300
305
  - spec/fixtures/definitions/nested_folder/test_nested_process.rb
301
306
  - spec/fixtures/definitions/test_process.rb
302
307
  - spec/fixtures/entities/rainbow_loom.rb
308
+ - spec/fixtures/participants/naughty_participant.rb
303
309
  - spec/fixtures/trackers.rb
304
310
  - spec/integration/entity_spec.rb
305
311
  - spec/integration/example_configurations_spec.rb
@@ -309,12 +315,14 @@ test_files:
309
315
  - spec/lib/bumbleworks/entity_spec.rb
310
316
  - spec/lib/bumbleworks/error_handler_spec.rb
311
317
  - spec/lib/bumbleworks/error_logger_spec.rb
318
+ - spec/lib/bumbleworks/expression_spec.rb
312
319
  - spec/lib/bumbleworks/hash_storage_spec.rb
313
320
  - spec/lib/bumbleworks/participant/base_spec.rb
314
321
  - spec/lib/bumbleworks/participant/entity_interactor_spec.rb
315
322
  - spec/lib/bumbleworks/participant/error_dispatcher_spec.rb
316
323
  - spec/lib/bumbleworks/participant/local_participant_spec.rb
317
324
  - spec/lib/bumbleworks/participant_registration_spec.rb
325
+ - spec/lib/bumbleworks/process/error_record_spec.rb
318
326
  - spec/lib/bumbleworks/process_definition_spec.rb
319
327
  - spec/lib/bumbleworks/process_spec.rb
320
328
  - spec/lib/bumbleworks/ruote/exp/broadcast_event_expression_spec.rb