ruote 2.1.10 → 2.1.11

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.
Files changed (94) hide show
  1. data/CHANGELOG.txt +51 -1
  2. data/CREDITS.txt +9 -0
  3. data/README.rdoc +13 -0
  4. data/Rakefile +50 -21
  5. data/TODO.txt +42 -4
  6. data/examples/pong.rb +37 -0
  7. data/lib/ruote/context.rb +19 -9
  8. data/lib/ruote/engine/process_error.rb +10 -0
  9. data/lib/ruote/engine/process_status.rb +140 -41
  10. data/lib/ruote/engine.rb +394 -27
  11. data/lib/ruote/exp/command.rb +2 -0
  12. data/lib/ruote/exp/fe_concurrence.rb +8 -0
  13. data/lib/ruote/exp/fe_concurrent_iterator.rb +3 -0
  14. data/lib/ruote/exp/fe_cursor.rb +48 -4
  15. data/lib/ruote/exp/fe_iterator.rb +40 -0
  16. data/lib/ruote/exp/fe_listen.rb +3 -3
  17. data/lib/ruote/exp/fe_participant.rb +30 -12
  18. data/lib/ruote/exp/fe_ref.rb +126 -0
  19. data/lib/ruote/exp/fe_subprocess.rb +20 -1
  20. data/lib/ruote/exp/fe_wait.rb +4 -1
  21. data/lib/ruote/exp/fe_when.rb +7 -10
  22. data/lib/ruote/exp/flowexpression.rb +23 -12
  23. data/lib/ruote/exp/ro_attributes.rb +5 -8
  24. data/lib/ruote/exp/ro_variables.rb +4 -2
  25. data/lib/ruote/fei.rb +2 -0
  26. data/lib/ruote/id/wfid_generator.rb +1 -1
  27. data/lib/ruote/log/pretty.rb +137 -0
  28. data/lib/ruote/log/storage_history.rb +1 -1
  29. data/lib/ruote/log/test_logger.rb +51 -126
  30. data/lib/ruote/log/wait_logger.rb +8 -13
  31. data/lib/ruote/parser/ruby_dsl.rb +4 -4
  32. data/lib/ruote/parser.rb +2 -2
  33. data/lib/ruote/part/block_participant.rb +1 -1
  34. data/lib/ruote/part/engine_participant.rb +1 -1
  35. data/lib/ruote/part/storage_participant.rb +27 -28
  36. data/lib/ruote/part/template.rb +8 -3
  37. data/lib/ruote/receiver/base.rb +24 -6
  38. data/lib/ruote/storage/base.rb +76 -11
  39. data/lib/ruote/storage/fs_storage.rb +10 -0
  40. data/lib/ruote/storage/hash_storage.rb +19 -8
  41. data/lib/ruote/{part → svc}/dispatch_pool.rb +3 -2
  42. data/lib/ruote/svc/dollar_sub.rb +265 -0
  43. data/lib/ruote/{error_handler.rb → svc/error_handler.rb} +6 -1
  44. data/lib/ruote/{exp → svc}/expression_map.rb +31 -37
  45. data/lib/ruote/{part → svc}/participant_list.rb +165 -25
  46. data/lib/ruote/{evt → svc}/tracker.rb +0 -0
  47. data/lib/ruote/{util → svc}/treechecker.rb +0 -0
  48. data/lib/ruote/util/look.rb +4 -1
  49. data/lib/ruote/util/ometa.rb +21 -5
  50. data/lib/ruote/{subprocess.rb → util/subprocess.rb} +0 -0
  51. data/lib/ruote/version.rb +1 -1
  52. data/lib/ruote/worker.rb +29 -69
  53. data/lib/ruote/workitem.rb +28 -1
  54. data/ruote.gemspec +26 -22
  55. data/test/functional/base.rb +3 -0
  56. data/test/functional/concurrent_base.rb +1 -0
  57. data/test/functional/crunner.sh +1 -1
  58. data/test/functional/ct_0_concurrence.rb +6 -0
  59. data/test/functional/ct_1_iterator.rb +3 -0
  60. data/test/functional/ct_2_cancel.rb +5 -0
  61. data/test/functional/eft_13_iterator.rb +39 -4
  62. data/test/functional/eft_14_cursor.rb +39 -0
  63. data/test/functional/eft_30_ref.rb +140 -0
  64. data/test/functional/eft_3_participant.rb +25 -23
  65. data/test/functional/ft_10_dollar.rb +17 -1
  66. data/test/functional/ft_14_re_apply.rb +76 -0
  67. data/test/functional/ft_1_process_status.rb +170 -29
  68. data/test/functional/ft_20_storage_participant.rb +14 -0
  69. data/test/functional/ft_24_block_participants.rb +1 -1
  70. data/test/functional/ft_26_participant_timeout.rb +93 -0
  71. data/test/functional/ft_2_errors.rb +24 -17
  72. data/test/functional/ft_30_smtp_participant.rb +7 -2
  73. data/test/functional/ft_38_participant_more.rb +15 -0
  74. data/test/functional/ft_39_wait_for.rb +34 -1
  75. data/test/functional/ft_3_participant_registration.rb +270 -2
  76. data/test/functional/ft_40_wait_logger.rb +61 -0
  77. data/test/functional/ft_42_storage_copy.rb +4 -0
  78. data/test/functional/{ft_40_participant_on_reply.rb → ft_43_participant_on_reply.rb} +17 -0
  79. data/test/functional/ft_44_var_participant.rb +35 -0
  80. data/test/functional/ft_45_participant_accept.rb +64 -0
  81. data/test/functional/ft_46_launch_single.rb +49 -0
  82. data/test/functional/ft_5_on_error.rb +39 -1
  83. data/test/functional/storage_helper.rb +7 -1
  84. data/test/test_helper.rb +1 -1
  85. data/test/unit/storage.rb +105 -32
  86. data/test/unit/ut_0_ruby_parser.rb +31 -1
  87. data/test/unit/ut_16_parser.rb +20 -0
  88. data/test/unit/ut_19_part_template.rb +11 -1
  89. data/test/unit/ut_20_composite_storage.rb +1 -1
  90. data/test/unit/ut_4_expmap.rb +1 -1
  91. data/test/unit/ut_6_condition.rb +2 -2
  92. metadata +112 -74
  93. data/lib/ruote/exp/raw.rb +0 -44
  94. data/lib/ruote/util/dollar.rb +0 -193
