flor 0.15.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +14 -1
  3. data/CREDITS.md +1 -0
  4. data/LICENSE.txt +1 -1
  5. data/Makefile +6 -2
  6. data/README.md +2 -1
  7. data/flor.gemspec +12 -2
  8. data/lib/flor.rb +3 -3
  9. data/lib/flor/colours.rb +1 -1
  10. data/lib/flor/conf.rb +3 -4
  11. data/lib/flor/core/executor.rb +31 -61
  12. data/lib/flor/core/node.rb +213 -96
  13. data/lib/flor/core/procedure.rb +194 -75
  14. data/lib/flor/core/texecutor.rb +6 -7
  15. data/lib/flor/djan.rb +41 -22
  16. data/lib/flor/flor.rb +137 -42
  17. data/lib/flor/id.rb +77 -59
  18. data/lib/flor/log.rb +43 -22
  19. data/lib/flor/migrations/0001_tables.rb +7 -7
  20. data/lib/flor/parser.rb +271 -74
  21. data/lib/flor/pcore/_apply.rb +108 -0
  22. data/lib/flor/pcore/_atom.rb +2 -4
  23. data/lib/flor/pcore/_att.rb +54 -37
  24. data/lib/flor/pcore/_dmute.rb +18 -0
  25. data/lib/flor/pcore/_dol.rb +17 -0
  26. data/lib/flor/pcore/_dqs.rb +35 -0
  27. data/lib/flor/pcore/_head.rb +25 -0
  28. data/lib/flor/pcore/_obj.rb +1 -3
  29. data/lib/flor/pcore/_pat_guard.rb +1 -1
  30. data/lib/flor/pcore/_pat_obj.rb +11 -3
  31. data/lib/flor/pcore/_pat_regex.rb +16 -2
  32. data/lib/flor/pcore/_ref.rb +51 -0
  33. data/lib/flor/pcore/_rxs.rb +27 -0
  34. data/lib/flor/pcore/_val.rb +11 -6
  35. data/lib/flor/pcore/{logo.rb → andor.rb} +4 -6
  36. data/lib/flor/pcore/apply.rb +72 -2
  37. data/lib/flor/pcore/arith.rb +16 -4
  38. data/lib/flor/pcore/array_qmark.rb +100 -0
  39. data/lib/flor/pcore/break.rb +1 -2
  40. data/lib/flor/pcore/case.rb +1 -1
  41. data/lib/flor/pcore/cmp.rb +3 -2
  42. data/lib/flor/pcore/collect.rb +2 -2
  43. data/lib/flor/pcore/cond.rb +19 -1
  44. data/lib/flor/pcore/cursor.rb +12 -11
  45. data/lib/flor/pcore/define.rb +30 -4
  46. data/lib/flor/pcore/do_return.rb +3 -0
  47. data/lib/flor/pcore/flatten.rb +39 -0
  48. data/lib/flor/pcore/if.rb +15 -5
  49. data/lib/flor/pcore/includes.rb +5 -2
  50. data/lib/flor/pcore/inject.rb +1 -1
  51. data/lib/flor/pcore/iterator.rb +28 -18
  52. data/lib/flor/pcore/keys.rb +2 -2
  53. data/lib/flor/pcore/map.rb +19 -1
  54. data/lib/flor/pcore/match.rb +2 -2
  55. data/lib/flor/pcore/matchr.rb +18 -5
  56. data/lib/flor/pcore/max.rb +51 -0
  57. data/lib/flor/pcore/merge.rb +134 -0
  58. data/lib/flor/pcore/move.rb +1 -1
  59. data/lib/flor/pcore/noret.rb +1 -1
  60. data/lib/flor/pcore/not.rb +15 -1
  61. data/lib/flor/pcore/on.rb +11 -0
  62. data/lib/flor/pcore/on_cancel.rb +5 -1
  63. data/lib/flor/pcore/on_error.rb +69 -4
  64. data/lib/flor/pcore/push.rb +4 -9
  65. data/lib/flor/pcore/range.rb +5 -5
  66. data/lib/flor/pcore/reduce.rb +5 -18
  67. data/lib/flor/pcore/return.rb +26 -0
  68. data/lib/flor/pcore/reverse.rb +4 -0
  69. data/lib/flor/pcore/sequence.rb +8 -1
  70. data/lib/flor/pcore/set.rb +74 -15
  71. data/lib/flor/pcore/shuffle.rb +71 -0
  72. data/lib/flor/pcore/slice.rb +137 -0
  73. data/lib/flor/pcore/sort.rb +244 -0
  74. data/lib/flor/pcore/sort_by.rb +67 -0
  75. data/lib/flor/pcore/split.rb +39 -0
  76. data/lib/flor/pcore/stall.rb +1 -1
  77. data/lib/flor/pcore/strings.rb +123 -0
  78. data/lib/flor/pcore/timestamp.rb +34 -0
  79. data/lib/flor/pcore/to_array.rb +2 -3
  80. data/lib/flor/pcore/twig.rb +1 -1
  81. data/lib/flor/pcore/type_of.rb +37 -0
  82. data/lib/flor/pcore/until.rb +3 -3
  83. data/lib/flor/punit/cancel.rb +3 -3
  84. data/lib/flor/punit/ccollect.rb +29 -0
  85. data/lib/flor/punit/cmap.rb +76 -20
  86. data/lib/flor/punit/concurrence.rb +440 -33
  87. data/lib/flor/punit/cron.rb +1 -1
  88. data/lib/flor/punit/every.rb +1 -1
  89. data/lib/flor/punit/graft.rb +2 -3
  90. data/lib/flor/punit/on_timeout.rb +5 -1
  91. data/lib/flor/punit/part.rb +63 -0
  92. data/lib/flor/punit/schedule.rb +1 -1
  93. data/lib/flor/punit/task.rb +52 -10
  94. data/lib/flor/punit/trap.rb +4 -5
  95. data/lib/flor/tools/shell.rb +37 -18
  96. data/lib/flor/unit/caller.rb +23 -11
  97. data/lib/flor/unit/executor.rb +33 -12
  98. data/lib/flor/unit/ganger.rb +10 -1
  99. data/lib/flor/unit/hook.rb +2 -1
  100. data/lib/flor/unit/hooker.rb +13 -2
  101. data/lib/flor/unit/loader.rb +7 -7
  102. data/lib/flor/unit/logger.rb +15 -17
  103. data/lib/flor/unit/models.rb +4 -2
  104. data/lib/flor/unit/models/execution.rb +83 -38
  105. data/lib/flor/unit/models/message.rb +16 -0
  106. data/lib/flor/unit/models/pointer.rb +24 -0
  107. data/lib/flor/unit/models/timer.rb +25 -4
  108. data/lib/flor/unit/models/trace.rb +14 -0
  109. data/lib/flor/unit/models/trap.rb +39 -14
  110. data/lib/flor/unit/scheduler.rb +11 -7
  111. data/lib/flor/unit/storage.rb +55 -39
  112. data/lib/flor/unit/taskers.rb +17 -14
  113. data/lib/flor/unit/waiter.rb +4 -3
  114. metadata +40 -10
  115. data/lib/flor/changes.rb +0 -26
  116. data/lib/flor/dollar.rb +0 -224
  117. data/lib/flor/unit/hooks.rb +0 -37
