flor 0.16.1 → 0.16.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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +8 -0
  3. data/CREDITS.md +1 -0
  4. data/Makefile +1 -1
  5. data/README.md +82 -6
  6. data/lib/flor.rb +1 -1
  7. data/lib/flor/conf.rb +19 -6
  8. data/lib/flor/core/executor.rb +45 -16
  9. data/lib/flor/core/node.rb +4 -4
  10. data/lib/flor/core/procedure.rb +40 -0
  11. data/lib/flor/djan.rb +5 -2
  12. data/lib/flor/flor.rb +92 -7
  13. data/lib/flor/id.rb +19 -0
  14. data/lib/flor/migrations/0001_tables.rb +6 -6
  15. data/lib/flor/migrations/0005_pointer_content.rb +20 -0
  16. data/lib/flor/pcore/_apply.rb +103 -57
  17. data/lib/flor/pcore/_att.rb +15 -1
  18. data/lib/flor/pcore/_ref.rb +2 -1
  19. data/lib/flor/pcore/arith.rb +46 -9
  20. data/lib/flor/pcore/break.rb +1 -1
  21. data/lib/flor/pcore/case.rb +41 -0
  22. data/lib/flor/pcore/collect.rb +1 -1
  23. data/lib/flor/pcore/cursor.rb +1 -1
  24. data/lib/flor/pcore/define.rb +32 -6
  25. data/lib/flor/pcore/iterator.rb +12 -0
  26. data/lib/flor/pcore/on_cancel.rb +1 -1
  27. data/lib/flor/pcore/set.rb +14 -4
  28. data/lib/flor/punit/{ccollect.rb → c_collect.rb} +2 -2
  29. data/lib/flor/punit/c_each.rb +11 -0
  30. data/lib/flor/punit/c_for_each.rb +41 -0
  31. data/lib/flor/punit/c_iterator.rb +160 -0
  32. data/lib/flor/punit/c_map.rb +43 -0
  33. data/lib/flor/punit/concurrence.rb +43 -200
  34. data/lib/flor/punit/graft.rb +3 -2
  35. data/lib/flor/punit/m_ram.rb +281 -0
  36. data/lib/flor/unit.rb +1 -0
  37. data/lib/flor/unit/caller.rb +6 -1
  38. data/lib/flor/unit/executor.rb +17 -4
  39. data/lib/flor/unit/ganger.rb +12 -1
  40. data/lib/flor/unit/hloader.rb +251 -0
  41. data/lib/flor/unit/hook.rb +74 -15
  42. data/lib/flor/unit/hooker.rb +9 -12
  43. data/lib/flor/unit/loader.rb +41 -17
  44. data/lib/flor/unit/models.rb +54 -18
  45. data/lib/flor/unit/models/execution.rb +15 -4
  46. data/lib/flor/unit/models/pointer.rb +11 -0
  47. data/lib/flor/unit/scheduler.rb +126 -30
  48. data/lib/flor/unit/spooler.rb +5 -3
  49. data/lib/flor/unit/storage.rb +40 -13
  50. data/lib/flor/unit/waiter.rb +165 -26
  51. data/lib/flor/unit/wlist.rb +98 -5
  52. metadata +10 -4
  53. data/lib/flor/punit/cmap.rb +0 -112
@@ -38,7 +38,7 @@ class Flor::Pro::Collect < Flor::Macro::Iterator
38
38
  #
39
39
  # ## see also
40
40
  #
41
- # Map, cmap, ccollect.
41
+ # Map, c_map, c_collect.
42
42
 
43
43
  name 'collect'
44
44
 
@@ -30,7 +30,7 @@ class Flor::Pro::Cursor < Flor::Procedure
30
30
  # sales_team "fill in customer details"
31
31
  # ops_team "attribute account number"
32
32
  # continue _ if f.ops_decision == 'reject'
33
- # create_account
33
+ # create_account _
34
34
  # ```
35
35
  #
36
36
  # ## move
@@ -50,7 +50,19 @@ class Flor::Pro::Define < Flor::Procedure
50
50
 
51
51
  def receive_att
