flor 0.15.0 → 0.16.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +14 -1
- data/CREDITS.md +1 -0
- data/LICENSE.txt +1 -1
- data/Makefile +6 -2
- data/README.md +2 -1
- data/flor.gemspec +12 -2
- data/lib/flor.rb +3 -3
- data/lib/flor/colours.rb +1 -1
- data/lib/flor/conf.rb +3 -4
- data/lib/flor/core/executor.rb +31 -61
- data/lib/flor/core/node.rb +213 -96
- data/lib/flor/core/procedure.rb +194 -75
- data/lib/flor/core/texecutor.rb +6 -7
- data/lib/flor/djan.rb +41 -22
- data/lib/flor/flor.rb +137 -42
- data/lib/flor/id.rb +77 -59
- data/lib/flor/log.rb +43 -22
- data/lib/flor/migrations/0001_tables.rb +7 -7
- data/lib/flor/parser.rb +271 -74
- data/lib/flor/pcore/_apply.rb +108 -0
- data/lib/flor/pcore/_atom.rb +2 -4
- data/lib/flor/pcore/_att.rb +54 -37
- data/lib/flor/pcore/_dmute.rb +18 -0
- data/lib/flor/pcore/_dol.rb +17 -0
- data/lib/flor/pcore/_dqs.rb +35 -0
- data/lib/flor/pcore/_head.rb +25 -0
- data/lib/flor/pcore/_obj.rb +1 -3
- data/lib/flor/pcore/_pat_guard.rb +1 -1
- data/lib/flor/pcore/_pat_obj.rb +11 -3
- data/lib/flor/pcore/_pat_regex.rb +16 -2
- data/lib/flor/pcore/_ref.rb +51 -0
- data/lib/flor/pcore/_rxs.rb +27 -0
- data/lib/flor/pcore/_val.rb +11 -6
- data/lib/flor/pcore/{logo.rb → andor.rb} +4 -6
- data/lib/flor/pcore/apply.rb +72 -2
- data/lib/flor/pcore/arith.rb +16 -4
- data/lib/flor/pcore/array_qmark.rb +100 -0
- data/lib/flor/pcore/break.rb +1 -2
- data/lib/flor/pcore/case.rb +1 -1
- data/lib/flor/pcore/cmp.rb +3 -2
- data/lib/flor/pcore/collect.rb +2 -2
- data/lib/flor/pcore/cond.rb +19 -1
- data/lib/flor/pcore/cursor.rb +12 -11
- data/lib/flor/pcore/define.rb +30 -4
- data/lib/flor/pcore/do_return.rb +3 -0
- data/lib/flor/pcore/flatten.rb +39 -0
- data/lib/flor/pcore/if.rb +15 -5
- data/lib/flor/pcore/includes.rb +5 -2
- data/lib/flor/pcore/inject.rb +1 -1
- data/lib/flor/pcore/iterator.rb +28 -18
- data/lib/flor/pcore/keys.rb +2 -2
- data/lib/flor/pcore/map.rb +19 -1
- data/lib/flor/pcore/match.rb +2 -2
- data/lib/flor/pcore/matchr.rb +18 -5
- data/lib/flor/pcore/max.rb +51 -0
- data/lib/flor/pcore/merge.rb +134 -0
- data/lib/flor/pcore/move.rb +1 -1
- data/lib/flor/pcore/noret.rb +1 -1
- data/lib/flor/pcore/not.rb +15 -1
- data/lib/flor/pcore/on.rb +11 -0
- data/lib/flor/pcore/on_cancel.rb +5 -1
- data/lib/flor/pcore/on_error.rb +69 -4
- data/lib/flor/pcore/push.rb +4 -9
- data/lib/flor/pcore/range.rb +5 -5
- data/lib/flor/pcore/reduce.rb +5 -18
- data/lib/flor/pcore/return.rb +26 -0
- data/lib/flor/pcore/reverse.rb +4 -0
- data/lib/flor/pcore/sequence.rb +8 -1
- data/lib/flor/pcore/set.rb +74 -15
- data/lib/flor/pcore/shuffle.rb +71 -0
- data/lib/flor/pcore/slice.rb +137 -0
- data/lib/flor/pcore/sort.rb +244 -0
- data/lib/flor/pcore/sort_by.rb +67 -0
- data/lib/flor/pcore/split.rb +39 -0
- data/lib/flor/pcore/stall.rb +1 -1
- data/lib/flor/pcore/strings.rb +123 -0
- data/lib/flor/pcore/timestamp.rb +34 -0
- data/lib/flor/pcore/to_array.rb +2 -3
- data/lib/flor/pcore/twig.rb +1 -1
- data/lib/flor/pcore/type_of.rb +37 -0
- data/lib/flor/pcore/until.rb +3 -3
- data/lib/flor/punit/cancel.rb +3 -3
- data/lib/flor/punit/ccollect.rb +29 -0
- data/lib/flor/punit/cmap.rb +76 -20
- data/lib/flor/punit/concurrence.rb +440 -33
- data/lib/flor/punit/cron.rb +1 -1
- data/lib/flor/punit/every.rb +1 -1
- data/lib/flor/punit/graft.rb +2 -3
- data/lib/flor/punit/on_timeout.rb +5 -1
- data/lib/flor/punit/part.rb +63 -0
- data/lib/flor/punit/schedule.rb +1 -1
- data/lib/flor/punit/task.rb +52 -10
- data/lib/flor/punit/trap.rb +4 -5
- data/lib/flor/tools/shell.rb +37 -18
- data/lib/flor/unit/caller.rb +23 -11
- data/lib/flor/unit/executor.rb +33 -12
- data/lib/flor/unit/ganger.rb +10 -1
- data/lib/flor/unit/hook.rb +2 -1
- data/lib/flor/unit/hooker.rb +13 -2
- data/lib/flor/unit/loader.rb +7 -7
- data/lib/flor/unit/logger.rb +15 -17
- data/lib/flor/unit/models.rb +4 -2
- data/lib/flor/unit/models/execution.rb +83 -38
- data/lib/flor/unit/models/message.rb +16 -0
- data/lib/flor/unit/models/pointer.rb +24 -0
- data/lib/flor/unit/models/timer.rb +25 -4
- data/lib/flor/unit/models/trace.rb +14 -0
- data/lib/flor/unit/models/trap.rb +39 -14
- data/lib/flor/unit/scheduler.rb +11 -7
- data/lib/flor/unit/storage.rb +55 -39
- data/lib/flor/unit/taskers.rb +17 -14
- data/lib/flor/unit/waiter.rb +4 -3
- metadata +40 -10
- data/lib/flor/changes.rb +0 -26
- data/lib/flor/dollar.rb +0 -224
- data/lib/flor/unit/hooks.rb +0 -37
@@ -67,6 +67,209 @@ class Flor::Pro::Concurrence < Flor::Procedure
|
|
67
67
|
# task 'alpha'
|
68
68
|
# task 'bravo'
|
69
69
|
# ```
|
70
|
+
#
|
71
|
+
# ## on_receive: / receiver:
|
72
|
+
#
|
73
|
+
# Sets a function that is to be run each time a concurrence branch replies.
|
74
|
+
# Should return a boolean, `true` for the concurrence to end (and trigger
|
75
|
+
# the merging) or `false` for the concurrence to go on (and replies from
|
76
|
+
# other branches to be received).
|
77
|
+
#
|
78
|
+
# In this example, the receiver is actually an implementation of the default
|
79
|
+
# receive behaviour, "concurrence" merges as soon as all the children have
|
80
|
+
# replied (`>= (length replies) branch_count`).
|
81
|
+
# ```
|
82
|
+
# define r reply, from, replies, branch_count
|
83
|
+
# >= (length replies) branch_count
|
84
|
+
# concurrence on_receive: r
|
85
|
+
# + 1 2
|
86
|
+
# + 3 4
|
87
|
+
# ```
|
88
|
+
#
|
89
|
+
# The receiver can be used to change the reply payload. Instead of
|
90
|
+
# returning a boolean, it can return an object with the `done:` and
|
91
|
+
# the `payload:` keys:
|
92
|
+
# ```
|
93
|
+
# define r reply, from, replies, branch_count, over
|
94
|
+
# set reply.ret (+ reply.ret 10)
|
95
|
+
# { done: (>= (length replies) branch_count), payload: reply }
|
96
|
+
# concurrence on_receive: r
|
97
|
+
# + 1 2
|
98
|
+
# + 3 4
|
99
|
+
# ```
|
100
|
+
# The first branch thus returns `1 + 2 + 10`, while the second one returns
|
101
|
+
# `3 + 4 + 10`.
|
102
|
+
#
|
103
|
+
# The signature for receiver functions is:
|
104
|
+
# `define r reply, from, replies, branch_count`
|
105
|
+
#
|
106
|
+
# * _reply_ the current reply, here something like `{ ret: 3 }`.
|
107
|
+
# * _from_ a string like "0_1_1", the nid of the node that emitted
|
108
|
+
# the current reply.
|
109
|
+
# * _replies_ an object indexing the replies received so far, like
|
110
|
+
# `{ "0_1_1" => { "ret" => 13 } }`.
|
111
|
+
# * _branch_count_ simply contains the count of branches. It should be
|
112
|
+
# superior or equal to the size of _rets_ and _replies_.
|
113
|
+
# * _over_ is set to `true` if a previous receiver call said the
|
114
|
+
# concurrence should end. It is set to `false` else. So it's `true`
|
115
|
+
# for replies post-merge. It might happen for children answering
|
116
|
+
# right after the merge limit and children of concurrences that
|
117
|
+
# wait for all the replies, see the `remaining:` attribute above.
|
118
|
+
#
|
119
|
+
# ## on_receive (non-attribute)
|
120
|
+
#
|
121
|
+
# Sometimes, it's better to declutter the concurrence and write the
|
122
|
+
# on_receive as a 'special' child rather than a attribute:
|
123
|
+
#
|
124
|
+
# ```
|
125
|
+
# define r reply, from, replies, branch_count
|
126
|
+
# >= (length replies) branch_count
|
127
|
+
# concurrence on_receive: r
|
128
|
+
# + 1 2
|
129
|
+
# + 3 4
|
130
|
+
# ```
|
131
|
+
# becomes
|
132
|
+
# ```
|
133
|
+
# concurrence tag: 'x'
|
134
|
+
# on_receive (def \ >= (length replies) branch_count)
|
135
|
+
# + 12 34
|
136
|
+
# + 56 78
|
137
|
+
# ```
|
138
|
+
# One can even express the function has a 'block':
|
139
|
+
# ```
|
140
|
+
# concurrence tag: 'x'
|
141
|
+
# on_receive
|
142
|
+
# >= (length replies) branch_count
|
143
|
+
# + 12 34
|
144
|
+
# + 56 78
|
145
|
+
# ```
|
146
|
+
#
|
147
|
+
# ## on_merge: / merger:
|
148
|
+
#
|
149
|
+
# the function given to `on_merge:` or `merger:` is called once the
|
150
|
+
# concurrence has gathered enough replies (or the right replies,
|
151
|
+
# depending on `on_receive:` / `receiver:` or `expect:`).
|
152
|
+
#
|
153
|
+
# In the example below, the merging function take all the `f.ret` and
|
154
|
+
# selects the maximum one:
|
155
|
+
# ```
|
156
|
+
# define m rets, replies, branch_count
|
157
|
+
# rets | values _ | max _
|
158
|
+
# concurrence on_merge: m
|
159
|
+
# + 3 4 5
|
160
|
+
# + 6 7 8
|
161
|
+
# + 1 2 3
|
162
|
+
# ```
|
163
|
+
# It can be shortened to:
|
164
|
+
# ```
|
165
|
+
# concurrence on_merge: (def rets \ rets | values _ | max _)
|
166
|
+
# + 3 4 5
|
167
|
+
# + 6 7 8
|
168
|
+
# + 1 2 3
|
169
|
+
# ```
|
170
|
+
# `rets` looks like `{ "0_1_1" => 12, "0_1_2" => 21, "0_1_3" => 6 }`,
|
171
|
+
# hence the `rets | values _ | max _`.
|
172
|
+
#
|
173
|
+
# The signature for the merge function looks like:
|
174
|
+
# `define m rets, replies, branch_count`
|
175
|
+
#
|
176
|
+
# * _rets_ is the object collecting the `f.ret` of the replies to merge,
|
177
|
+
# like `{ "0_1_1" => 12, "0_1_2" => 21, "0_1_3" => 6 }` as seen above.
|
178
|
+
# * _replies_ is the equivalent but for the whole reply payload (fields),
|
179
|
+
# like `{"0_1"=>{"ret"=>12}, "0_2"=>{"ret"=>21}, "0_3"=>{"ret"=>6}}`.
|
180
|
+
# * _branch_count_ simply contains the count of branches. It should be
|
181
|
+
# superior or equal to the size of _rets_ and _replies_.
|
182
|
+
#
|
183
|
+
# ## on_merge (non-attribute)
|
184
|
+
#
|
185
|
+
# Like `receiver:` / `:on_receive` has the `on_receive` construct, there
|
186
|
+
# is the `on_merge` construct which accepts a function or a block:
|
187
|
+
# ```
|
188
|
+
# concurrence
|
189
|
+
# on_merge (def rs \ rs | values _ | max _)
|
190
|
+
# + 1 4 5
|
191
|
+
# + 3 7 8
|
192
|
+
# + 6 2 3
|
193
|
+
# ```
|
194
|
+
# or
|
195
|
+
# ```
|
196
|
+
# concurrence
|
197
|
+
# on_merge
|
198
|
+
# rets | values _ | min _
|
199
|
+
# + 3 4 5
|
200
|
+
# + 6 7 8
|
201
|
+
# + 1 2 3
|
202
|
+
# ```
|
203
|
+
#
|
204
|
+
# ## child_on_error: / children_on_error:
|
205
|
+
#
|
206
|
+
# Setting the common attribute `on_error:` on a concurrence is OK, but it
|
207
|
+
# only catches a single error and then the flow resumes after the concurrence:
|
208
|
+
# ```
|
209
|
+
# sequence
|
210
|
+
# set l []
|
211
|
+
# concurrence on_error:
|
212
|
+
# (def msg err \ push l "error at $(msg.nid): $(err.msg)")
|
213
|
+
# push l 0
|
214
|
+
# push l x # fails because 'x' is unknown)
|
215
|
+
# push l 2
|
216
|
+
# ```
|
217
|
+
# The `push l x` here fails, the on_error is triggered and the flow resumes
|
218
|
+
# at `push l 2`. `l` ends up containing
|
219
|
+
# `[ 0, "error at 0_1_2_1: cannot find \"x\"", 2 ]`.
|
220
|
+
#
|
221
|
+
# It is easy to set an `on_error:` on each child:
|
222
|
+
# ```
|
223
|
+
# sequence
|
224
|
+
# set l []
|
225
|
+
# define oe msg err
|
226
|
+
# push l "error at $(msg.nid): $(err.msg)"
|
227
|
+
# concurrence
|
228
|
+
# push l 0 on_error: oe
|
229
|
+
# push l x on_error: oe
|
230
|
+
# push l 2
|
231
|
+
# ```
|
232
|
+
# But that can also be written as:
|
233
|
+
# ```
|
234
|
+
# sequence
|
235
|
+
# set l []
|
236
|
+
# concurrence child_on_error:
|
237
|
+
# (def msg err \ push l "error at $(msg.nid): $(err.msg)")
|
238
|
+
# push l 0
|
239
|
+
# push l x
|
240
|
+
# push l 2
|
241
|
+
# ```
|
242
|
+
# `child_on_error` can also be written as `children_on_error`.
|
243
|
+
#
|
244
|
+
# The signature for the error handler is:
|
245
|
+
# * _msg_ the message (usually `point: 'failed'`) communicating the error
|
246
|
+
# * _err_ contains the error itself, it's a shortcut to `msg.error`
|
247
|
+
#
|
248
|
+
#
|
249
|
+
# ## child_on_error / children_on_error (non-attribute)
|
250
|
+
#
|
251
|
+
# Those who prefer to tie handlers via a node rather than an attribute
|
252
|
+
# can do so:
|
253
|
+
# ```
|
254
|
+
# sequence
|
255
|
+
# set l []
|
256
|
+
# concurrence
|
257
|
+
# child_on_error (def msg err \ push l "error at $(msg.nid): $(err.msg)")
|
258
|
+
# push l 0
|
259
|
+
# push l x
|
260
|
+
# push l 2
|
261
|
+
# ```
|
262
|
+
# One step further, with a block:
|
263
|
+
# ```
|
264
|
+
# sequence
|
265
|
+
# set l []
|
266
|
+
# concurrence
|
267
|
+
# child_on_error
|
268
|
+
# push l "error at $(msg.nid): $(err.msg)")
|
269
|
+
# push l 0
|
270
|
+
# push l x
|
271
|
+
# push l 2
|
272
|
+
# ```
|
70
273
|
|
71
274
|
name 'concurrence'
|
72
275
|
|
@@ -74,6 +277,11 @@ class Flor::Pro::Concurrence < Flor::Procedure
|
|
74
277
|
|
75
278
|
@node['atts'] = []
|
76
279
|
@node['payloads'] = {}
|
280
|
+
|
281
|
+
@node['on_receive_nids'] = nil
|
282
|
+
@node['on_receive_queue'] = []
|
283
|
+
|
284
|
+
pre_execute_rewrite
|
77
285
|
end
|
78
286
|
|
79
287
|
def receive_last_att
|
@@ -83,8 +291,17 @@ class Flor::Pro::Concurrence < Flor::Procedure
|
|
83
291
|
@node['receiver'] = determine_receiver
|
84
292
|
@node['merger'] = determine_merger
|
85
293
|
|
86
|
-
|
87
|
-
|
294
|
+
|
295
|
+
branches = (@ncid..children.size - 1).to_a
|
296
|
+
@node['branch_count'] = branches.count
|
297
|
+
|
298
|
+
coe = att('children_on_error', 'child_on_error')
|
299
|
+
# might be nil
|
300
|
+
|
301
|
+
branches
|
302
|
+
.map { |i|
|
303
|
+
execute_child(
|
304
|
+
i, 0, 'payload' => payload.copy_current, 'on_error_handler' => coe) }
|
88
305
|
.flatten(1)
|
89
306
|
#
|
90
307
|
# call execute for each of the (non _att) children
|
@@ -92,44 +309,186 @@ class Flor::Pro::Concurrence < Flor::Procedure
|
|
92
309
|
|
93
310
|
def receive_non_att
|
94
311
|
|
95
|
-
|
312
|
+
if message['from_on']
|
313
|
+
super
|
314
|
+
#receive_from_on
|
315
|
+
elsif Flor.same_sub?(nid, from)
|
316
|
+
receive_from_branch
|
317
|
+
elsif from_error_handler?
|
318
|
+
wrap_reply
|
319
|
+
elsif @node['on_receive_nids'] && @node['on_receive_nids'][0] == from
|
320
|
+
receive_from_receiver
|
321
|
+
else
|
322
|
+
receive_from_merger
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
protected
|
327
|
+
|
328
|
+
|
329
|
+
def receive_from_child_when_closed
|
330
|
+
|
331
|
+
ms = receive
|
332
|
+
|
333
|
+
return [] if ms.empty?
|
334
|
+
|
335
|
+
pop_on_receive_last || ms
|
336
|
+
end
|
337
|
+
|
338
|
+
def receive_from_branch
|
339
|
+
|
340
|
+
@node['payloads'][from] = @message['payload']
|
341
|
+
|
342
|
+
apply_receiver
|
343
|
+
end
|
344
|
+
|
345
|
+
def apply_receiver
|
346
|
+
|
347
|
+
if @node['receiver'].is_a?(String)
|
348
|
+
apply_receiver_method
|
349
|
+
else
|
350
|
+
apply_receiver_function
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
def apply_receiver_method
|
355
|
+
|
356
|
+
ret = send('rm__' + @node['receiver'])
|
357
|
+
msg = { 'payload' => { 'ret' => ret } }
|
358
|
+
|
359
|
+
receive_from_receiver(msg)
|
360
|
+
end
|
361
|
+
|
362
|
+
def apply_receiver_function
|
363
|
+
|
364
|
+
@node['on_receive_queue'] << from
|
365
|
+
|
366
|
+
dequeue_receiver_function
|
367
|
+
end
|
368
|
+
|
369
|
+
def dequeue_receiver_function
|
370
|
+
|
371
|
+
if @node['on_receive_nids']
|
372
|
+
[]
|
373
|
+
elsif f = @node['on_receive_queue'].shift
|
374
|
+
ms = apply(@node['receiver'], receiver_args(f), tree[2])
|
375
|
+
@node['on_receive_nids'] = [ ms.first['nid'], f ]
|
376
|
+
ms
|
377
|
+
else
|
378
|
+
[]
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
def receiver_args(from)
|
383
|
+
|
384
|
+
rs = Flor.dup(@node['payloads'])
|
385
|
+
|
386
|
+
[ [ 'reply', rs[from] ],
|
387
|
+
[ 'from', from ],
|
388
|
+
[ 'replies', rs ],
|
389
|
+
[ 'branch_count', @node['branch_count'] ],
|
390
|
+
[ 'over', !! @node['over'] ] ]
|
391
|
+
end
|
392
|
+
|
393
|
+
def receive_from_receiver(msg=message)
|
394
|
+
|
395
|
+
ret = msg['payload']['ret']
|
396
|
+
over = @node['over']
|
397
|
+
|
398
|
+
if ret.is_a?(Hash) && ret.keys == %w[ done payload ]
|
399
|
+
over = over || ret['done']
|
400
|
+
from = @node['on_receive_nids'][1]
|
401
|
+
@node['payloads'][from] = ret['payload']
|
402
|
+
else
|
403
|
+
over = over || ret
|
404
|
+
end
|
405
|
+
|
406
|
+
@node['on_receive_nids'] = nil
|
96
407
|
|
97
|
-
over = @node['over'] || invoke_receiver
|
98
408
|
just_over = over && ! @node['over']
|
409
|
+
|
99
410
|
@node['over'] ||= just_over
|
100
411
|
|
101
|
-
|
102
|
-
|
412
|
+
if just_over
|
413
|
+
apply_merger
|
414
|
+
elsif ! over
|
415
|
+
[] # wait for more branches
|
416
|
+
else
|
417
|
+
receive_from_merger(nil)
|
418
|
+
end +
|
419
|
+
dequeue_receiver_function
|
420
|
+
end
|
103
421
|
|
104
|
-
|
422
|
+
def apply_merger
|
423
|
+
|
424
|
+
if @node['merger'].is_a?(String)
|
425
|
+
apply_merger_method
|
426
|
+
else
|
427
|
+
apply_merger_function
|
428
|
+
end
|
105
429
|
end
|
106
430
|
|
107
|
-
def
|
431
|
+
def apply_merger_method
|
108
432
|
|
109
|
-
|
433
|
+
pld = send('mm__' + @node['merger'])
|
434
|
+
msg = { 'payload' => pld }
|
435
|
+
|
436
|
+
receive_from_merger(msg)
|
110
437
|
end
|
111
438
|
|
112
|
-
def
|
439
|
+
def apply_merger_function
|
113
440
|
|
114
|
-
|
115
|
-
|
116
|
-
return [] \
|
117
|
-
if @node['payloads'].size < non_att_count && ( ! rem || rem == 'wait')
|
441
|
+
apply(@node['merger'], merger_args, tree[2])
|
442
|
+
end
|
118
443
|
|
119
|
-
|
120
|
-
|
444
|
+
def merger_args
|
445
|
+
|
446
|
+
rs = Flor.dup(@node['payloads'])
|
447
|
+
|
448
|
+
[ [ 'rets', rs.inject({}) { |h, (k, v)| h[k] = v['ret']; h } ],
|
449
|
+
[ 'replies', rs ],
|
450
|
+
[ 'branch_count', @node['branch_count'] ] ]
|
121
451
|
end
|
122
452
|
|
123
|
-
def
|
453
|
+
def receive_from_merger(msg=message)
|
124
454
|
|
125
|
-
|
455
|
+
pl = msg ? msg['payload'] : {}
|
456
|
+
ret = pl['ret']
|
126
457
|
|
127
|
-
|
458
|
+
pl = ret['payload'] \
|
459
|
+
if ret.is_a?(Hash) && ret.keys == %w[ done payload ]
|
128
460
|
|
129
|
-
|
461
|
+
# TODO somehow, what if done is false, should we un-over the concurrence?
|
462
|
+
|
463
|
+
@node['merged_payload'] = pl \
|
464
|
+
if msg && ! @node.has_key?('merged_payload')
|
465
|
+
|
466
|
+
rem = determine_remainder
|
467
|
+
|
468
|
+
cancel_children(rem) + reply_to_parent(rem)
|
130
469
|
end
|
131
470
|
|
132
|
-
|
471
|
+
def rm__default_receive
|
472
|
+
|
473
|
+
@node['payloads'].size >= non_att_count
|
474
|
+
end
|
475
|
+
|
476
|
+
def rm__expect_integer_receive
|
477
|
+
|
478
|
+
@node['payloads'].size >= att(:expect)
|
479
|
+
end
|
480
|
+
|
481
|
+
def mm__default_merge
|
482
|
+
|
483
|
+
@node['payloads'].values
|
484
|
+
.reverse
|
485
|
+
.inject({}) { |h, pl| h.merge!(pl) }
|
486
|
+
end
|
487
|
+
|
488
|
+
def determine_remainder
|
489
|
+
|
490
|
+
att(:remaining, :rem) || 'cancel'
|
491
|
+
end
|
133
492
|
|
134
493
|
def determine_receiver
|
135
494
|
|
@@ -137,32 +496,80 @@ class Flor::Pro::Concurrence < Flor::Procedure
|
|
137
496
|
|
138
497
|
return 'expect_integer_receive' if ex && ex.is_a?(Integer) && ex > 0
|
139
498
|
|
140
|
-
'default_receive'
|
499
|
+
att(:on_receive, :receiver) || 'default_receive'
|
141
500
|
end
|
142
501
|
|
143
502
|
def determine_merger
|
144
503
|
|
145
|
-
'default_merge'
|
504
|
+
att(:on_merge, :merger) || 'default_merge'
|
146
505
|
end
|
147
506
|
|
148
|
-
def
|
149
|
-
def invoke_merger; self.send(@node['merger']); end
|
507
|
+
def cancel_children(rem)
|
150
508
|
|
151
|
-
|
509
|
+
(rem && rem != 'forget') ? wrap_cancel_children : []
|
510
|
+
end
|
152
511
|
|
153
|
-
|
512
|
+
def reply_to_parent(rem)
|
513
|
+
|
514
|
+
return [] \
|
515
|
+
if @node['replied']
|
516
|
+
return [] \
|
517
|
+
if @node['payloads'].size < non_att_count && ( ! rem || rem == 'wait')
|
518
|
+
|
519
|
+
@node['replied'] = true
|
520
|
+
|
521
|
+
wrap_reply('payload' => @node['merged_payload'])
|
154
522
|
end
|
155
523
|
|
156
|
-
def
|
524
|
+
def make_on_def(cn, l)
|
157
525
|
|
158
|
-
|
526
|
+
c0 = cn.length == 1 ? cn[0] : nil
|
527
|
+
|
528
|
+
return c0 if Flor.is_definition_tree?(c0)
|
529
|
+
return make_on_def(c0[1], l) if Flor.is_att_tree?(c0)
|
530
|
+
|
531
|
+
[ 'def', cn, l ]
|
159
532
|
end
|
160
533
|
|
161
|
-
def
|
534
|
+
def rewrite_as_attribute(child_tree)
|
162
535
|
|
163
|
-
|
164
|
-
|
165
|
-
|
536
|
+
ct0, ct1, ct2 = child_tree
|
537
|
+
|
538
|
+
k = [ ct0, [], ct2 ]
|
539
|
+
v = make_on_def(ct1, ct2)
|
540
|
+
|
541
|
+
[ '_att', [ k, v ], ct2 ]
|
542
|
+
end
|
543
|
+
|
544
|
+
REWRITE_AS_ATTS = %w[
|
545
|
+
on_receive on_merge
|
546
|
+
child_on_error children_on_error ]
|
547
|
+
#
|
548
|
+
# heads of the child nodes that should get rewritten as attributes
|
549
|
+
# of the concurrence ...
|
550
|
+
|
551
|
+
def pre_execute_rewrite
|
552
|
+
|
553
|
+
t = tree
|
554
|
+
t1 = t[1]
|
555
|
+
|
556
|
+
return if t1.empty?
|
557
|
+
|
558
|
+
atts, cldn =
|
559
|
+
t1.inject([ [], [] ]) { |r, ct|
|
560
|
+
if ct[0] == '_att'
|
561
|
+
r[0] << ct
|
562
|
+
elsif REWRITE_AS_ATTS.include?(ct[0])
|
563
|
+
r[0] << rewrite_as_attribute(ct)
|
564
|
+
else
|
565
|
+
r[1] << ct
|
566
|
+
end
|
567
|
+
r }
|
568
|
+
|
569
|
+
nt1 = atts + cldn
|
570
|
+
|
571
|
+
@node['tree'] = [ t[0], nt1, *t[2..-1] ] \
|
572
|
+
if nt1 != t1
|
166
573
|
end
|
167
574
|
end
|
168
575
|
|