@@ -0,0 +1,67 @@
1
+
2
+ class Flor::Pro::SortBy < Flor::Pro::Iterator
3
+ #
4
+ # Takes a collection and a function and returns the collection
5
+ # sorted by the value returned by the function.
6
+ #
7
+ # ```
8
+ # sort_by [ { n: 1 } { n: 0 } { n: 4 } { n: 7 } ] (def e \ e.n)
9
+ # # OR
10
+ # sort_by (def e \ e.n) [ { n: 1 } { n: 0 } { n: 4 } { n: 7 } ]
11
+ # #
12
+ # # => [ { 'n' => 0 }, { 'n' => 1 }, { 'n' => 4 }, { 'n' => 7 } ]
13
+ # ```
14
+ #
15
+ # ## function parameters
16
+ #
17
+ # If the collection is an array, the function signature may look like:
18
+ # ```
19
+ # def f(elt, idx, len)
20
+ # # elt: the element
21
+ # # idx: the index of the element (an integer starting at 0)
22
+ # # len: the length of the array being sorted
23
+ # ```
24
+ # If the collection is an object:
25
+ # ```
26
+ # def f(key, val, idx, len)
27
+ # # key: the key for the entry
28
+ # # val: the value for the entry
29
+ # # idx: the index of the entry (an integer starting at 0)
30
+ # # len: the number of keys/entries in the object
31
+ # ```
32
+ #
33
+ # Once the function has returning what value to rank/sort by, the
34
+ # sorting is done behind the scene by a (Ruby) sort. If the
35
+ # values returned are heterogeneous, the values are turned into
36
+ # their JSON representation before the sorting happens.
37
+ #
38
+ # ## see also
39
+ #
40
+ # sort, reverse, and shuffle
41
+
42
+ name 'sort_by'
43
+
44
+ protected
45
+
46
+ def receive_iteration
47
+
48
+ @node['res'] << payload['ret']
49
+ end
50
+
51
+ def iterator_result
52
+
53
+ res = @node['res']
54
+
55
+ classes = res.collect(&:class).uniq
56
+
57
+ res = res.collect { |e| e.is_a?(String) ? e : JSON.dump(e) } \
58
+ if classes.count > 1 || [ Hash ].include?(classes[0])
59
+
60
+ r = res.zip(@node['ocol'])
61
+ .sort_by(&:first)
62
+ .collect(&:last)
63
+
64
+ @node['ocol'].is_a?(Hash) ? Hash[r] : r
65
+ end
66
+ end
67
+
@@ -0,0 +1,39 @@
1
+
2
+ class Flor::Pro::Split < Flor::Procedure
3
+
4
+ names %w[ split ]
5
+
6
+ def pre_execute
7
+
8
+ @node['rets'] = []
9
+
10
+ unatt_unkeyed_children
11
+ end
12
+
13
+ def receive_last
14
+
15
+ str = nil
16
+ rex = nil
17
+ #
18
+ (@node['rets'] + [ node_payload_ret ])
19
+ .each { |r|
20
+ break if str && rex
21
+ if r.is_a?(String)
22
+ if str == nil
23
+ str = r
24
+ else
25
+ rex ||= r
26
+ end
27
+ elsif Flor.is_regex_tree?(r)
28
+ rex = Flor.to_regex(r)
29
+ end }
30
+ #
31
+ rex ||= /\s+/
32
+
33
+ fail Flor::FlorError.new("found no string to split", self) \
34
+ if str == nil
35
+
36
+ wrap('ret' => str.split(rex))
37
+ end
38
+ end
39
+
@@ -1,7 +1,7 @@
1
1
 
