bumbleworks 0.0.71 → 0.0.72

Sign up to get free protection for your applications and to get access to all the features.
@@ -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