52
52
 
53
- t = flatten_tree
53
+ t, sig = flatten_tree
54
+
55
+ bad_param =
56
+ sig.inject(nil) { |r, a|
57
+ a10 = a[1][0]
58
+ pa = a10[0] == '_ref' && a10[1].size > 1 && a10[1].collect { |e| e[1] }
59
+ break pa.join('.') if pa && ! pa[0].match(/\A(f|fld|field)\z/)
60
+ nil }
61
+
62
+ fail Flor::FlorError.new(
63
+ "cannot accept #{bad_param.inspect} as parameter", self
64
+ ) if bad_param
65
+
54
66
  cnode = lookup_var_node(@node, 'l')
55
67
  cnid = cnode['nid']
56
68
  fun = counter_next('funs') - 1
@@ -79,16 +91,22 @@ class Flor::Pro::Define < Flor::Procedure
79
91
 
80
92
  def flatten_tree
81
93
 
94
+ #puts "== tree"
95
+ #pp tree
96
+ tre = tree
82
97
  off = heap == 'define' ? 1 : 0
83
- sig = tree[1][off..-1].select { |t| t[0] == '_att' }
98
+ sig = tre[1][off..-1].select { |t| t[0] == '_att' }
99
+ #puts "sig: " + sig.inspect
100
+ #p sig[0][0]
101
+ #p sig[0][1][0]
84
102
 
85
- return tree if sig.all? { |a| a[1][0][1] == [] }
103
+ return [ tre, sig ] unless wrapped?(sig)
86
104
 
87
105
  # There is a parenthese around the parameters, let's unwrap that...
88
106
 
89
- hed = Flor.dup(tree[1][0, off])
107
+ hed = Flor.dup(tre[1][0, off])
90
108
  sig = Flor.dup(sig)
91
- bdy = Flor.dup(tree[1][(off + sig.length)..-1])
109
+ bdy = Flor.dup(tre[1][(off + sig.length)..-1])
92
110
 
93
111
  att0 = sig[0][1][0]
94
112
  att0atts = att0[1]
@@ -96,7 +114,15 @@ class Flor::Pro::Define < Flor::Procedure
96
114
 
97
115
  sig = sig + att0atts
98
116
 
99
- [ heap, hed + sig + bdy, *tree[2..-1] ]
117
+ [ [ heap, hed + sig + bdy, *tre[2..-1] ],
118
+ sig ]
119
+ end
120
+
121
+ def wrapped?(sig)
122
+
123
+ return false if sig.length != 1
124
+ return false if sig[0][1][0][0] == '_ref'
125
+ sig[0][1][1] != []
100
126
  end
101
127
  end
102
128
 
@@ -26,6 +26,18 @@ class Flor::Pro::Iterator < Flor::Procedure
26
26
  end
27
27
  end
28
28
 
29
+ def add
30
+
31
+ elts = message['elements']
32
+
33
+ elts = elts.inject([]) { |a, e| a.concat(e.to_a) } \
34
+ if @node['ocol'].is_a?(Hash)
35
+
36
+ @node['col'].concat(elts)
37
+
38
+ [] # no new messages to queue
39
+ end
40
+
29
41
  protected
30
42
 
31
43
  def receive_argument
@@ -44,7 +44,7 @@ class Flor::Pro::OnCancel < Flor::Procedure
44
44
  unatt_unkeyed_children
45
45
  end
46
46
 
47
- def receive_non_att
47
+ def receive_last
48
48
 
49
49
  store_on(:cancel)
50
50
 
@@ -9,6 +9,7 @@ class Flor::Pro::Set < Flor::Procedure
9
9
  # set a false # sets the `false` in the variable 'a'
10
10
  # set v.b [ 1 2 ] # sets `[ 1, 2 ]` in the variable 'b'
11
11
  # set v.c.0 -1 # sets `-1` in first slot of the array in var 'c'
12
+ # set v.a f.a.0 # copy the value in field 'a' (first elt) into var 'a'
12
13
  # ```
13
14
  #
14
15
  # When set has a single child, it uses as value to copy the content of