2
2
  class Flor::Pro::Stall < Flor::Procedure
3
3
  #
4
- # "stall" is mostly used in flor tests. It simply dead ends.
4
+ # Mostly used in flor tests. Stalls the current branch of execution.
5
5
  #
6
6
  # It receives its execution message, executes all its attributes but
7
7
  # does not answer to its parent procedure, effectively stalling
@@ -0,0 +1,123 @@
1
+
2
+ class Flor::Pro::Strings < Flor::Procedure
3
+ #
4
+ # "downcase", "upcase", "capitalize", etc.
5
+ #
6
+ # "downcase", "lowercase", "lowcase",
7
+ # "upcase", "uppercase",
8
+ # "capitalize",
9
+ # "snakecase", "snake_case",
10
+ # "trim", "strip"
11
+ #
12
+ # ```
13
+ # downcase 'HELLO' # => 'hello'
14
+ # 'HELLO'; downcase _ # => 'hello'
15
+ # 'HELLO'; downcase 'WORLD' # => 'world'
16
+ # # ...
17
+ # downcase 'WORLD' # => 'world'
18
+ # downcase 'WORLD' cap: true # => 'World'
19
+ # # ...
20
+ # capitalize 'hello world' # => 'Hello World'
21
+ # ```
22
+ #
23
+ # The `cap:` attribute, when set to something trueish, will make sure the
24
+ # resulting string(s) first char is capitalized. Not that "capitalize" itself
25
+ # will capitalize all the words (unlike Ruby's `String#capitalize`).
26
+ #
27
+ # ## objects and arrays
28
+ #
29
+ # Please note:
30
+ #
31
+ # ```
32
+ # [ "A" "BC" "D" ]; downcase _ # => [ 'a' 'bc' 'd' ]
33
+ # { a: "A" b: "BC" }; downcase _ # => { a: 'a', b: 'bc' }
34
+ # ```
35
+ #
36
+ # ## see also
37
+ #
38
+ # length, reverse
39
+
40
+ names %w[
41
+ downcase lowercase lowcase
42
+ upcase uppercase
43
+ capitalize
44
+ trim strip
45
+ snakecase snake_case
46
+ camelcase camelCase ]
47
+
48
+ def pre_execute
49
+
50
+ @node['ret'] = nil
51
+ @node['atts'] = []
52
+
53
+ unatt_unkeyed_children
54
+ end
55
+
56
+ def receive_payload_ret; payload['ret']; end # don't duplicate the ret
57
+
58
+ def receive_last
59
+
60
+ met =
61
+ case heap
62
+ when 'downcase', 'lowercase', 'lowcase' then :downcase
63
+ when 'upcase', 'uppercase' then :upcase
64
+ when 'capitalize' then :capitalize
65
+ when 'strip', 'trim' then :strip
66
+ when 'snakecase', 'snake_case' then :snakecase
67
+ when 'camelcase', 'camelCase' then :camelcase
68
+ else fail NotImplementedError.new("#{heap.inspect} not implemented")
69
+ end
70
+ ret =
71
+ process(
72
+ met,
73
+ @node['ret'] || node_payload_ret,
74
+ att('cap', 'capitalize'))
75
+
76
+ wrap('ret' => ret)
77
+ end
78
+
79
+ protected
80
+
81
+ def process(met, o, cap)
82
+
83
+ r =
84
+ case o
85
+ when String then StringWrapper.new(o).send(met)
86
+ when Array then o.collect { |e| process(met, e, cap) }
87
+ when Hash then o.inject({}) { |h, (k, v)| h[k] = process(met, v, cap); h }
88
+ else o
89
+ end
90
+
91
+ cap ?
92
+ r.capitalize :
93
+ r
94
+ end
95
+
96
+ class StringWrapper
97
+ extend ::Forwardable
98
+
99
+ def_delegators :@s, :downcase, :upcase, :strip
100
+
101
+ def initialize(s); @s = s; end
102
+
103
+ def camelcase
104
+
105
+ @s
106
+ .gsub(/_(.)/) { |_| $1.upcase }
107
+ end
108
+
109
+ def capitalize
110
+
111
+ @s
112
+ .gsub(/\b[a-z]/) { |c| c.upcase }
113
+ end
114
+
115
+ def snakecase
116
+
117
+ @s
118
+ .gsub(/([a-z])([A-Z])/) { |_| $1 + '_' + $2.downcase }
119
+ .gsub(/([A-Z])/) { |c| c.downcase }
120
+ end
121
+ end
122
+ end
123
+
@@ -0,0 +1,34 @@
1
+
2
+ class Flor::Pro::TimeStamp < Flor::Procedure
3
+ #
4
+ # Places a string timestamp in f.ret.
5
+ #
6
+ # ## timestamp
7
+ #
8
+ # Places the current UTC timestamp into `f.ret`.
9
+ #
10
+ # ```
11
+ # set f.timestamp # set the field "timestamp" to
12
+ # timestamp _ # something like "2018-08-13T08:04:06Z"
13
+ # ```
14
+ #
15
+ # ## ltimestamp
16
+ #
17
+ # ```
18
+ # set f.timestamp # set the field "timestamp" to
19
+ # ltimestamp _ # something like "2018-08-13T10:04:06"
20
+ # ```
21
+
22
+ names %w[ timestamp ltimestamp ]
23
+
24
+ def receive_last
25
+
26
+ payload['ret'] =
27
+ heap[0, 1] == 'l' ?
28
+ Time.now.strftime('%Y-%m-%dT%H:%M:%S') :
29
+ Flor.ststamp
30
+
31
+ wrap
32
+ end
33
+ end
34
+
@@ -1,8 +1,7 @@
1
1
 