@@ -52,7 +52,7 @@ module Ruote
52
52
 
53
53
  def by_process (wfid)
54
54
 
55
- @context.storage.get_many('history', /!#{wfid}$/)
55
+ @context.storage.get_many('history', wfid)
56
56
  end
57
57
  alias :by_wfid :by_process
58
58
 
@@ -22,13 +22,15 @@
22
22
  # Made in Japan.
23
23
  #++
24
24
 
25
- #require 'ruote/util/tree'
25
+ require 'ruote/log/pretty'
26
26
 
27
27
 
28
28
  module Ruote
29
29
 
30
30
  class TestLogger
31
31
 
32
+ include PrettyLogging
33
+
32
34
  attr_reader :seen
33
35
  attr_reader :log
34
36
 
@@ -52,20 +54,15 @@ module Ruote
52
54
 
53
55
  @seen = []
54
56
  @log = []
55
- @waiting = nil
57
+ @waiting = []
56
58
 
57
59
  @count = -1
58
60
  @color = 33
59
61
  @noisy = false
60
-
61
- # NOTE
62
- # in case of troubles, why not have the wait_for has an event ?
63
62
  end
64
63
 
65
64
  def notify (msg)
66
65
 
67
- #@context.storage.put(event.merge('type' => 'archived_msgs'))
68
-
69
66
  puts(pretty_print(msg)) if @noisy
70
67
 
71
68
  @seen << msg
@@ -74,13 +71,26 @@ module Ruote
74
71
  check_waiting
75
72
  end
76
73
 
74
+ # Blocks until one or more interests are satisfied.
75
+ #
76
+ # interests must be an array of interests. Please refer to
77
+ # Engine#wait_for documentation for allowed values of each interest.
78
+ #
79
+ # If multiple interests are given, wait_for blocks until
80
+ # all of the interests are satisfied.
81
+ #
82
+ # wait_for may only be used by one thread at a time. If one
83
+ # thread calls wait_for and later another thread calls wait_for
84
+ # while the first thread is waiting, the first thread's
85
+ # interests are lost and the first thread will never wake up.
86
+ #
77
87
  def wait_for (interests)
78
88
 
79
- @waiting = [ Thread.current, interests ]
89
+ @waiting << [ Thread.current, interests ]
80
90
 
81
91
  check_waiting
82
92
 
83
- Thread.stop if @waiting
93
+ Thread.stop if @waiting.find { |w| w.first == Thread.current }
84
94
 
85
95
  # and when this thread gets woken up, go on and return __result__
86
96
 
@@ -109,39 +119,52 @@ module Ruote
109
119
 
110
120
  def check_waiting
111
121
 
112
- return unless @waiting
122
+ return if @waiting.size < 1
113
123
 
114
124
  while msg = @seen.shift
115
-
116
- break if check_msg(msg)
125
+ check_msg(msg)
117
126
  end
118
127
  end
119
128
 
120
129
  def check_msg (msg)
121
130
 
122
- if check_interest(msg)
131
+ wakeup = []
123
132
 
124
- thread = @waiting.first
125
- @waiting = nil
126
- thread['__result__'] = msg
127
- thread.wakeup
133
+ @waiting.each do |thread, interests|
128
134
 
129
- true
130
- else
135
+ wakeup << thread if matches(interests, msg)
136
+ end
131
137
 
132
- false
138
+ @waiting.delete_if { |t, i| i.size < 1 }
139
+
140
+ wakeup.each do |thread|
141
+
142
+ thread['__result__'] = msg
143
+ thread.wakeup
133
144
  end
134
145
  end
135
146
 
136
147
  FINAL_ACTIONS = %w[ terminated ceased error_intercepted ]
137
148
 
138
- def check_interest (msg)
149
+ # Checks whether message msg matches any of interests being waited for.
150
+ #
151
+ # Some interests look for actions on particular workflows (e.g.,
152
+ # waiting for some workflow to finish). Other interests are not
153
+ # attached to any particular workflow (e.g., :inactive waits until
154
+ # the engine finishes processing all active and pending workflows)
155
+ # but are still satisfied when actions happen on workflows (e.g.,
156
+ # the last workflow being run finishes).
157
+ #
158
+ # Returns true if all interests being waited for have been satisfied,
159
+ # false otherwise.
160
+ #
161
+ def matches (interests, msg)
139
162
 
140
163
  action = msg['action']
141
164
 
142
- @waiting.last.each do |interest|
165
+ interests.each do |interest|
143
166
 
144
- satisfied = if interest == :inactive
167
+ satisfied = if interest == :inactive
145
168
 
146
169
  (FINAL_ACTIONS.include?(action) && @context.worker.inactive?)
147
170
 
@@ -155,9 +178,10 @@ module Ruote
155
178
 
156
179
  elsif interest.is_a?(Fixnum)
157
180
 
158
- @waiting[-1] = @waiting[-1] - [ interest ]
181
+ interests.delete(interest)
182
+
159
183
  if (interest > 1)
160
- @waiting[-1] << (interest - 1)
184
+ interests << (interest - 1)
161
185
  false
162
186
  else
163
187
  true
@@ -168,109 +192,10 @@ module Ruote
168
192
  (FINAL_ACTIONS.include?(action) && msg['wfid'] == interest)
169
193
  end
170
194
 
171
- @waiting[-1] = @waiting[-1] - [ interest ] if satisfied
172
- end
173
-
174
- @waiting.last.size < 1
175
- end
176
-
177
- # <ESC>[{attr1};...;{attrn}m
178
- #
179
- # 0 Reset all attributes
180
- # 1 Bright
181
- # 2 Dim
182
- # 4 Underscore
183
- # 5 Blink
184
- # 7 Reverse
185
- # 8 Hidden
186
- #
187
- # Foreground Colours
188
- # 30 Black
189
- # 31 Red
190
- # 32 Green
191
- # 33 Yellow
192
- # 34 Blue
193
- # 35 Magenta
194
- # 36 Cyan
195
- # 37 White
196
- #
197
- # Background Colours
198
- # 40 Black
199
- # 41 Red
200
- # 42 Green
201
- # 43 Yellow
202
- # 44 Blue
203
- # 45 Magenta
204
- # 46 Cyan
205
- # 47 White
206
-
207
- def color (mod, s, clear=false)
208
-
209
- return s if Ruote::WIN
210
- return s unless STDOUT.tty?
211
-
212
- "[#{mod}m#{s}#{clear ? '' : "[#{@color}m"}"
213
- end
214
-
215
- def pretty_print (msg)
216
-
217
- @count += 1
218
- @count = 0 if @count > 9
219
-
220
- ei = self.object_id.to_s[-2..-1]
221
-
222
- fei = msg['fei']
223
- depth = fei ? fei['expid'].split('_').size : 0
224
-
225
- i = fei ?
226
- [ fei['wfid'], fei['sub_wfid'], fei['expid'] ].join(' ') :
227
- msg['wfid']
228
-
229
- rest = msg.dup
230
- %w[
231
- _id put_at _rev
232
- type action
233
- fei wfid variables
234
- ].each { |k| rest.delete(k) }
235
-
236
- if v = rest['parent_id']
237
- rest['parent_id'] = Ruote.to_storage_id(v)
238
- end
239
- if v = rest.delete('workitem')
240
- rest[:wi] = [
241
- v['fei'] ? Ruote.to_storage_id(v['fei']) : nil,
242
- v['fields'].size ]
243
- end
244
-
245
- { 'tree' => :t, 'parent_id' => :pi }.each do |k0, k1|
246
- if v = rest.delete(k0)
247
- rest[k1] = v
248
- end
249
- end
250
-
251
- action = msg['action'][0, 2]
252
- action = case msg['action']
253
- when 'receive' then 'rc'
254
- when 'dispatched' then 'dd'
255
- when 'dispatch_cancel' then 'dc'
256
- else action
257
- end
258
- action = case action
259
- when 'la' then color('4;32', action)
260
- when 'te' then color('4;31', action)
261
- when 'ce' then color('31', action)
262
- when 'ca' then color('31', action)
263
- when 'rc' then color('4;33', action)
264
- when 'di' then color('4;33', action)
265
- when 'dd' then color('4;33', action)
266
- when 'dc' then color('4;31', action)
267
- else action
195
+ interests.delete(interest) if satisfied
268
196
  end
269
197
 
270
- color(
271
- @color,
272
- "#{@count} #{ei} #{' ' * depth}#{action} * #{i} #{rest.inspect}",
273
- true)
198
+ interests.size < 1
274
199
  end
275
200
  end
276
201
  end
@@ -37,33 +37,28 @@ module Ruote
37
37
  def initialize (context)
38
38
 
39
39
  @context = context
40
- @waiting = nil
41
40
  @color = 33
42
41
 
43
42
  @context.worker.subscribe(:all, self) if @context.worker
44
43
 
45
44
  @noisy = false
46
45
  @count = -1
46
+
47
+ @seen = []
48
+ @waiting = []
47
49
  end
48
50
 
49
51
  def notify (msg)
50
52
 
51
53
  puts(pretty_print(msg)) if @noisy
52
54
 
53
- return unless @waiting
54
-
55
- check_msg(msg)
56
- end
57
-
58
- def wait_for (interests)
59
-
60
- @waiting = [ Thread.current, interests ]
61
-
62
- Thread.stop
55
+ #return if @waiting.size < 1
56
+ #check_msg(msg)
63
57
 
64
- # and when this thread gets woken up, go on and return __result__
58
+ @seen << msg
59
+ @seen.shift if @seen.size > 147
65
60
 
66
- Thread.current['__result__']
61
+ check_waiting
67
62
  end
68
63
  end
69
64
  end
@@ -22,6 +22,8 @@
22
22
  # Made in Japan.
23
23
  #++
24
24
 
25
+ require 'ruote/util/ometa'
26
+
25
27
 
26
28
  module Ruote
27
29
 
@@ -94,7 +96,7 @@ module Ruote
94
96
  #
95
97
  module RubyDsl
96
98
 
97
- class BranchContext
99
+ class BranchContext < Ruote::BlankSlate
98
100
 
99
101
  def initialize (name, attributes)
100
102
 
@@ -117,9 +119,7 @@ module Ruote
117
119
 
118
120
  def self.create_branch (name, attributes, &block)
119
121
 
120
- while name[0, 1] == '_'
121
- name = name[1..-1]
122
- end
122
+ name = name[1..-1] while name[0, 1] == '_'
123
123
 
124
124
  h = attributes.inject({}) { |h1, a|
125
125
  a.is_a?(Hash) ? h1.merge!(a) : h1[a] = nil
data/lib/ruote/parser.rb CHANGED
@@ -55,7 +55,7 @@ module Ruote
55
55
  (return Rufus::Json.decode(definition)) rescue nil
56
56
  (return ruby_eval(definition)) rescue nil
57
57
 
58
- if definition.index("\n") == nil
58
+ if definition.index("\n").nil? && definition.index(' ').nil?
59
59
 
60
60
  raise ArgumentError.new(
61
61
  "remote process definitions are not allowed"
@@ -77,7 +77,7 @@ module Ruote
77
77
  unless @parser
78
78
 
79
79
  require 'ostruct'
80
- require 'ruote/util/treechecker'
80
+ require 'ruote/svc/treechecker'
81
81
 
82
82
  @parser = Ruote::Parser.new(
83
83
  OpenStruct.new('treechecker' => Ruote::TreeChecker.new({})))
@@ -45,7 +45,7 @@ module Ruote
45
45
  # end
46
46
  #
47
47
  #
48
- # == do_not_thead
48
+ # == do_not_thread
49
49
  #
50
50
  # By default, this participant (like most other participants) is executed
51
51
  # in its own thread (in a Ruby runtime where EventMachine is running,
@@ -22,7 +22,7 @@
22
22
  # Made in Singapore.
23
23
  #++
24
24
 
25
- require 'ruote/subprocess'
25
+ require 'ruote/util/subprocess'
26
26
  require 'ruote/part/local_participant'
27
27
 
28
28
 
@@ -138,7 +138,7 @@ module Ruote
138
138
  #
139
139
  def size
140
140
 
141
- fetch_all.size
141
+ fetch_all(:count => true)
142
142
  end
143
143
 
144
144
  # Iterates over the workitems stored in here.
@@ -150,9 +150,9 @@ module Ruote
150
150
 
151
151
  # Returns all the workitems stored in here.
152
152
  #
153
- def all
153
+ def all (opts={})
154
154
 
155
- fetch_all.map { |hwi| Ruote::Workitem.new(hwi) }
155
+ fetch_all(opts).map { |hwi| Ruote::Workitem.new(hwi) }
156
156
  end
157
157
 
158
158
  # A convenience method (especially when testing), returns the first
@@ -169,22 +169,24 @@ module Ruote
169
169
  #
170
170
  def by_wfid (wfid)
171
171
 
172
- @context.storage.get_many('workitems', /!#{wfid}$/).map { |hwi|
172
+ @context.storage.get_many('workitems', wfid).collect { |hwi|
173
173
  Ruote::Workitem.new(hwi)
174
174
  }
175
175
  end
176
176
 
177
177
  # Returns all workitems for the specified participant name
178
178
  #
179
- def by_participant (participant_name)
179
+ def by_participant (participant_name, opts={})
180
180
 
181
181
  hwis = if @context.storage.respond_to?(:by_participant)
182
182
 
183
- @context.storage.by_participant('workitems', participant_name)
183
+ @context.storage.by_participant('workitems', participant_name, opts)
184
184
 
185
185
  else
186
186
 
187
- fetch_all.select { |wi| wi['participant_name'] == participant_name }
187
+ fetch_all(opts).select { |wi|
188
+ wi['participant_name'] == participant_name
189
+ }
188
190
  end
189
191
 
190
192
  hwis.collect { |hwi| Ruote::Workitem.new(hwi) }
@@ -230,7 +232,7 @@ module Ruote
230
232
  # constraints for fields.
231
233
  #
232
234
  # 'offset' and 'limit' are reserved as well. They should prove useful
233
- # for pagination.
235
+ # for pagination. 'skip' can be used instead of 'offset'.
234
236
  #
235
237
  # Note : the criteria is AND only, you'll have to do ORs (aggregation)
236
238
  # by yourself.
@@ -239,32 +241,28 @@ module Ruote
239
241
 
240
242
  cr = criteria.inject({}) { |h, (k, v)| h[k.to_s] = v; h }
241
243
 
242
- return @context.storage.query_workitems(cr).collect { |h|
243
- Ruote::Workitem.new(h)
244
- } if @context.storage.respond_to?(:query_workitems)
244
+ if @context.storage.respond_to?(:query_workitems)
245
+ return @context.storage.query_workitems(cr)
246
+ end
245
247
 
246
- offset = cr.delete('offset')
247
- limit = cr.delete('limit')
248
+ opts = {}
249
+ opts[:skip] = cr.delete('offset') || cr.delete('skip')
250
+ opts[:limit] = cr.delete('limit')
251
+ opts[:count] = cr.delete('count')
248
252
 
249
253
  wfid = cr.delete('wfid')
250
254
  pname = cr.delete('participant_name') || cr.delete('participant')
251
255
 
252
- hwis = if wfid
253
- @context.storage.get_many('workitems', /!#{wfid}$/)
254
- else
255
- fetch_all
256
- end
256
+ hwis = wfid ?
257
+ @context.storage.get_many('workitems', wfid, opts) : fetch_all(opts)
257
258
 
258
- hwis = hwis.select { |hwi|
259
+ return hwis if opts[:count]
260
+
261
+ hwis.select { |hwi|
259
262
  Ruote::StorageParticipant.matches?(hwi, pname, cr)
260
263
  }.collect { |hwi|
261
264
  Ruote::Workitem.new(hwi)
262
265
  }
263
-
264
- offset = offset || 0
265
- limit = limit || hwis.length
266
-
267
- hwis[offset, limit]
268
266
  end
269
267
 
270
268
  # Cleans this participant out completely
@@ -294,11 +292,12 @@ module Ruote
294
292
  # Fetches all the workitems. If there is a @store_name, will only fetch
295
293
  # the workitems in that store.
296
294
  #
297
- def fetch_all
298
-
299
- key = @store_name ? /^wi!#{@store_name}::/ : nil
295
+ def fetch_all (opts={})
300
296
 
301
- @context.storage.get_many('workitems', key)
297
+ @context.storage.get_many(
298
+ 'workitems',
299
+ @store_name ? /^wi!#{@store_name}::/ : nil,
300
+ opts)
302
301
  end
303
302
 
304
303
  # Computes the id for the document representing the document in the storage.
@@ -23,7 +23,6 @@
23
23
  #++
24
24
 
25
25
  require 'rufus/json'
26
- require 'ruote/util/dollar'
27
26
 
28
27
 
29
28
  module Ruote
@@ -34,8 +33,14 @@ module Ruote
34
33
  # (Currently only used by the SmtpParticipant, could prove useful for
35
34
  # custom participants)
36
35
  #
36
+ # This module expects to find the ruote @context available (probably
37
+ # accessible thanks to the Ruote::LocalParticipant module, see
38
+ # Ruote::SmtpParticipant as an example).
39
+ #
37
40
  module TemplateMixin
38
41
 
42
+ # Do the rendering.
43
+ #
39
44
  def render_template (template, flow_expression, workitem)
40
45
 
41
46
  template = (File.read(template) rescue nil) if is_a_file?(template)
@@ -45,7 +50,7 @@ module Ruote
45
50
  template = template.to_s
46
51
  workitem = workitem.to_h if workitem.respond_to?(:to_h)
47
52
 
48
- Ruote.dosub(template, flow_expression, workitem)
53
+ @context.dollar_sub.s(template, flow_expression, workitem)
49
54
  end
50
55
 
51
56
  # Simply returns a pretty-printed view of the workitem
@@ -57,7 +62,7 @@ module Ruote
57
62
  s = []
58
63
  s << "workitem for #{workitem['participant_name']}"
59
64
  s << ''
60
- s << Rufus::Json.encode(workitem['fei'])
65
+ s << Rufus::Json.pretty_encode(workitem['fei'])
61
66
  s << ''
62
67
  workitem['fields'].keys.sort.each do |key|
63
68
  s << " - '#{key}' ==> #{Rufus::Json.encode(workitem['fields'][key])}"
@@ -55,6 +55,14 @@ module Ruote
55
55
  # This method is mostly used from the Ruote::Engine class (which includes
56
56
  # this mixin).
57
57
  #
58
+ # process_definition must be a result of Ruote.process_definition call
59
+ # or XML or JSON serialized process definition, as accepted by
60
+ # Ruote::Parser#parse.
61
+ #
62
+ # fields are workflow parameters that will be placed in workitem.fields.
63
+ #
64
+ # variables contain engine variables.
65
+ #
58
66
  def launch (process_definition, fields={}, variables={})
59
67
 
60
68
  wfid = @context.wfidgen.generate
@@ -98,16 +106,26 @@ module Ruote
98
106
  self.class.to_s
99
107
  end
100
108
 
101
- protected
102
-
103
- # Convenience method, fetches the flow expression (ParticipantExpression)
104
- # that emitted that workitem.
109
+ # Convenience method, given a workitem or a fei, returns the
110
+ # corresponding flow expession.
105
111
  #
106
- def fetch_flow_expression (workitem)
112
+ def fetch_flow_expression (workitem_or_fei)
107
113
 
108
- Ruote::Exp::FlowExpression.fetch(@context, workitem.fei.to_h)
114
+ Ruote::Exp::FlowExpression.fetch(
115
+ @context,
116
+ Ruote::FlowExpressionId.extract_h(workitem_or_fei))
109
117
  end
110
118
 
119
+ # For example :
120
+ #
121
+ # fexp = engine.fexp(fei)
122
+ # # or
123
+ # fexp = engine.fexp(workitem)
124
+ #
125
+ alias fexp fetch_flow_expression
126
+
127
+ protected
128
+
111
129
  # Stashes values in the participant expression (in the storage).
112
130
  #
113
131
  # put(workitem.fei, 'key' => 'value', 'colour' => 'blue')