@@ -27,6 +28,15 @@ class Flor::Pro::Set < Flor::Procedure
27
28
  #
28
29
  # ## splat
29
30
  #
31
+ # There is a splat system using underscores to extract array values and
32
+ # assign them to variables.
33
+ #
34
+ # 3 underscores following a variable name instructs "set" to place
35
+ # as many values as possible into an array under the given variable name.
36
+ # 2 underscores following a variable name and followed by an integer
37
+ # restrict the number of values thus globbed.
38
+ # Underscores following nothing simply discard the array values.
39
+ #
30
40
  # ```
31
41
  # sequence
32
42
  # set a b___ c
@@ -56,10 +66,10 @@ class Flor::Pro::Set < Flor::Procedure
56
66
  # value set by its last child (usually the value set).
57
67
  #
58
68
  # ```
59
- # sequence
60
- # 123 # payload.ret is set to `123`
61
- # set a 456 # var 'a' is set to 456, payload.ret is reset to `123`
62
- # setr b 789 # var 'b' is set to `789`, payload.ret as well
69
+ # sequence
70
+ # 123 # payload.ret is set to `123`
71
+ # set a 456 # var 'a' is set to 456, payload.ret is reset to `123`
72
+ # setr b 789 # var 'b' is set to `789`, payload.ret as well
63
73
  # ```
64
74
 
65
75
  names %w[ set setr ]
@@ -28,9 +28,9 @@ class Flor::Pro::Ccollect < Flor::Macro::Iterator
28
28
  #
29
29
  # ## see also
30
30
  #
31
- # Collect, map, cmap.
31
+ # Collect, map, c-map.
32
32
 
33
- name 'ccollect'
33
+ names %w[ ccollect c-collect ]
34
34
 
35
35
  def rewrite_tree
36
36
 