2
2
  class Flor::Pro::ToArray < Flor::Procedure
3
3
  #
4
- # "to-array", turns an argument into an array, "to-object" turns it into
5
- # an object.
4
+ # Turns the argument into an array or an object.
6
5
  #
7
6
  # ```
8
7
  # to-array [ 0 1 2 ]
@@ -48,7 +47,7 @@ class Flor::Pro::ToArray < Flor::Procedure
48
47
  r = @node['ret']
49
48
 
50
49
  fail Flor::FlorError.new('to-object wants an array (or an object)', self) \
51
- unless r.is_a?(Array) || r.is_a?(Hash)
50
+ unless Flor.is_collection?(r)
52
51
 
53
52
  fail Flor::FlorError.new('to-object expects array with even length', self) \
54
53
  if r.is_a?(Array) && r.length.odd?
@@ -6,7 +6,7 @@ class Flor::Pro::Twig < Flor::Procedure
6
6
  def pre_execute
7
7
 
8
8
  unatt_unkeyed_children
9
- stringify_first_child
9
+ rep_first_child
10
10
  end
11
11
 
12
12
  def receive_first
@@ -0,0 +1,37 @@
1
+
2
+ class Flor::Pro::TypeOf < Flor::Procedure
3
+ #
4
+ # returns the type of argument or the incoming f.ret.
5
+ #
6
+ # ```
7
+ # type-of "hello" # ==> 'string'
8
+ # type-of 1 # ==> 'number'
9
+ # type-of 1.1 # ==> 'number'
10
+ # type-of [ 'a' 1 ] # ==> 'array'
11
+ # type-of { a: 1 } # ==> 'object'
12
+ #
13
+ # type {} # ==> 'object'
14
+ # type true # ==> 'boolean'
15
+ # ```
16
+ #
17
+ # ## see also
18
+ #
19
+ # array?, number?, ...
20
+
21
+ names %w[ type-of type ]
22
+
23
+ def pre_execute
24
+
25
+ @node['ret'] = receive_payload_ret
26
+
27
+ unatt_unkeyed_children
28
+ end
29
+
30
+ def receive_payload_ret; payload['ret']; end # don't duplicate the ret
31
+
32
+ def receive_last
33
+
34
+ wrap('ret' => Flor.type(@node['ret']).to_s)
35
+ end
36
+ end
37
+
@@ -1,8 +1,7 @@
1
1
 
