flor 0.14.0 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +9 -0
- data/CREDITS.md +21 -0
- data/LICENSE.txt +4 -1
- data/Makefile +4 -0
- data/README.md +8 -0
- data/flor.gemspec +10 -10
- data/lib/flor.rb +2 -2
- data/lib/flor/changes.rb +3 -3
- data/lib/flor/colours.rb +14 -8
- data/lib/flor/conf.rb +63 -58
- data/lib/flor/core.rb +4 -4
- data/lib/flor/core/executor.rb +65 -29
- data/lib/flor/core/node.rb +37 -20
- data/lib/flor/core/procedure.rb +182 -40
- data/lib/flor/core/texecutor.rb +125 -52
- data/lib/flor/djan.rb +111 -82
- data/lib/flor/dollar.rb +31 -30
- data/lib/flor/flor.rb +314 -237
- data/lib/flor/id.rb +7 -2
- data/lib/flor/log.rb +250 -245
- data/lib/flor/parser.rb +72 -38
- data/lib/flor/pcore/_arr.rb +10 -10
- data/lib/flor/pcore/_att.rb +49 -14
- data/lib/flor/pcore/_coll.rb +18 -0
- data/lib/flor/pcore/_obj.rb +23 -7
- data/lib/flor/pcore/_pat_.rb +1 -1
- data/lib/flor/pcore/_pat_guard.rb +8 -0
- data/lib/flor/pcore/_pat_obj.rb +3 -3
- data/lib/flor/pcore/_pat_or.rb +4 -0
- data/lib/flor/pcore/_pat_regex.rb +24 -0
- data/lib/flor/pcore/_skip.rb +4 -0
- data/lib/flor/pcore/_val.rb +0 -1
- data/lib/flor/pcore/all.rb +111 -0
- data/lib/flor/pcore/any.rb +83 -0
- data/lib/flor/pcore/arith.rb +35 -6
- data/lib/flor/pcore/break.rb +39 -1
- data/lib/flor/pcore/case.rb +82 -4
- data/lib/flor/pcore/cmp.rb +7 -7
- data/lib/flor/pcore/collect.rb +50 -0
- data/lib/flor/pcore/cond.rb +17 -3
- data/lib/flor/pcore/cursor.rb +8 -2
- data/lib/flor/pcore/detect.rb +45 -0
- data/lib/flor/pcore/each.rb +52 -0
- data/lib/flor/pcore/empty.rb +60 -0
- data/lib/flor/pcore/filter.rb +94 -0
- data/lib/flor/pcore/find.rb +67 -0
- data/lib/flor/pcore/for_each.rb +65 -0
- data/lib/flor/pcore/includes.rb +32 -0
- data/lib/flor/pcore/inject.rb +55 -0
- data/lib/flor/pcore/iterator.rb +151 -0
- data/lib/flor/pcore/keys.rb +60 -0
- data/lib/flor/pcore/length.rb +34 -7
- data/lib/flor/pcore/logo.rb +18 -0
- data/lib/flor/pcore/loop.rb +4 -0
- data/lib/flor/pcore/map.rb +77 -46
- data/lib/flor/pcore/match.rb +8 -2
- data/lib/flor/pcore/matchr.rb +4 -5
- data/lib/flor/pcore/move.rb +3 -3
- data/lib/flor/pcore/noeval.rb +13 -0
- data/lib/flor/pcore/not.rb +16 -0
- data/lib/flor/pcore/on.rb +172 -0
- data/lib/flor/pcore/on_cancel.rb +54 -0
- data/lib/flor/pcore/on_error.rb +68 -0
- data/lib/flor/pcore/rand.rb +2 -2
- data/lib/flor/pcore/range.rb +2 -1
- data/lib/flor/pcore/reduce.rb +124 -0
- data/lib/flor/pcore/reverse.rb +46 -0
- data/lib/flor/pcore/select.rb +72 -0
- data/lib/flor/pcore/set.rb +8 -0
- data/lib/flor/pcore/stall.rb +10 -0
- data/lib/flor/pcore/to_array.rb +61 -0
- data/lib/flor/pcore/until.rb +34 -0
- data/lib/flor/punit/cancel.rb +30 -5
- data/lib/flor/punit/ccollect.rb +11 -0
- data/lib/flor/punit/cmap.rb +10 -5
- data/lib/flor/punit/concurrence.rb +42 -51
- data/lib/flor/punit/cron.rb +33 -0
- data/lib/flor/punit/do_trap.rb +42 -0
- data/lib/flor/punit/every.rb +48 -13
- data/lib/flor/punit/graft.rb +3 -3
- data/lib/flor/punit/on_timeout.rb +38 -0
- data/lib/flor/punit/schedule.rb +69 -6
- data/lib/flor/punit/signal.rb +54 -0
- data/lib/flor/punit/sleep.rb +1 -1
- data/lib/flor/punit/task.rb +4 -1
- data/lib/flor/punit/trap.rb +188 -13
- data/lib/flor/tools/shell.rb +408 -62
- data/lib/flor/tools/shell_out.rb +31 -0
- data/lib/flor/unit.rb +1 -1
- data/lib/flor/unit/caller.rb +177 -0
- data/lib/flor/unit/executor.rb +1 -0
- data/lib/flor/unit/ganger.rb +15 -21
- data/lib/flor/unit/hook.rb +1 -1
- data/lib/flor/unit/hooker.rb +22 -10
- data/lib/flor/unit/loader.rb +22 -22
- data/lib/flor/unit/logger.rb +63 -36
- data/lib/flor/unit/models.rb +6 -1
- data/lib/flor/unit/models/execution.rb +12 -1
- data/lib/flor/unit/models/message.rb +7 -0
- data/lib/flor/unit/models/trap.rb +31 -17
- data/lib/flor/unit/scheduler.rb +18 -10
- data/lib/flor/unit/storage.rb +83 -23
- data/lib/flor/unit/waiter.rb +1 -2
- metadata +96 -52
- data/lib/flor/deep.rb +0 -144
- data/lib/flor/punit/on.rb +0 -57
- data/lib/flor/unit/runner.rb +0 -84
- data/match.md +0 -22
@@ -0,0 +1,65 @@
|
|
1
|
+
|
2
|
+
require 'flor/pcore/iterator'
|
3
|
+
|
4
|
+
|
5
|
+
class Flor::Pro::ForEach < Flor::Pro::Iterator
|
6
|
+
#
|
7
|
+
# Calls a function for each element in the argument collection.
|
8
|
+
#
|
9
|
+
# When the "for-each" ends, `f.ret` is pointing back to the argument
|
10
|
+
# collection.
|
11
|
+
#
|
12
|
+
# ```
|
13
|
+
# set l []
|
14
|
+
# for-each [ 0 1 2 3 4 5 6 7 ]
|
15
|
+
# def x
|
16
|
+
# pushr l (2 * x) if x % 2 == 0
|
17
|
+
# ```
|
18
|
+
# the var `l` will yield `[ 0, 4, 8, 12 ]` after the `for-each`
|
19
|
+
# the field `ret` will yield `[ 0, 1, 2, 3, 4, 5, 6, 7 ]`.
|
20
|
+
#
|
21
|
+
# ```
|
22
|
+
# set r []
|
23
|
+
# for-each { a: 'A', b: 'B', c: 'C' }
|
24
|
+
# def k v i l # key, val, idx, len
|
25
|
+
# pushr r (+ k v (+ i 1) '/' l)
|
26
|
+
# ```
|
27
|
+
# the var `r` will yield `[ 'aA1/3', 'bB2/3', 'cC3/3' ]` after the `for-each`
|
28
|
+
# the field `ret` will yield `{ 'a': 'A', 'b': 'B', 'c': 'C' }`.
|
29
|
+
#
|
30
|
+
# ## iterating and functions
|
31
|
+
#
|
32
|
+
# Iterating functions accept 0 to 3 arguments when iterating over an
|
33
|
+
# array and 0 to 4 arguments when iterating over an object.
|
34
|
+
#
|
35
|
+
# Those arguments are `[ value, index, length ]` for arrays.
|
36
|
+
# They are `[ key, value, index, length ]` for objects.
|
37
|
+
#
|
38
|
+
# The corresponding `key`, `val`, `idx` and `len` variables are also
|
39
|
+
# set in the closure for the function call.
|
40
|
+
#
|
41
|
+
# ## see also
|
42
|
+
#
|
43
|
+
# each.
|
44
|
+
|
45
|
+
name 'for-each'
|
46
|
+
|
47
|
+
protected
|
48
|
+
|
49
|
+
def pre_iterator
|
50
|
+
|
51
|
+
# nothing to do
|
52
|
+
end
|
53
|
+
|
54
|
+
def receive_iteration
|
55
|
+
|
56
|
+
# nothing to do
|
57
|
+
end
|
58
|
+
|
59
|
+
def iterator_result
|
60
|
+
|
61
|
+
@node['ocol']
|
62
|
+
# back to the original collection
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
|
2
|
+
class Flor::Pro::Includes < Flor::Procedure
|
3
|
+
|
4
|
+
name 'includes?'
|
5
|
+
|
6
|
+
def pre_execute
|
7
|
+
|
8
|
+
@node['rets'] = []
|
9
|
+
|
10
|
+
unatt_unkeyed_children
|
11
|
+
end
|
12
|
+
|
13
|
+
def receive_last
|
14
|
+
|
15
|
+
col = nil
|
16
|
+
elt = :nil
|
17
|
+
|
18
|
+
@node['rets'].each do |ret|
|
19
|
+
if col == nil && (ret.is_a?(Array) || ret.is_a?(Hash))
|
20
|
+
col = ret
|
21
|
+
elsif elt == :nil
|
22
|
+
elt = ret
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
fail Flor::FlorError.new('missing collection', self) if col == nil
|
27
|
+
fail Flor::FlorError.new('missing element', self) if elt == :nil
|
28
|
+
|
29
|
+
wrap_reply('ret' => col.include?(elt))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
@@ -0,0 +1,55 @@
|
|
1
|
+
|
2
|
+
require 'flor/pcore/iterator'
|
3
|
+
|
4
|
+
|
5
|
+
class Flor::Pro::Inject < Flor::Macro::Iterator
|
6
|
+
#
|
7
|
+
# Inject is a simplified version of [reduce](reduce.md).
|
8
|
+
#
|
9
|
+
# Inject takes a collection and a block. It reduces the collection
|
10
|
+
# to a single result thanks to the block.
|
11
|
+
#
|
12
|
+
# The block is run for each element in the collection, it's passed
|
13
|
+
# `res` and `elt`. `res` is the result, the accumulator, `elt`
|
14
|
+
# is the current element in the collection.
|
15
|
+
#
|
16
|
+
# The block must return the result for the next iteration.
|
17
|
+
#
|
18
|
+
# ```
|
19
|
+
# inject [ '0', 1, 'b', 3 ]
|
20
|
+
# res + elt
|
21
|
+
# # --> "01b3"
|
22
|
+
# ```
|
23
|
+
#
|
24
|
+
# An initial value is accepted (generally after the collection)
|
25
|
+
#
|
26
|
+
# ```
|
27
|
+
# inject [ 0, 1, 2, 3, 4 ] 10
|
28
|
+
# res + elt
|
29
|
+
# # --> 20
|
30
|
+
# ```
|
31
|
+
#
|
32
|
+
# ## iterating blocks
|
33
|
+
#
|
34
|
+
# Iterating blocks are given 3 to 4 local variables.
|
35
|
+
#
|
36
|
+
# A block iterating over an array will receive `elt` (the current element
|
37
|
+
# of the iteration), `idx` (the zero-based index of the current element),
|
38
|
+
# and `len` (the length of the array).
|
39
|
+
#
|
40
|
+
# A block iterating over an object will receive `key` (the current string
|
41
|
+
# key), `val` (the current value), `idx` (the zero-based index of the
|
42
|
+
# current key/val), and `len` (the length of the object).
|
43
|
+
#
|
44
|
+
# ## see also
|
45
|
+
#
|
46
|
+
# Reduce.
|
47
|
+
|
48
|
+
name 'inject'
|
49
|
+
|
50
|
+
def rewrite_tree
|
51
|
+
|
52
|
+
rewrite_iterator_tree('reduce')
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
@@ -0,0 +1,151 @@
|
|
1
|
+
|
2
|
+
class Flor::Pro::Iterator < Flor::Procedure
|
3
|
+
|
4
|
+
def pre_execute
|
5
|
+
|
6
|
+
@node['vars'] ||= {}
|
7
|
+
|
8
|
+
@node['args'] = [] # before iterating, arguments are collected
|
9
|
+
|
10
|
+
@node['ocol'] = nil # original collection
|
11
|
+
@node['fun'] = nil # function
|
12
|
+
|
13
|
+
@node['col'] = nil # collection
|
14
|
+
@node['idx'] = -1
|
15
|
+
|
16
|
+
unatt_unkeyed_children
|
17
|
+
end
|
18
|
+
|
19
|
+
def receive_non_att
|
20
|
+
|
21
|
+
if @node['args']
|
22
|
+
receive_argument
|
23
|
+
else
|
24
|
+
receive_iteration
|
25
|
+
iterate
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
def receive_argument
|
32
|
+
|
33
|
+
@node['args'] << payload['ret']
|
34
|
+
|
35
|
+
if children[@ncid]
|
36
|
+
execute_child(@ncid)
|
37
|
+
else
|
38
|
+
iterate
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def iterate
|
43
|
+
|
44
|
+
prepare_iterations unless @node['ocol']
|
45
|
+
|
46
|
+
return no_iterate unless @node['fun']
|
47
|
+
|
48
|
+
@node['idx'] += 1
|
49
|
+
@node['mtime'] = Flor.tstamp
|
50
|
+
|
51
|
+
return end_iterator if iterator_over?
|
52
|
+
|
53
|
+
apply_iteration
|
54
|
+
end
|
55
|
+
|
56
|
+
def function_mandatory?
|
57
|
+
|
58
|
+
true
|
59
|
+
end
|
60
|
+
|
61
|
+
def prepare_iterations
|
62
|
+
|
63
|
+
prepare_iterator
|
64
|
+
|
65
|
+
@node['args']
|
66
|
+
.each { |a|
|
67
|
+
if Flor.is_func_tree?(a)
|
68
|
+
@node['fun'] ||= a
|
69
|
+
elsif a.is_a?(Array) || a.is_a?(Hash)
|
70
|
+
@node['ocol'] ||= a
|
71
|
+
end }
|
72
|
+
|
73
|
+
@node['ocol'] ||= node_payload_ret
|
74
|
+
ocol = @node['ocol']
|
75
|
+
|
76
|
+
fail Flor::FlorError.new(
|
77
|
+
"function not given to #{heap.inspect}", self
|
78
|
+
) if function_mandatory? && ( ! @node['fun'])
|
79
|
+
fail Flor::FlorError.new(
|
80
|
+
"collection not given to #{heap.inspect}", self
|
81
|
+
) unless ocol.is_a?(Array) || ocol.is_a?(Hash)
|
82
|
+
|
83
|
+
@node['col'] = Flor.to_coll(@node['ocol']) if @node['fun']
|
84
|
+
@node['args'] = nil
|
85
|
+
end
|
86
|
+
|
87
|
+
def prepare_iterator
|
88
|
+
|
89
|
+
@node['res'] = []
|
90
|
+
end
|
91
|
+
|
92
|
+
def apply_iteration
|
93
|
+
|
94
|
+
vars = determine_iteration_vars
|
95
|
+
|
96
|
+
args = vars.values
|
97
|
+
vars.each { |k, v| @node['vars'][k] = v }
|
98
|
+
|
99
|
+
apply(@node['fun'], args, tree[2])
|
100
|
+
end
|
101
|
+
|
102
|
+
def determine_iteration_vars
|
103
|
+
|
104
|
+
idx = @node['idx']
|
105
|
+
elt = @node['col'][idx]
|
106
|
+
len = @node['col'].length
|
107
|
+
|
108
|
+
if @node['ocol'].is_a?(Array)
|
109
|
+
{ 'elt' => elt, 'idx' => idx, 'len' => len }
|
110
|
+
else
|
111
|
+
{ 'key' => elt[0], 'val' => elt[1], 'idx' => idx, 'len' => len }
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def iterator_over?
|
116
|
+
|
117
|
+
@node['idx'] == @node['col'].size
|
118
|
+
end
|
119
|
+
|
120
|
+
def end_iterator
|
121
|
+
|
122
|
+
wrap_reply('ret' => iterator_result)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
|
127
|
+
class Flor::Macro::Iterator < Flor::Macro
|
128
|
+
|
129
|
+
def rewrite_iterator_tree(procedure_name)
|
130
|
+
|
131
|
+
atts = att_children
|
132
|
+
|
133
|
+
l = tree[2]
|
134
|
+
|
135
|
+
th = [ procedure_name, [], l, *tree[3] ]
|
136
|
+
atts.each { |ac| th[1] << Flor.dup(ac) }
|
137
|
+
|
138
|
+
if non_att_children.any?
|
139
|
+
|
140
|
+
td = [ 'def', [], l ]
|
141
|
+
td[1] << [ '_att', [ [ 'res', [], l ] ], l ] if procedure_name == 'reduce'
|
142
|
+
td[1] << [ '_att', [ [ 'elt', [], l ] ], l ]
|
143
|
+
non_att_children.each { |nac| td[1] << Flor.dup(nac) }
|
144
|
+
|
145
|
+
th[1] << td
|
146
|
+
end
|
147
|
+
|
148
|
+
th
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
@@ -0,0 +1,60 @@
|
|
1
|
+
|
2
|
+
class Flor::Pro::Keys < Flor::Procedure
|
3
|
+
#
|
4
|
+
# Returns the "keys" or the "values" of an object.
|
5
|
+
#
|
6
|
+
# ```
|
7
|
+
# keys { a: 'A', b: 'B' }
|
8
|
+
# # f.ret --> [ 'a', 'b' ]
|
9
|
+
# values { a: 'A', b: 'B' }
|
10
|
+
# # f.ret --> [ 'A', 'B' ]
|
11
|
+
# ```
|
12
|
+
#
|
13
|
+
# When used against an array, the indexes will be the numerical indexes
|
14
|
+
# 0 to array length - 1.
|
15
|
+
#
|
16
|
+
# ```
|
17
|
+
# keys [ 1, 'to', true ]
|
18
|
+
# # f.ret -> [ 0, 1, 2 ]
|
19
|
+
# values [ 1, 'to', true ]
|
20
|
+
# # f.ret -> [ 1, 'to', true ]
|
21
|
+
# ```
|
22
|
+
#
|
23
|
+
# When used against something that is neither an object nor an array
|
24
|
+
# it will fail.
|
25
|
+
#
|
26
|
+
# ## see also
|
27
|
+
#
|
28
|
+
# length
|
29
|
+
|
30
|
+
names %w{ keys values }
|
31
|
+
|
32
|
+
def pre_execute
|
33
|
+
|
34
|
+
@node['ret'] = receive_payload_ret
|
35
|
+
|
36
|
+
unatt_unkeyed_children
|
37
|
+
end
|
38
|
+
|
39
|
+
def receive_last
|
40
|
+
|
41
|
+
ret = @node['ret']
|
42
|
+
|
43
|
+
fail Flor::FlorError.new(
|
44
|
+
"no argument given", self
|
45
|
+
) if ret.nil?
|
46
|
+
fail Flor::FlorError.new(
|
47
|
+
"received argument of class #{ret.class}, no #{heap}", self
|
48
|
+
) unless ret.is_a?(Array) || ret.is_a?(Hash)
|
49
|
+
|
50
|
+
r =
|
51
|
+
if ret.is_a?(Hash)
|
52
|
+
heap == 'keys' ? ret.keys : ret.values
|
53
|
+
else
|
54
|
+
heap == 'keys' ? (0..ret.length - 1).to_a : ret
|
55
|
+
end
|
56
|
+
|
57
|
+
wrap_reply('ret' => r)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
data/lib/flor/pcore/length.rb
CHANGED
@@ -1,18 +1,45 @@
|
|
1
1
|
|
2
2
|
class Flor::Pro::Length < Flor::Procedure
|
3
|
+
#
|
4
|
+
# Returns the length of its last collection argument or
|
5
|
+
# the length of the incoming f.ret
|
6
|
+
#
|
7
|
+
# ```
|
8
|
+
# length [ 0 1 2 3 ]
|
9
|
+
# # f.ret ==> 4
|
10
|
+
#
|
11
|
+
# { a: 'A', b: 'B', c: 'C' }
|
12
|
+
# length _
|
13
|
+
# # f.ret ==> 3
|
14
|
+
# ```
|
15
|
+
#
|
16
|
+
# It will fail unless "length" receives a (non-attribute) argument
|
17
|
+
# that has a length.
|
18
|
+
#
|
19
|
+
# Has the "size" alias.
|
3
20
|
|
4
|
-
|
21
|
+
names %w[ length size ]
|
5
22
|
|
6
|
-
def
|
23
|
+
def pre_execute
|
24
|
+
|
25
|
+
@node['ret'] = receive_payload_ret
|
26
|
+
|
27
|
+
unatt_unkeyed_children
|
28
|
+
end
|
29
|
+
|
30
|
+
def receive_payload_ret
|
7
31
|
|
8
32
|
r = payload['ret']
|
33
|
+
r.respond_to?(:length) ? r.length : false
|
34
|
+
end
|
35
|
+
|
36
|
+
def receive_last
|
9
37
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
38
|
+
r =
|
39
|
+
@node['ret'] ||
|
40
|
+
fail(Flor::FlorError.new('found no argument that has a length', self))
|
14
41
|
|
15
|
-
|
42
|
+
wrap_reply('ret' => r)
|
16
43
|
end
|
17
44
|
end
|
18
45
|
|
data/lib/flor/pcore/logo.rb
CHANGED
@@ -1,5 +1,23 @@
|
|
1
1
|
|
2
2
|
class Flor::Pro::Logo < Flor::Procedure
|
3
|
+
#
|
4
|
+
# When `and` evaluates the children and returns false as soon
|
5
|
+
# as one of returns a falsy value. Returns true else.
|
6
|
+
# When `or` evaluates the children and returns true as soon
|
7
|
+
# as one of them returns a trueish value. Returns false else.
|
8
|
+
#
|
9
|
+
# ```
|
10
|
+
# and
|
11
|
+
# false
|
12
|
+
# true
|
13
|
+
# # => evalutes to false
|
14
|
+
# ```
|
15
|
+
#
|
16
|
+
# ```
|
17
|
+
# and (check_this _) (check_that _)
|
18
|
+
# ```
|
19
|
+
#
|
20
|
+
# Gives priority to `and` over `or`.
|
3
21
|
|
4
22
|
names %w[ and or ]
|
5
23
|
|