@@ -0,0 +1,11 @@
1
+
2
+ class Flor::Pro::Ceach < Flor::Macro::Iterator
3
+
4
+ names %w[ ceach c-each ]
5
+
6
+ def rewrite_tree
7
+
8
+ rewrite_iterator_tree('c-for-each')
9
+ end
10
+ end
11
+
@@ -0,0 +1,41 @@
1
+
2
+ require 'flor/punit/c_iterator'
3
+
4
+
5
+ class Flor::Pro::CforEach < Flor::Pro::ConcurrentIterator
6
+ #
7
+ # Concurrent "for-each", launches a concurrent branch for each elt or entry
8
+ # of the incoming collection.
9
+ #
10
+ # ```
11
+ # c-for-each [ 'alice' 'bob' 'charly' ]
12
+ # def user \ task user 'contact customer group'
13
+ # #
14
+ # # is thus equivalent to
15
+ # #
16
+ # task 'alice' 'contact customer group'
17
+ # task 'bob' 'contact customer group'
18
+ # task 'charly' 'contact customer group'
19
+ # ```
20
+ #
21
+ # By default, the incoming `f.ret` collection is used:
22
+ # ```
23
+ # [ 'alice' 'bob' 'charly' ]
24
+ # c-for-each
25
+ # def user \ task user 'contact customer group'
26
+ # ```
27
+ #
28
+ # ## see also
29
+ #
30
+ # For-each, cmap, and ceach.
31
+
32
+ name 'c-for-each'
33
+
34
+ protected
35
+
36
+ def post_merge
37
+
38
+ @node['merged_payload'].merge!('ret' => @node['col'])
39
+ end
40
+ end
41
+
@@ -0,0 +1,160 @@
1
+
2
+ require 'flor/punit/m_ram'
3
+
4
+
5
+ # Parent class for "c-for-each" and "c-map"
6
+ #
7
+ class Flor::Pro::ConcurrentIterator < Flor::Procedure
8
+
9
+ include Flor::Pro::ReceiveAndMerge
10
+
11
+ def pre_execute
12
+
13
+ @node['atts'] = []
14
+ @node['args'] = []
15
+ @node['col'] = nil
16
+
17
+ reff_att_children
18
+ unatt_unkeyed_children
19
+ end
20
+
21
+ def receive_non_att
22
+
23
+ if Flor.same_sub?(nid, from)
24
+ @node['args'] << payload['ret']
25
+ super
26
+ elsif @node['on_receive_nids'] && @node['on_receive_nids'][0] == from
27
+ receive_from_receiver
28
+ elsif @node['merging']
29
+ receive_from_merger
30
+ else
31
+ receive_from_branch
32
+ end
33
+ end
34
+
35
+ def receive_last
36
+
37
+ t1 = tree[1]
38
+
39
+ col = nil
40
+ fun = nil
41
+ refs = []
42
+ #
43
+ @node['args'].each_with_index do |a, i|
44
+ if ( ! fun) && Flor.is_func_tree?(a)
45
+ fun = a
46
+ elsif ( ! col) && Flor.is_collection?(a)
47
+ col = a
48
+ else
49
+ tt = t1[i]
50
+ refs << Flor.ref_to_path(tt) if Flor.is_ref_tree?(tt)
51
+ end
52
+ end
53
+ #
54
+ col ||= node_payload_ret
55
+
56
+ fail Flor::FlorError.new("collection not given to #{heap.inspect}", self) \
57
+ unless Flor.is_collection?(col)
58
+ return wrap('ret' => col) \
59
+ unless Flor.is_func_tree?(fun)
60
+
61
+ @node['col'] = col
62
+ @node['cnt'] = col.size
63
+ @node['fun'] = fun
64
+ @node['refs'] = refs
65
+
66
+ col
67
+ .collect
68
+ .with_index { |e, i|
69
+ apply(fun, determine_iteration_args(col, i), tree[2]) }
70
+ .flatten(1)
71
+ end
72
+
73
+ def add
74
+
75
+ col = @node['col']
76
+ elts = message['elements']
77
+
78
+ fail Flor::FlorError.new(
79
+ "cannot add branches to #{heap}", self
80
+ ) unless elts
81
+
82
+ tcol = Flor.type(col)
83
+
84
+ x =
85
+ if tcol == :object
86
+ elts.inject(nil) { |r, e|
87
+ next r if r
88
+ t = Flor.type(e)
89
+ t != :object ? t : r }
90
+ else
91
+ nil
92
+ end
93
+ fail Flor::FlorError.new("cannot add #{x} to object", self) \
94
+ if x
95
+
96
+ if tcol == :array
97
+ col.concat(elts)
98
+ else # tcol == :object
99
+ elts.each { |e| col.merge!(e) }
100
+ end
101
+
102
+ cnt = @node['cnt']
103
+ @node['cnt'] += elts.size
104
+
105
+ pl = message['payload'] || node_payload.current
106
+
107
+ elts
108
+ .collect
109
+ .with_index { |e, i|
110
+ apply(
111
+ @node['fun'], determine_iteration_args(col, cnt + i), tree[2],
112
+ payload: Flor.dup(pl)) }
113
+ .flatten(1)
114
+ end
115
+
116
+ protected
117
+
118
+ def branch_count
119
+
120
+ @node['col'].size
121
+ end
122
+
123
+ def determine_iteration_args(col, idx)
124
+
125
+ refs = @node['refs'].dup
126
+
127
+ args =
128
+ if col.is_a?(Array)
129
+ [ [ refs.shift || 'elt', col[idx] ] ]
130
+ else
131
+ e = col.to_a[idx]
132
+ [ [ refs.shift || 'key', e[0] ], [ refs.shift || 'val', e[1] ] ]
133
+ end
134
+ args << [ refs.shift || 'idx', idx ]
135
+ args << [ refs.shift || 'len', col.length ]
136
+
137
+ args
138
+ end
139
+
140
+ # TODO: eventually move me up to Flor::Procedure, as Flor::Iterator might
141
+ # use me
142
+ #
143
+ def reff_att_children
144
+
145
+ t = tree
146
+ t1 = t[1]
147
+
148
+ is = t1.each.with_index.inject([]) { |a, (tt, i)|
149
+ a << i \
150
+ if tt[0] == '_att' && tt[1].size == 1 && Flor.is_ref_tree?(tt[1][0])
151
+ a }
152
+
153
+ return if is.empty?
154
+
155
+ is.each { |i| t1[i][1][0][0] = '_reff' }
156
+
157
+ @node['tree'] = [ t[0], t1, *t[2..-1] ]
158
+ end
159
+ end
160
+
@@ -0,0 +1,43 @@
1
+
2
+ require 'flor/punit/c_iterator'
3
+
4
+
5
+ class Flor::Pro::Cmap < Flor::Pro::ConcurrentIterator
6
+ #
7
+ # Concurrent version of "map". Spins a concurrent child for each
8
+ # element of the incoming/argument collection.
9
+ #
10
+ # ```
11
+ # cmap [ 1 2 3 ]
12
+ # def x \ * x 2
13
+ # # yields: [ 2, 4, 6 ]
14
+ #
15
+ # [ 1 2 3 ]
16
+ # cmap (def x \ * x 2)
17
+ # # yields: [ 2, 4, 6 ]
18
+ #
19
+ # define double x \ * x 2
20
+ # cmap double [ 1 2 3 ]
21
+ # # yields: [ 2, 4, 6 ]
22
+ # ```
23
+ #
24
+ # "cmap" is over when all the children have answered. For more complex
25
+ # concurrent behaviours, look at [concurrence](concurrence.md).
26
+ #
27
+ # ## see also
28
+ #
29
+ # Map, concurrence.
30
+
31
+ names %w[ cmap c-map ]
32
+
33
+ protected
34
+
35
+ def post_merge
36
+
37
+ @node['merged_payload'].merge!(
38
+ 'ret' => @node['payloads']
39
+ .sort_by { |k, v| k.split('-').last }
40
+ .collect { |_, v| v['ret'] })
41
+ end
42
+ end
43
+
@@ -1,4 +1,7 @@
1
1
 