2
2
  class Flor::Pro::Until < Flor::Procedure
3
3
  #
4
- # `until` loops until a condition evaluates to true.
5
- # `while` loops while a condition evaluates to true.
4
+ # Loops until or while a condiation evalutates to true.
6
5
  #
7
6
  # ```
8
7
  # set i 0
@@ -104,7 +103,8 @@ class Flor::Pro::Until < Flor::Procedure
104
103
 
105
104
  def cancel_when_closed
106
105
 
107
- return [] unless @message['flavour'] == 'break'
106
+ return cancel if node_status_flavour == 'on-error'
107
+ return [] if @message['flavour'] != 'break'
108
108
 
109
109
  cancel
110
110
  end
@@ -43,8 +43,8 @@ class Flor::Pro::Cancel < Flor::Procedure
43
43
 
44
44
  targets =
45
45
  @node['atts']
46
- .select { |k, v| k == nil }
47
- .inject([]) { |a, (k, v)|
46
+ .select { |k, _| k == nil }
47
+ .inject([]) { |a, (_, v)|
48
48
  v = Array(v)
49
49
  a.concat(v) if v.all? { |e| e.is_a?(String) }
50
50
  a } +
@@ -55,7 +55,7 @@ class Flor::Pro::Cancel < Flor::Procedure
55
55
  nids += tags_to_nids(tags)
56
56
  nids = nids.uniq
57
57
 
58
- fla = @node['heap']
58
+ fla = heap
59
59
 
60
60
  messages = nids
61
61
  .collect { |nid| wrap_cancel('nid' => nid, 'flavour' => fla)[0] }
@@ -1,5 +1,34 @@
1
1
 
2
2
  class Flor::Pro::Ccollect < Flor::Macro::Iterator
3
+ #
4
+ # A concurrent version of [collect](collect.md).
5
+ #
6
+ # Whereas "collect" executes its children one by one and then yields
7
+ # an array with the result of each child, "ccollect" executes the children
8
+ # concurrently.
9
+ #
10
+ # In the example below, Alice, Bob, and Charly are concurrently tasked
11
+ # with some analysis work. The field `ret` coming out of the "ccollect"
12
+ # will be an array composed of the `f.ret` of each "task" call.
13
+ # ```
14
+ # sequence
15
+ # ccollect [ 'alice' 'bob' 'charly' ]
16
+ # notify elt 'you received a task'
17
+ # task elt "initiate analysis for project $(project.id) as #$(idx + 1)"
18
+ # ```
19
+ #
20
+ # Like "collect", the block iterating over an array will receive the `elt`,
21
+ # `idx` and `len` variables (current element, current element index (starting
22
+ # at zero), and length of the collection).
23
+ #
24
+ # When iterating over an object (a hash), the variables will be `key`, `val`,
25
+ # `idx`, and `len`.
26
+ #
27
+ # "ccollect" is actually a macro rewriting itself to a [cmap](cmap.md).
28
+ #
29
+ # ## see also
30
+ #
31
+ # Collect, map, cmap.
3
32
 
4
33
  name 'ccollect'
5
34
 
@@ -1,56 +1,112 @@
1
1
 
2
2
  class Flor::Pro::Cmap < Flor::Procedure
3
+ #
4
+ # Concurrent version of "map". Spins a concurrent child for each
5
+ # element of the incoming/argument collection.
6
+ #
7
+ # ```
8
+ # cmap [ 1 2 3 ]
9
+ # def x \ * x 2
10
+ # # yields: [ 2, 4, 6 ]
11
+ #
12
+ # [ 1 2 3 ]
13
+ # cmap (def x \ * x 2)
14
+ # # yields: [ 2, 4, 6 ]
15
+ #
16
+ # define double x \ * x 2
17
+ # cmap double [ 1 2 3 ]
18
+ # # yields: [ 2, 4, 6 ]
19
+ # ```
20
+ #
21
+ # "cmap" is over when all the children have answered. For more complex
22
+ # concurrent behaviours, look at [concurrence](concurrence.md).
23
+ #
24
+ # ## see also
25
+ #
26
+ # Map, concurrence.
3
27
 
4
28
  name 'cmap'
5
29
 
6
30
  def pre_execute
7
31
 
8
- @node['atts'] = []
32
+ @node['args'] = []
33
+ @node['result'] = nil
9
34
 
10
- @node['fun'] = nil
11
- @node['col'] = []
35
+ unatt_unkeyed_children
12
36
  end
13
37
 
14
38
  def receive_non_att
15
39
 
16
- if @node['fun']
17
- receive_elt
40
+ if @node['result']
41
+ receive_ret
18
42
  else
19
- receive_fun
43
+ @node['args'] << payload['ret']
44
+ super
45
+ end
46
+ end
47
+
48
+ def receive_last
49
+
50
+ if @node['result']
51
+ super
52
+ else
53
+ receive_last_argument
20
54
  end
21
55
  end
22
56
 
23
57
  protected
24
58
 
25
- def receive_fun
59
+ def receive_last_argument
26
60
 
27
- fun = payload['ret']
61
+ col = nil
62
+ fun = nil
63
+ @node['args'].each do |a|
64
+ if Flor.is_func_tree?(a)
65
+ fun = a
66
+ elsif Flor.is_collection?(a)
67
+ col = a
68
+ end
69
+ end
70
+ col ||= node_payload_ret
28
71
 
29
- fail Flor::FlorError.new("'#{tree[0]}' expects a function", self) \
72
+ fail Flor::FlorError.new("collection not given to #{heap.inspect}", self) \
73
+ unless Flor.is_collection?(col)
74
+ return wrap('ret' => col) \
30
75
  unless Flor.is_func_tree?(fun)
31
76
 
32
- @node['fun'] = fun
77
+ @node['cnt'] = col.size
78
+ @node['result'] = []
33
79
 
34
- att(nil)
80
+ col
35
81
  .collect
36
82
  .with_index { |e, i|
37
- apply(fun, [ e, i ], tree[2], vars: { 'idx' => i }) }
83
+ apply(fun, determine_iteration_args(col, i), tree[2]) }
38
84
  .flatten(1)
39
85
  end
40
86
 
41
- def receive_elt
87
+ def determine_iteration_args(col, idx)
42
88
 
43
- idx =
44
- (message['rvars'] && message['rvars']['idx']) ||
45
- Flor.sub_nid(message['from']) - 1 # fall back :-(
89
+ args =
90
+ if col.is_a?(Array)
91
+ [ [ 'elt', col[idx] ] ]
92
+ else
93
+ e = col.to_a[idx]
94
+ [ [ 'key', e[0] ], [ 'val', e[1] ] ]
95
+ end
96
+ args << [ 'idx', idx ]
97
+ args << [ 'len', col.length ]
98
+
99
+ args
100
+ end
46
101
 
47
- @node['col'][idx] = payload['ret']
102
+ def receive_ret
48
103
 
49
- return [] if cnodes_any?
104
+ @node['result'] << [ from_sub_nid, payload['ret'] ]
105
+ @node['cnt'] = @node['cnt'] - 1
50
106
 
51
- payload['ret'] = @node['col']
107
+ return [] if @node['cnt'] > 0 # still waiting for answers
52
108
 
53
- wrap
109
+ wrap('ret' => @node['result'].sort_by(&:first).collect(&:last)) # over
54
110
  end
55
111
  end
56
112