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