2
+ require 'flor/punit/m_ram'
3
+
4
+
2
5
  class Flor::Pro::Concurrence < Flor::Procedure
3
6
  #
4
7
  # Executes its children concurrently.
@@ -144,7 +147,7 @@ class Flor::Pro::Concurrence < Flor::Procedure
144
147
  # + 56 78
145
148
  # ```
146
149
  #
147
- # ## on_merge: / merger:
150
+ # ## on_merge: / merger: / merge:
148
151
  #
149
152
  # the function given to `on_merge:` or `merger:` is called once the
150
153
  # concurrence has gathered enough replies (or the right replies,
@@ -180,6 +183,25 @@ class Flor::Pro::Concurrence < Flor::Procedure
180
183
  # * _branch_count_ simply contains the count of branches. It should be
181
184
  # superior or equal to the size of _rets_ and _replies_.
182
185
  #
186
+ # ### merge: and string values
187
+ #
188
+ # By default, the merge technique is a deep merge favouring the first
189
+ # branches to reply. By passing a string value to merge:/merger:/on_merge:
190
+ # one can select a different merge technique.
191
+ #
192
+ # * "first" (the default) - the first branch to reply has priority in the deep # merge
193
+ # * "last" - the last branch to reply has priority in the deep merge
194
+ # * "top" or "north" - the branch are prioritized in the order they are
195
+ # in the flow definition
196
+ # * "bottom" or "south" - the branch are prioritized in the reverse order of
197
+ # the flow definition
198
+ #
199
+ # Adding "plain" (for example "south plain") tells the "concurrence" not to
200
+ # use a deep merge but the plain/vanilla merge found in Ruby.
201
+ #
202
+ # "north plain" can be abbreviated to "np", "bottom" to "b", "first plain" to
203
+ # "fp", etc...
204
+ #
183
205
  # ## on_merge (non-attribute)
184
206
  #
185
207
  # Like `receiver:` / `:on_receive` has the `on_receive` construct, there
@@ -271,15 +293,13 @@ class Flor::Pro::Concurrence < Flor::Procedure
271
293
  # push l 2
272
294
  # ```
273
295
 
