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
@@ -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
|
+
|
data/lib/flor/pcore/stall.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
|
2
2
|
class Flor::Pro::Stall < Flor::Procedure
|
3
3
|
#
|
4
|
-
#
|
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
|
+
|
data/lib/flor/pcore/to_array.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
|
2
2
|
class Flor::Pro::ToArray < Flor::Procedure
|
3
3
|
#
|
4
|
-
#
|
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
|
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?
|
data/lib/flor/pcore/twig.rb
CHANGED
@@ -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
|
+
|
data/lib/flor/pcore/until.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
|
2
2
|
class Flor::Pro::Until < Flor::Procedure
|
3
3
|
#
|
4
|
-
#
|
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
|
106
|
+
return cancel if node_status_flavour == 'on-error'
|
107
|
+
return [] if @message['flavour'] != 'break'
|
108
108
|
|
109
109
|
cancel
|
110
110
|
end
|
data/lib/flor/punit/cancel.rb
CHANGED
@@ -43,8 +43,8 @@ class Flor::Pro::Cancel < Flor::Procedure
|
|
43
43
|
|
44
44
|
targets =
|
45
45
|
@node['atts']
|
46
|
-
.select { |k,
|
47
|
-
.inject([]) { |a, (
|
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 =
|
58
|
+
fla = heap
|
59
59
|
|
60
60
|
messages = nids
|
61
61
|
.collect { |nid| wrap_cancel('nid' => nid, 'flavour' => fla)[0] }
|
data/lib/flor/punit/ccollect.rb
CHANGED
@@ -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
|
|
data/lib/flor/punit/cmap.rb
CHANGED
@@ -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['
|
32
|
+
@node['args'] = []
|
33
|
+
@node['result'] = nil
|
9
34
|
|
10
|
-
|
11
|
-
@node['col'] = []
|
35
|
+
unatt_unkeyed_children
|
12
36
|
end
|
13
37
|
|
14
38
|
def receive_non_att
|
15
39
|
|
16
|
-
if @node['
|
17
|
-
|
40
|
+
if @node['result']
|
41
|
+
receive_ret
|
18
42
|
else
|
19
|
-
|
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
|
59
|
+
def receive_last_argument
|
26
60
|
|
27
|
-
|
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("
|
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['
|
77
|
+
@node['cnt'] = col.size
|
78
|
+
@node['result'] = []
|
33
79
|
|
34
|
-
|
80
|
+
col
|
35
81
|
.collect
|
36
82
|
.with_index { |e, i|
|
37
|
-
apply(fun,
|
83
|
+
apply(fun, determine_iteration_args(col, i), tree[2]) }
|
38
84
|
.flatten(1)
|
39
85
|
end
|
40
86
|
|
41
|
-
def
|
87
|
+
def determine_iteration_args(col, idx)
|
42
88
|
|
43
|
-
|
44
|
-
(
|
45
|
-
|
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
|
-
|
102
|
+
def receive_ret
|
48
103
|
|
49
|
-
|
104
|
+
@node['result'] << [ from_sub_nid, payload['ret'] ]
|
105
|
+
@node['cnt'] = @node['cnt'] - 1
|
50
106
|
|
51
|
-
|
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
|
|