rbpm 0.0.1
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.
- data/README +18 -0
- data/docs/sub_processes.png +0 -0
- data/docs/todo.txt +12 -0
- data/lib/rbpm.rb +590 -0
- data/test/rbpm_action_tests.rb +148 -0
- data/test/rbpm_call_tests.rb +125 -0
- data/test/rbpm_condition_tests.rb +28 -0
- data/test/rbpm_ctxvars_tests.rb +40 -0
- data/test/rbpm_exceptions_tests.rb +79 -0
- data/test/rbpm_fork_join_tests.rb +72 -0
- data/test/rbpm_simple_tests.rb +103 -0
- data/test/rbpm_state_tests.rb +61 -0
- data/test/rbpm_transition_tests.rb +94 -0
- data/test/rbpm_versionning_tests.rb +44 -0
- data/test/ruby_lang_tests.rb +116 -0
- data/test/ts_all.rb +14 -0
- metadata +54 -0
data/README
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
what is rBPM?
|
2
|
+
|
3
|
+
rbpm is a (JBoss-)jBPM-like lightweight workflow framework. it helps you to
|
4
|
+
develop asynchronous programs. developers describe their process definitions
|
5
|
+
(workflows) using extended ruby class definition syntax - no use of xml
|
6
|
+
files is planned.
|
7
|
+
|
8
|
+
license terms?
|
9
|
+
|
10
|
+
LGPL
|
11
|
+
|
12
|
+
author?
|
13
|
+
|
14
|
+
(c) 2005 by Christian Tschenett
|
15
|
+
|
16
|
+
getting startet...
|
17
|
+
|
18
|
+
take a look at the unit tests
|
Binary file
|
data/docs/todo.txt
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
[ok] workflow instance persistence
|
2
|
+
[ok] states
|
3
|
+
[ok] context variables
|
4
|
+
[ok] versioning
|
5
|
+
[ok] more nodes (join, fork)
|
6
|
+
[ok] more nodes (call)
|
7
|
+
[ok] conditions
|
8
|
+
[~ok] consistent and clean meta syntax
|
9
|
+
[~ok] clean block support
|
10
|
+
[pending] rbpm server
|
11
|
+
[pending] rpbm server java/web service interface
|
12
|
+
[ok] workflow support for exception handling
|
data/lib/rbpm.rb
ADDED
@@ -0,0 +1,590 @@
|
|
1
|
+
class WorkflowVersionManager
|
2
|
+
def self.find_workflow_versions(workflow_name)
|
3
|
+
versions = []
|
4
|
+
ObjectSpace.each_object(Module) do |m|
|
5
|
+
module_name = m.to_s
|
6
|
+
if module_name =~ /#{workflow_name}Version([0-9]+)$/
|
7
|
+
versions << $1.to_i
|
8
|
+
end
|
9
|
+
end
|
10
|
+
versions
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.create_workflow_instance(workflow_name, version = :latest)
|
14
|
+
workflow_name = workflow_name.to_s if workflow_name.is_a? Symbol
|
15
|
+
if :latest == version
|
16
|
+
versions = find_workflow_versions(workflow_name)
|
17
|
+
if versions.empty?
|
18
|
+
#fallback to non-versionned wf
|
19
|
+
return eval("#{workflow_name}.new")
|
20
|
+
else
|
21
|
+
version = versions.max
|
22
|
+
end
|
23
|
+
end
|
24
|
+
return eval("#{workflow_name}Version#{version}::#{workflow_name}.new")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Workflow
|
29
|
+
|
30
|
+
##WORKFLOW METADATA
|
31
|
+
|
32
|
+
#@@meta will be a class attribute of a concrete Workflow
|
33
|
+
def self.init_meta
|
34
|
+
module_eval <<-end_eval
|
35
|
+
unless defined? @@meta
|
36
|
+
@@meta = {}
|
37
|
+
def self.meta
|
38
|
+
@@meta
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end_eval
|
42
|
+
end
|
43
|
+
|
44
|
+
#insert generic node helper...
|
45
|
+
|
46
|
+
def self.node(node_sym, options = {})
|
47
|
+
self.init_meta
|
48
|
+
self.nodes[node_sym] = {} unless self.nodes[node_sym]
|
49
|
+
options.each do |key,value|
|
50
|
+
case key.to_s
|
51
|
+
when /(.*)_trans$|trans$/
|
52
|
+
trans = $1.nil? ? "default" : $1
|
53
|
+
transition(node_sym, trans.to_sym, value)
|
54
|
+
when /(.*)_trans_action$|trans_action$/
|
55
|
+
trans = $1.nil? ? "default" : $1
|
56
|
+
transition_action(node_sym, trans.to_sym, value)
|
57
|
+
when /(.*)_trans_cond$|trans_cond$/
|
58
|
+
trans = $1.nil? ? "default" : $1
|
59
|
+
transition_condition(node_sym, trans.to_sym, value)
|
60
|
+
when "exception"
|
61
|
+
exception_transition(node_sym, value)
|
62
|
+
when "action"
|
63
|
+
node_action(node_sym, value)
|
64
|
+
when "on_enter"
|
65
|
+
node_enter_action(node_sym, value)
|
66
|
+
when "on_leave"
|
67
|
+
node_leave_action(node_sym, value)
|
68
|
+
when "action_singleton"
|
69
|
+
node_action_singleton(node_sym, value)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
#insert special nodes helpers...
|
75
|
+
|
76
|
+
def self.start_node(node_sym, options = {})
|
77
|
+
node(node_sym, options)
|
78
|
+
nodes[node_sym][:start] = true
|
79
|
+
end
|
80
|
+
|
81
|
+
def self.end_node(node_sym, options = {})
|
82
|
+
node(node_sym, options)
|
83
|
+
nodes[node_sym][:end] = true
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.state_node(node_sym, options = {})
|
87
|
+
node(node_sym, options)
|
88
|
+
nodes[node_sym][:state] = true
|
89
|
+
end
|
90
|
+
|
91
|
+
def self.join_node(node_sym, options = {})
|
92
|
+
options[:action_singleton] = JoinNode.new
|
93
|
+
node(node_sym, options)
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.fork_node(node_sym, options = {})
|
97
|
+
options[:action_singleton] = ForkNode.new(options[:children_ctx])
|
98
|
+
node(node_sym, options)
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.call_node(node_sym, options = {})
|
102
|
+
options[:action_singleton] = CallNode.new(options[:workflow], options[:workflow_version], options[:call_in], options[:call_out])
|
103
|
+
node(node_sym, options)
|
104
|
+
end
|
105
|
+
|
106
|
+
#insert node action helpers...
|
107
|
+
|
108
|
+
def self.enter(node_sym, &blk)
|
109
|
+
node_enter_action(node_sym, blk)
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.node_enter_action(node_sym, action)
|
113
|
+
nodes[node_sym][:on_enter] = action
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.action(node_sym, &blk)
|
117
|
+
node_action(node_sym, blk)
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.node_action(node_sym, action)
|
121
|
+
nodes[node_sym][:action] = action
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.leave(node_sym, &blk)
|
125
|
+
node_leave_action(node_sym, blk)
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.node_leave_action(node_sym, action)
|
129
|
+
nodes[node_sym][:on_leave] = action
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.node_action_singleton(node_sym, instance)
|
133
|
+
nodes[node_sym][:action_singleton] = instance
|
134
|
+
end
|
135
|
+
|
136
|
+
#insert transition, transition action and transition condition helpers...
|
137
|
+
|
138
|
+
def self.transition(node_from_sym, trans_sym, node_to_sym)
|
139
|
+
transitions(node_from_sym)[trans_sym] = node_to_sym
|
140
|
+
end
|
141
|
+
|
142
|
+
def self.transition_action(node_from_sym, trans_sym, action)
|
143
|
+
transition_actions(node_from_sym)[trans_sym] = action
|
144
|
+
end
|
145
|
+
|
146
|
+
def self.transition_condition(node_from_sym, trans_sym, condition)
|
147
|
+
transition_conditions(node_from_sym)[trans_sym] = condition
|
148
|
+
end
|
149
|
+
|
150
|
+
def self.exception_transition(node_from_sym, node_to_sym)
|
151
|
+
transition(node_from_sym, :exception, node_to_sym)
|
152
|
+
transition_condition(node_from_sym, :exception, "false") #only executed if "hardcoded" signalled
|
153
|
+
end
|
154
|
+
|
155
|
+
#metadata access helpers...
|
156
|
+
|
157
|
+
def self.nodes
|
158
|
+
meta[:nodes] = {} unless meta[:nodes]
|
159
|
+
meta[:nodes]
|
160
|
+
end
|
161
|
+
|
162
|
+
def self.node_option_values(node, option)
|
163
|
+
nodes[node][option] = {} unless nodes[node][option]
|
164
|
+
nodes[node][option]
|
165
|
+
end
|
166
|
+
|
167
|
+
def self.transitions(node)
|
168
|
+
node_option_values(node, :transitions)
|
169
|
+
end
|
170
|
+
|
171
|
+
def self.transition_actions(node)
|
172
|
+
node_option_values(node, :transition_actions)
|
173
|
+
end
|
174
|
+
|
175
|
+
def self.transition_conditions(node)
|
176
|
+
node_option_values(node, :transition_conditions)
|
177
|
+
end
|
178
|
+
|
179
|
+
def self.find_first_node_with_option(option)
|
180
|
+
nodes.each do |node,options|
|
181
|
+
return node if options[option]
|
182
|
+
end
|
183
|
+
nil
|
184
|
+
end
|
185
|
+
|
186
|
+
def self.find_nodes_with_option(option)
|
187
|
+
found = nodes.collect do |node,options|
|
188
|
+
options[option] ? node : nil
|
189
|
+
end
|
190
|
+
found.compact
|
191
|
+
end
|
192
|
+
|
193
|
+
def self.find_node_with_name(name)
|
194
|
+
nodes[name.to_sym]
|
195
|
+
end
|
196
|
+
|
197
|
+
def self.find_start_node
|
198
|
+
find_first_node_with_option :start
|
199
|
+
end
|
200
|
+
|
201
|
+
def self.find_end_nodes
|
202
|
+
find_nodes_with_option :end
|
203
|
+
end
|
204
|
+
|
205
|
+
def self.end_node?(node)
|
206
|
+
nodes[node][:end]
|
207
|
+
end
|
208
|
+
|
209
|
+
@@next_token_id = 1
|
210
|
+
|
211
|
+
def self.generate_token_id
|
212
|
+
@@next_token_id += 1
|
213
|
+
end
|
214
|
+
|
215
|
+
##WORKFLOW INSTANCE
|
216
|
+
|
217
|
+
attr_writer :token_id_generator
|
218
|
+
attr_reader :tokens, :result, :token_id_generator
|
219
|
+
|
220
|
+
def initialize
|
221
|
+
@tokens = []
|
222
|
+
@result = nil
|
223
|
+
end
|
224
|
+
|
225
|
+
def clazz
|
226
|
+
self.class
|
227
|
+
end
|
228
|
+
|
229
|
+
#token management...
|
230
|
+
|
231
|
+
def generate_token_id
|
232
|
+
if token_id_generator
|
233
|
+
token_id_generator.generate_token_id
|
234
|
+
else
|
235
|
+
clazz.generate_token_id
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
def create_root_token(ctx_vars = {})
|
240
|
+
create_token(nil, ctx_vars)
|
241
|
+
end
|
242
|
+
|
243
|
+
def create_token(parent = nil, ctx_vars = {})
|
244
|
+
token = Token.new(self, parent, ctx_vars, generate_token_id)
|
245
|
+
@tokens << token
|
246
|
+
token
|
247
|
+
end
|
248
|
+
|
249
|
+
def token_done(token)
|
250
|
+
unless token.parent
|
251
|
+
@result = token.all_ctx_vars
|
252
|
+
end
|
253
|
+
|
254
|
+
@tokens.delete token
|
255
|
+
end
|
256
|
+
|
257
|
+
def find_root_token
|
258
|
+
@tokens.each do |token|
|
259
|
+
return token unless token.parent
|
260
|
+
end
|
261
|
+
nil?
|
262
|
+
end
|
263
|
+
|
264
|
+
def [](key)
|
265
|
+
if @result
|
266
|
+
@result[key]
|
267
|
+
else
|
268
|
+
find_root_token[key]
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
#(metadata-driven) reflection helpers...
|
273
|
+
|
274
|
+
def find_method(method_name)
|
275
|
+
action = method(method_name) if methods.include? method_name
|
276
|
+
|
277
|
+
unless action
|
278
|
+
action = clazz.method(method_name) if clazz.methods.include? method_name
|
279
|
+
end
|
280
|
+
|
281
|
+
action
|
282
|
+
end
|
283
|
+
|
284
|
+
def find_node_event_action(node, default_method_suffix, action_option)
|
285
|
+
default_method_name = "#{node.to_s}#{default_method_suffix}"
|
286
|
+
|
287
|
+
action_singleton = clazz.nodes[node][:action_singleton]
|
288
|
+
action = action_singleton.method(action_option) if action_singleton && action_singleton.methods.include?(action_option.to_s)
|
289
|
+
|
290
|
+
unless action
|
291
|
+
action = find_method(default_method_name)
|
292
|
+
end
|
293
|
+
|
294
|
+
unless action
|
295
|
+
action_or_name = clazz.nodes[node][action_option]
|
296
|
+
|
297
|
+
if action_or_name.is_a? Symbol
|
298
|
+
action_name = action_or_name.to_s
|
299
|
+
action = find_method(action_name)
|
300
|
+
|
301
|
+
raise "unknown custom action #{action_name}" unless action
|
302
|
+
else
|
303
|
+
action = action_or_name
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
action
|
308
|
+
end
|
309
|
+
|
310
|
+
def find_enter_action(node)
|
311
|
+
find_node_event_action(node, "_on_enter", :on_enter)
|
312
|
+
end
|
313
|
+
|
314
|
+
def find_leave_action(node)
|
315
|
+
find_node_event_action(node, "_on_leave", :on_leave)
|
316
|
+
end
|
317
|
+
|
318
|
+
def find_node_action(node)
|
319
|
+
find_node_event_action(node, "_action", :action)
|
320
|
+
end
|
321
|
+
|
322
|
+
def find_transit_action(node, transition)
|
323
|
+
default_method_name = "#{node.to_s}_#{transition.to_s}_trans_action"
|
324
|
+
action = find_method(default_method_name)
|
325
|
+
|
326
|
+
unless action
|
327
|
+
action_or_name = clazz.transition_actions(node)[transition]
|
328
|
+
|
329
|
+
if action_or_name.is_a? Symbol
|
330
|
+
action_name = action_or_name.to_s
|
331
|
+
action = find_method(action_name)
|
332
|
+
|
333
|
+
raise "unknown custom action #{action_name}" unless action
|
334
|
+
else
|
335
|
+
action = action_or_name
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
action
|
340
|
+
end
|
341
|
+
|
342
|
+
def find_transit_target(node, transition)
|
343
|
+
clazz.transitions(node)[transition]
|
344
|
+
end
|
345
|
+
|
346
|
+
#flow management...
|
347
|
+
|
348
|
+
def start(ctx_vars = {})
|
349
|
+
token = create_root_token(ctx_vars)
|
350
|
+
token.enter(clazz.find_start_node)
|
351
|
+
token
|
352
|
+
end
|
353
|
+
|
354
|
+
def call_event_action(action, token, node)
|
355
|
+
begin
|
356
|
+
action.call(token, node)
|
357
|
+
rescue Exception => exception
|
358
|
+
if clazz.transitions(node)[:exception]
|
359
|
+
token[:exception] = exception
|
360
|
+
return :exception
|
361
|
+
else
|
362
|
+
raise
|
363
|
+
end
|
364
|
+
end
|
365
|
+
return nil
|
366
|
+
end
|
367
|
+
|
368
|
+
def enter_node(token, node)
|
369
|
+
token.entered(node)
|
370
|
+
|
371
|
+
hardcoded_transition = call_event_action(find_enter_action(node), token, node) if find_enter_action(node)
|
372
|
+
|
373
|
+
if hardcoded_transition || (not (clazz.end_node?(node) || clazz.nodes[node][:state]))
|
374
|
+
signal_node(token, node, hardcoded_transition)
|
375
|
+
end
|
376
|
+
end
|
377
|
+
|
378
|
+
def signal_node(token, node, hardcoded_transition = nil)
|
379
|
+
if hardcoded_transition
|
380
|
+
transitions = [[hardcoded_transition, token]]
|
381
|
+
elsif find_node_action(node)
|
382
|
+
transitions = find_node_action(node).call(token, node)
|
383
|
+
else
|
384
|
+
clazz.transitions(node).each do |transition,target|
|
385
|
+
if (clazz.transition_conditions(node)[transition].nil? || token.eval_expr(clazz.transition_conditions(node)[transition]))
|
386
|
+
transitions = [[transition, token]]
|
387
|
+
break
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
if not clazz.nodes[node][:end] and transitions
|
393
|
+
transitions.each do |transition_token|
|
394
|
+
transition,token = transition_token
|
395
|
+
token.transit(transition)
|
396
|
+
end
|
397
|
+
end
|
398
|
+
end
|
399
|
+
|
400
|
+
def leave_node(token, node)
|
401
|
+
call_event_action(find_leave_action(node), token, node) if find_leave_action(node)
|
402
|
+
end
|
403
|
+
|
404
|
+
def transit(token, node, transition)
|
405
|
+
hardcoded_transition = call_event_action(find_transit_action(node, transition), token, node) if find_transit_action(node, transition)
|
406
|
+
|
407
|
+
if hardcoded_transition
|
408
|
+
enter_node(token, find_transit_target(node, hardcoded_transition))
|
409
|
+
else
|
410
|
+
enter_node(token, find_transit_target(node, transition))
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
class Token
|
416
|
+
|
417
|
+
attr_writer :sub_process, :parent_process
|
418
|
+
attr_reader :workflow, :node, :parent, :sub_process, :parent_process, :ref
|
419
|
+
|
420
|
+
def initialize(workflow, parent = nil, ctx_vars = {}, ref = -1)
|
421
|
+
@workflow = workflow
|
422
|
+
@parent = parent
|
423
|
+
@ctx_vars = ctx_vars
|
424
|
+
@ref = ref
|
425
|
+
@childs = []
|
426
|
+
@sub_process = nil
|
427
|
+
@parent_process = nil
|
428
|
+
@done = false
|
429
|
+
if parent
|
430
|
+
@node = parent.node
|
431
|
+
@parent.add_child(self)
|
432
|
+
else
|
433
|
+
@node = nil
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
def all_ctx_vars
|
438
|
+
@ctx_vars
|
439
|
+
end
|
440
|
+
|
441
|
+
def token
|
442
|
+
self
|
443
|
+
end
|
444
|
+
|
445
|
+
def []=(key, value)
|
446
|
+
@ctx_vars[key] = value
|
447
|
+
end
|
448
|
+
|
449
|
+
def [](key)
|
450
|
+
@ctx_vars[key]
|
451
|
+
end
|
452
|
+
|
453
|
+
def add_child(token)
|
454
|
+
@childs << token
|
455
|
+
end
|
456
|
+
|
457
|
+
def remove_child(token)
|
458
|
+
@childs.delete(token)
|
459
|
+
end
|
460
|
+
|
461
|
+
def childs?
|
462
|
+
not @childs.empty?
|
463
|
+
end
|
464
|
+
|
465
|
+
def signal
|
466
|
+
@workflow.signal_node(self, node)
|
467
|
+
|
468
|
+
if end? && @parent_process
|
469
|
+
@parent_process.signal
|
470
|
+
@parent_process = nil
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
def enter(node)
|
475
|
+
@workflow.enter_node(self, node)
|
476
|
+
end
|
477
|
+
|
478
|
+
def entered(node)
|
479
|
+
@node = node
|
480
|
+
|
481
|
+
if @workflow.clazz.end_node?(@node) and not @parent
|
482
|
+
@workflow.token_done(self)
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
def transit(transition)
|
487
|
+
hardcoded_transition = @workflow.leave_node(self, @node)
|
488
|
+
if hardcoded_transition
|
489
|
+
@workflow.transit(self, @node, hardcoded_transition)
|
490
|
+
else
|
491
|
+
@workflow.transit(self, @node, transition)
|
492
|
+
end
|
493
|
+
end
|
494
|
+
|
495
|
+
def jump(child)
|
496
|
+
@node = child.node
|
497
|
+
end
|
498
|
+
|
499
|
+
def done
|
500
|
+
@parent.remove_child(self) if @parent
|
501
|
+
@parent.jump(self) if @parent
|
502
|
+
@workflow.token_done(self)
|
503
|
+
@done = true
|
504
|
+
end
|
505
|
+
|
506
|
+
def end?
|
507
|
+
@done || @workflow.clazz.end_node?(@node)
|
508
|
+
end
|
509
|
+
|
510
|
+
def fork(ctx_vars = {})
|
511
|
+
@workflow.create_token(self, ctx_vars)
|
512
|
+
end
|
513
|
+
|
514
|
+
def eval_expr(expr)
|
515
|
+
eval(expr, binding())
|
516
|
+
end
|
517
|
+
end
|
518
|
+
|
519
|
+
class ForkNode
|
520
|
+
def initialize(children_ctx_var)
|
521
|
+
@children_ctx_var = children_ctx_var
|
522
|
+
end
|
523
|
+
|
524
|
+
def action(token, caller)
|
525
|
+
exits = []
|
526
|
+
token[@children_ctx_var].each do |ctx_vars|
|
527
|
+
exits << [:default, token.fork(ctx_vars)]
|
528
|
+
end
|
529
|
+
return exits
|
530
|
+
end
|
531
|
+
end
|
532
|
+
|
533
|
+
class JoinNode
|
534
|
+
def action(token, caller)
|
535
|
+
parent_token = token.parent
|
536
|
+
token.done
|
537
|
+
parent_token.childs? ? nil : [[:default, parent_token]]
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
class CallNode
|
542
|
+
def initialize(workflow, workflow_version, call_in, call_out)
|
543
|
+
@workflow = workflow
|
544
|
+
@workflow_version = workflow_version
|
545
|
+
@call_in = call_in
|
546
|
+
@call_out = call_out
|
547
|
+
end
|
548
|
+
|
549
|
+
def action(token, caller)
|
550
|
+
if token.sub_process
|
551
|
+
#sub process has been finished...
|
552
|
+
|
553
|
+
@call_out.each do |remote,local|
|
554
|
+
token[local] = token.sub_process[remote]
|
555
|
+
end
|
556
|
+
|
557
|
+
token.sub_process = nil
|
558
|
+
|
559
|
+
return [[:default, token]]
|
560
|
+
else
|
561
|
+
#let's start a sub process...
|
562
|
+
|
563
|
+
if (@workflow.is_a? Symbol)
|
564
|
+
sub_wf = WorkflowVersionManager.create_workflow_instance(@workflow, @workflow_version.nil? ? :latest : @workflow_version)
|
565
|
+
else
|
566
|
+
sub_wf = @workflow.new
|
567
|
+
end
|
568
|
+
|
569
|
+
params = {}
|
570
|
+
@call_in.each do |local,remote|
|
571
|
+
params[remote] = token[local]
|
572
|
+
end
|
573
|
+
|
574
|
+
sub_t = sub_wf.start(params)
|
575
|
+
|
576
|
+
if (sub_t.end?)
|
577
|
+
@call_out.each do |remote,local|
|
578
|
+
token[local] = sub_t[remote]
|
579
|
+
end
|
580
|
+
|
581
|
+
return [[:default, token]]
|
582
|
+
else
|
583
|
+
sub_t.parent_process = token
|
584
|
+
token.sub_process = sub_t
|
585
|
+
|
586
|
+
return nil #stop wf execution... we'll be called later...
|
587
|
+
end
|
588
|
+
end
|
589
|
+
end
|
590
|
+
end
|