296
+ include Flor::Pro::ReceiveAndMerge
297
+
274
298
  name 'concurrence'
275
299
 
276
300
  def pre_execute
277
301
 
278
302
  @node['atts'] = []
279
- @node['payloads'] = {}
280
-
281
- @node['on_receive_nids'] = nil
282
- @node['on_receive_queue'] = []
283
303
 
284
304
  pre_execute_rewrite
285
305
  end
@@ -288,20 +308,11 @@ class Flor::Pro::Concurrence < Flor::Procedure
288
308
 
289
309
  return wrap_reply unless children[@ncid]
290
310
 
291
- @node['receiver'] = determine_receiver
292
- @node['merger'] = determine_merger
293
-
294
-
295
311
  branches = (@ncid..children.size - 1).to_a
296
312
  @node['branch_count'] = branches.count
297
313
 
298
- coe = att('children_on_error', 'child_on_error')
299
- # might be nil
300
-
301
314
  branches
302
- .map { |i|
303
- execute_child(
304
- i, 0, 'payload' => payload.copy_current, 'on_error_handler' => coe) }
315
+ .map { |i| execute_child(i, 0, 'payload' => payload.copy_current) }
305
316
  .flatten(1)
306
317
  #
307
318
  # call execute for each of the (non _att) children
@@ -323,202 +334,34 @@ class Flor::Pro::Concurrence < Flor::Procedure
323
334
  end
324
335
  end
325
336
 
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
407
-
408
- just_over = over && ! @node['over']
409
-
410
- @node['over'] ||= just_over
411
-
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
421
-
422
- def apply_merger
423
-
424
- if @node['merger'].is_a?(String)
425
- apply_merger_method
426
- else
427
- apply_merger_function
428
- end
429
- end
430
-
431
- def apply_merger_method
337
+ def add
432
338
 
433
- pld = send('mm__' + @node['merger'])
434
- msg = { 'payload' => pld }
339
+ super
435
340
 
436
- receive_from_merger(msg)
437
- end
341
+ i = Flor.child_id(message['tnid'])
342
+ ts = message['trees']
438
343
 
439
- def apply_merger_function
344
+ @node['branch_count'] += ts.size
440
345
 
441
- apply(@node['merger'], merger_args, tree[2])
442
- end
443
-
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'] ] ]
451
- end
452
-
453
- def receive_from_merger(msg=message)
454
-
455
- pl = msg ? msg['payload'] : {}
456
- ret = pl['ret']
457
-
458
- pl = ret['payload'] \
459
- if ret.is_a?(Hash) && ret.keys == %w[ done payload ]
460
-
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)
469
- end
470
-
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
492
-
493
- def determine_receiver
494
-
495
- ex = att(:expect)
496
-
497
- return 'expect_integer_receive' if ex && ex.is_a?(Integer) && ex > 0
498
-
499
- att(:on_receive, :receiver) || 'default_receive'
500
- end
501
-
502
- def determine_merger
503
-
504
- att(:on_merge, :merger) || 'default_merge'
346
+ ts
347
+ .collect.with_index { |t, j|
348
+ pl = Flor.dup(message['payload'] || node_payload.copy)
349
+ execute_child(i + j, 0, 'payload' => pl) }
350
+ .flatten(1)
505
351
  end
506
352
 
507
- def cancel_children(rem)
353
+ protected
508
354
 
509
- (rem && rem != 'forget') ? wrap_cancel_children : []
510
- end
355
+ alias branch_count non_att_count
356
+ # used by ReceiveAndMerge to determine procedure end
511
357
 
512
- def reply_to_parent(rem)
358
+ def receive_from_child_when_closed
513
359
 
514
- return [] \
515
- if @node['replied']
516
- return [] \
517
- if @node['payloads'].size < non_att_count && ( ! rem || rem == 'wait')
360
+ ms = receive
518
361
 
519
- @node['replied'] = true
362
+ return [] if ms.empty?
520
363
 
521
- wrap_reply('payload' => @node['merged_payload'])
364
+ pop_on_receive_last || ms
522
365
  end
523
366
 
524
367
  def make_on_def(cn, l)