flor 0.14.0 → 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +9 -0
  3. data/CREDITS.md +21 -0
  4. data/LICENSE.txt +4 -1
  5. data/Makefile +4 -0
  6. data/README.md +8 -0
  7. data/flor.gemspec +10 -10
  8. data/lib/flor.rb +2 -2
  9. data/lib/flor/changes.rb +3 -3
  10. data/lib/flor/colours.rb +14 -8
  11. data/lib/flor/conf.rb +63 -58
  12. data/lib/flor/core.rb +4 -4
  13. data/lib/flor/core/executor.rb +65 -29
  14. data/lib/flor/core/node.rb +37 -20
  15. data/lib/flor/core/procedure.rb +182 -40
  16. data/lib/flor/core/texecutor.rb +125 -52
  17. data/lib/flor/djan.rb +111 -82
  18. data/lib/flor/dollar.rb +31 -30
  19. data/lib/flor/flor.rb +314 -237
  20. data/lib/flor/id.rb +7 -2
  21. data/lib/flor/log.rb +250 -245
  22. data/lib/flor/parser.rb +72 -38
  23. data/lib/flor/pcore/_arr.rb +10 -10
  24. data/lib/flor/pcore/_att.rb +49 -14
  25. data/lib/flor/pcore/_coll.rb +18 -0
  26. data/lib/flor/pcore/_obj.rb +23 -7
  27. data/lib/flor/pcore/_pat_.rb +1 -1
  28. data/lib/flor/pcore/_pat_guard.rb +8 -0
  29. data/lib/flor/pcore/_pat_obj.rb +3 -3
  30. data/lib/flor/pcore/_pat_or.rb +4 -0
  31. data/lib/flor/pcore/_pat_regex.rb +24 -0
  32. data/lib/flor/pcore/_skip.rb +4 -0
  33. data/lib/flor/pcore/_val.rb +0 -1
  34. data/lib/flor/pcore/all.rb +111 -0
  35. data/lib/flor/pcore/any.rb +83 -0
  36. data/lib/flor/pcore/arith.rb +35 -6
  37. data/lib/flor/pcore/break.rb +39 -1
  38. data/lib/flor/pcore/case.rb +82 -4
  39. data/lib/flor/pcore/cmp.rb +7 -7
  40. data/lib/flor/pcore/collect.rb +50 -0
  41. data/lib/flor/pcore/cond.rb +17 -3
  42. data/lib/flor/pcore/cursor.rb +8 -2
  43. data/lib/flor/pcore/detect.rb +45 -0
  44. data/lib/flor/pcore/each.rb +52 -0
  45. data/lib/flor/pcore/empty.rb +60 -0
  46. data/lib/flor/pcore/filter.rb +94 -0
  47. data/lib/flor/pcore/find.rb +67 -0
  48. data/lib/flor/pcore/for_each.rb +65 -0
  49. data/lib/flor/pcore/includes.rb +32 -0
  50. data/lib/flor/pcore/inject.rb +55 -0
  51. data/lib/flor/pcore/iterator.rb +151 -0
  52. data/lib/flor/pcore/keys.rb +60 -0
  53. data/lib/flor/pcore/length.rb +34 -7
  54. data/lib/flor/pcore/logo.rb +18 -0
  55. data/lib/flor/pcore/loop.rb +4 -0
  56. data/lib/flor/pcore/map.rb +77 -46
  57. data/lib/flor/pcore/match.rb +8 -2
  58. data/lib/flor/pcore/matchr.rb +4 -5
  59. data/lib/flor/pcore/move.rb +3 -3
  60. data/lib/flor/pcore/noeval.rb +13 -0
  61. data/lib/flor/pcore/not.rb +16 -0
  62. data/lib/flor/pcore/on.rb +172 -0
  63. data/lib/flor/pcore/on_cancel.rb +54 -0
  64. data/lib/flor/pcore/on_error.rb +68 -0
  65. data/lib/flor/pcore/rand.rb +2 -2
  66. data/lib/flor/pcore/range.rb +2 -1
  67. data/lib/flor/pcore/reduce.rb +124 -0
  68. data/lib/flor/pcore/reverse.rb +46 -0
  69. data/lib/flor/pcore/select.rb +72 -0
  70. data/lib/flor/pcore/set.rb +8 -0
  71. data/lib/flor/pcore/stall.rb +10 -0
  72. data/lib/flor/pcore/to_array.rb +61 -0
  73. data/lib/flor/pcore/until.rb +34 -0
  74. data/lib/flor/punit/cancel.rb +30 -5
  75. data/lib/flor/punit/ccollect.rb +11 -0
  76. data/lib/flor/punit/cmap.rb +10 -5
  77. data/lib/flor/punit/concurrence.rb +42 -51
  78. data/lib/flor/punit/cron.rb +33 -0
  79. data/lib/flor/punit/do_trap.rb +42 -0
  80. data/lib/flor/punit/every.rb +48 -13
  81. data/lib/flor/punit/graft.rb +3 -3
  82. data/lib/flor/punit/on_timeout.rb +38 -0
  83. data/lib/flor/punit/schedule.rb +69 -6
  84. data/lib/flor/punit/signal.rb +54 -0
  85. data/lib/flor/punit/sleep.rb +1 -1
  86. data/lib/flor/punit/task.rb +4 -1
  87. data/lib/flor/punit/trap.rb +188 -13
  88. data/lib/flor/tools/shell.rb +408 -62
  89. data/lib/flor/tools/shell_out.rb +31 -0
  90. data/lib/flor/unit.rb +1 -1
  91. data/lib/flor/unit/caller.rb +177 -0
  92. data/lib/flor/unit/executor.rb +1 -0
  93. data/lib/flor/unit/ganger.rb +15 -21
  94. data/lib/flor/unit/hook.rb +1 -1
  95. data/lib/flor/unit/hooker.rb +22 -10
  96. data/lib/flor/unit/loader.rb +22 -22
  97. data/lib/flor/unit/logger.rb +63 -36
  98. data/lib/flor/unit/models.rb +6 -1
  99. data/lib/flor/unit/models/execution.rb +12 -1
  100. data/lib/flor/unit/models/message.rb +7 -0
  101. data/lib/flor/unit/models/trap.rb +31 -17
  102. data/lib/flor/unit/scheduler.rb +18 -10
  103. data/lib/flor/unit/storage.rb +83 -23
  104. data/lib/flor/unit/waiter.rb +1 -2
  105. metadata +96 -52
  106. data/lib/flor/deep.rb +0 -144
  107. data/lib/flor/punit/on.rb +0 -57
  108. data/lib/flor/unit/runner.rb +0 -84
  109. data/match.md +0 -22
@@ -1,53 +1,84 @@
1
1
 
2
- class Flor::Pro::Map < Flor::Procedure
3
-
4
- names %w[ map for-each ]
5
-
6
- def pre_execute
7
-
8
- @node['vars'] ||= {}
9
-
10
- @node['col'] = nil
11
- @node['idx'] = -1
12
- @node['fun'] = nil
13
-
14
- @node['res'] = @node['heat0'] == 'map' ? [] : nil
15
-
16
- unatt_unkeyed_children
2
+ class Flor::Pro::Map < Flor::Pro::Iterator
3
+ #
4
+ # This is the classical "map" procedure. It accepts a collection
5
+ # and a function and yields a new collection.
6
+ #
7
+ # ```
8
+ # map [ 1, 2, 3 ]
9
+ # def x
10
+ # + x 3
11
+ # # f.ret yields [ 4, 5, 6 ]
12
+ # ```
13
+ #
14
+ # The collection if not given is taken from `f.ret`:
15
+ # ```
16
+ # [ 1, 2, 3 ]
17
+ # map
18
+ # def x
19
+ # + x 2
20
+ # # f.ret yields [ 3, 4, 5 ]
21
+ # ```
22
+ #
23
+ # The function may be given by reference:
24
+ # ```
25
+ # define add3 x
26
+ # + x 3
27
+ # map [ 0, 1 ] add3
28
+ # ```
29
+ #
30
+ # There is an implicit `idx` var:
31
+ # ```
32
+ # map [ 'a', 'b' ]
33
+ # def x \ [ idx, x ]
34
+ # # f.ret yields [ [ 0, 'a' ], [ 1, 'b' ] ]
35
+ # ```
36
+ # but that index can be included in the function signature:
37
+ # ```
38
+ # map [ 'a', 'b' ]
39
+ # def x i \ [ x, i ]
40
+ # # f.ret yields [ [ 'a', 0 ], [ 'b', 1 ] ]
41
+ # ```
42
+ #
43
+ # ## with objects (hashes)
44
+ #
45
+ # ```
46
+ # map { a: 'A', b: 'B', c: 'C' }
47
+ # def k v \ [ k v ]
48
+ # # f.ret --> [ [ 'a', 'A' ], [ 'b', 'B' ], [ 'c', 'C' ] ]
49
+ #
50
+ # map { a: 'A', b: 'B', c: 'C' }
51
+ # def k v i \ [ i k v ]
52
+ # # f.ret --> [ [ 0, 'a', 'A' ], [ 1, 'b', 'B' ], [ 2, 'c', 'C' ] ]
53
+ # ```
54
+ #
55
+ # ## iterating and functions
56
+ #
57
+ # Iterating functions accept 0 to 3 arguments when iterating over an
58
+ # array and 0 to 4 arguments when iterating over an object.
59
+ #
60
+ # Those arguments are `[ value, index, length ]` for arrays.
61
+ # They are `[ key, value, index, length ]` for objects.
62
+ #
63
+ # The corresponding `key`, `val`, `idx` and `len` variables are also
64
+ # set in the closure for the function call.
65
+ #
66
+ # ## see also
67
+ #
68
+ # Collect.
69
+
70
+ name 'map'
71
+
72
+ protected
73
+
74
+ def receive_iteration
75
+
76
+ @node['res'] << payload['ret']
17
77
  end
18
78
 
19
- def receive_non_att
20
-
21
- @node['col'] ||=
22
- Flor.to_coll(
23
- if Flor.is_func_tree?(payload['ret'])
24
- node_payload_ret
25
- else
26
- payload['ret']
27
- end)
28
-
29
- return execute_child(@ncid) \
30
- if @node['fun'] == nil && children[@ncid] != nil
31
-
32
- if @node['idx'] < 0
33
- @node['fun'] = payload['ret']
34
- elsif res = @node['res']
35
- res << payload['ret']
36
- end
37
-
38
- @node['idx'] += 1
39
- @node['mtime'] = Flor.tstamp
40
-
41
- if @node['idx'] == @node['col'].size
42
- if res = @node['res']
43
- payload['ret'] = @node['res']
44
- end
45
- return wrap_reply
46
- end
47
-
48
- @node['vars']['idx'] = @node['idx']
79
+ def iterator_result
49
80
 
50
- apply(@node['fun'], @node['col'][@node['idx'], 1], tree[2])
81
+ @node['res']
51
82
  end
52
83
  end
53
84
 
@@ -184,6 +184,10 @@ class Flor::Pro::Match < Flor::Pro::Case
184
184
  # (guard {name})
185
185
  # (guard {name} {pattern|conditional}*)
186
186
  # ```
187
+ #
188
+ # ## see also
189
+ #
190
+ # Case.
187
191
 
188
192
  name 'match'
189
193
 
@@ -192,7 +196,7 @@ class Flor::Pro::Match < Flor::Pro::Case
192
196
  unatt_unkeyed_children
193
197
 
194
198
  conditional = true
195
- @node['val'] = payload['ret'] if non_att_children.size.even?
199
+ @node['val'] = payload['ret'] if non_att_count.even?
196
200
  found_val = @node.has_key?('val')
197
201
  t = tree
198
202
  changed = false
@@ -216,7 +220,7 @@ class Flor::Pro::Match < Flor::Pro::Case
216
220
  t = tree[1][index]
217
221
 
218
222
  payload['_pat_val'] = @node['val'] \
219
- if t && t[0].match(/\A_pat_(arr|obj|or|guard)\z/)
223
+ if t && t[0].match(/\A_pat_(arr|obj|or|guard|regex)\z/)
220
224
 
221
225
  super
222
226
  end
@@ -225,6 +229,8 @@ class Flor::Pro::Match < Flor::Pro::Case
225
229
 
226
230
  def patternize(t)
227
231
 
232
+ return [ '_pat_regex', *t[1..-1] ] if t[0] == '_rxs'
233
+
228
234
  return t unless t[1].is_a?(Array)
229
235
 
230
236
  bang = t[1]
@@ -73,18 +73,17 @@ class Flor::Pro::Matchr < Flor::Procedure
73
73
  rets = @node['rets'].dup
74
74
  rets.unshift(node_payload_ret) if rets.size < 2
75
75
 
76
- fail ArgumentError.new(
77
- "'#{tree[0]}' needs 1 or 2 arguments"
76
+ fail Flor::FlorError.new(
77
+ "'#{tree[0]}' needs 1 or 2 arguments", self
78
78
  ) if rets.size < 2
79
79
 
80
80
  rex =
81
- rets.find { |r| r.is_a?(Array) && r[0] == '_rxs' } ||
81
+ rets.find { |r| Flor.is_regex_tree?(r) } ||
82
82
  rets.last
83
83
 
84
84
  str = (rets - [ rex ]).first
85
85
 
86
- rex = rex.is_a?(String) ? rex : rex[1].to_s
87
- rex = rex.match(/\A\/[^\/]*\/[a-z]*\z/) ? Kernel.eval(rex) : Regexp.new(rex)
86
+ rex = Flor.to_regex(rex)
88
87
 
89
88
  [ rex, str ]
90
89
  end
@@ -1,13 +1,13 @@
1
1
 
2
2
  class Flor::Pro::Move < Flor::Procedure
3
3
  #
4
- # Moves a cursor to a given position
4
+ # Moves a cursor to a given position, a kind of local goto.
5
5
  #
6
6
  # ```
7
7
  # cursor
8
- # do-this
8
+ # do-this _
9
9
  # move to: 'do-that-other-thing'
10
- # do-that _ # got skipped
10
+ # do-that _ # gets skipped
11
11
  # do-that-other-thing _
12
12
  # ```
13
13
 
@@ -11,6 +11,19 @@ class Flor::Pro::NoEval < Flor::Procedure
11
11
  # [ 1, 2, 3 ]
12
12
  # # f.ret is still 1 here, not [ 1, 2, 3 ]
13
13
  # ```
14
+ #
15
+ # Could be useful when determining on the fly what the "parent"
16
+ # procedure should be.
17
+ # ```
18
+ # set head
19
+ # case (size f.customers)
20
+ # 0; noeval
21
+ # [ 1, 10 ]; concurrence
22
+ # else; sequence
23
+ # head
24
+ # task 'a'
25
+ # task 'b'
26
+ # ```
14
27
 
15
28
  name 'noeval'
16
29
 
@@ -1,5 +1,21 @@
1
1
 
2
2
  class Flor::Pro::Not < Flor::Procedure
3
+ #
4
+ # `not` negates its last child (or its last unkeyed attribute)
5
+ #
6
+ # ```
7
+ # not _ # --> true
8
+ # not true # --> false
9
+ # not false # --> true
10
+ # not 0 # --> false
11
+ # not 1 # --> false
12
+ # ```
13
+ #
14
+ # ```
15
+ # not
16
+ # true
17
+ # false # --> true
18
+ # ```
3
19
 
4
20
  name 'not'
5
21
 
@@ -0,0 +1,172 @@
1
+
2
+ class Flor::Pro::On < Flor::Macro
3
+ #
4
+ # Catches signals or errors.
5
+ #
6
+ # ## signals
7
+ #
8
+ # Turns
9
+ # ```
10
+ # on 'approve'
11
+ # task 'bob' mission: 'gather signatures'
12
+ # ```
13
+ # into
14
+ # ```
15
+ # trap point: 'signal', name: 'approve'
16
+ # def msg
17
+ # task 'bob' mission: 'gather signatures'
18
+ # ```
19
+ #
20
+ # It's OK trapping multiple signal names:
21
+ # ```
22
+ # on [ /^bl/ 'red' 'white' ]
23
+ # task 'bob' mission: "order can of $(sig) paint"
24
+ # ```
25
+ #
26
+ # ## error
27
+ #
28
+ # "on" understands `on error` with a block. It in facts turns:
29
+ # ```
30
+ # sequence
31
+ # on error
32
+ # push f.l err.msg # a block with an `err` variable
33
+ # # ...
34
+ # ```
35
+ # into:
36
+ # ```
37
+ # sequence
38
+ # on_error
39
+ # def err # a anonymous function definition with an `err` argument
40
+ # push f.l err.msg
41
+ # # ...
42
+ # ```
43
+ #
44
+ # Please note that "error" in `on error` is not quoted, nor double quoted.
45
+ # If it were, it would trap the signal named "error".
46
+ #
47
+ #
48
+ # ## cancel
49
+ #
50
+ # "on" understands `on cancel` with a block. It in facts turns:
51
+ # ```
52
+ # sequence
53
+ # on cancel
54
+ # push f.l msg # a block with a `msg` variable
55
+ # # ...
56
+ # ```
57
+ # into:
58
+ # ```
59
+ # sequence
60
+ # on_cancel
61
+ # def msg # a anonymous function definition with a `msg` argument
62
+ # push f.l msg
63
+ # # ...
64
+ # ```
65
+ #
66
+ # Please note that "cancel" in `on cancel` is not quoted, nor double quoted.
67
+ # If it were, it would trap the signal named "cancel".
68
+ #
69
+ #
70
+ # ## timeout
71
+ #
72
+ # `on timeout` turns:
73
+ # ```
74
+ # sequence timeout: '1w'
75
+ # on timeout
76
+ # push f.l msg # a block with a `msg` variable
77
+ # # ...
78
+ # ```
79
+ # into:
80
+ # ```
81
+ # sequence timeout: '1w'
82
+ # on_timeout
83
+ # def msg # a anonymous function definition with a `msg` argument
84
+ # push f.l msg
85
+ # # ...
86
+ # ```
87
+ #
88
+ # Please note that "timeout" in `on timeout` is not quoted, nor double quoted.
89
+ # If it were, it would trap the signal named "timeout".
90
+ #
91
+ #
92
+ # ## see also
93
+ #
94
+ # Trap and signal.
95
+
96
+ name 'on'
97
+
98
+ def rewrite_tree
99
+
100
+ if att = find_catch # 22
101
+ rewrite_as_catch_tree(att)
102
+ else
103
+ rewrite_as_trap_tree
104
+ end
105
+ end
106
+
107
+ protected
108
+
109
+ CATCHES = %w[ error cancel timeout ]
110
+
111
+ def find_catch
112
+
113
+ att_children
114
+ .each_with_index { |t, i|
115
+ tt = t[1].is_a?(Array) && t[1].length == 1 && t[1].first
116
+ return [ tt[0], i ] if tt && tt[1] == [] && CATCHES.include?(tt[0]) }
117
+
118
+ nil
119
+ end
120
+
121
+ def rewrite_as_catch_tree(att)
122
+
123
+ flavour, index = att
124
+
125
+ atts = att_children
126
+ atts.delete_at(index)
127
+
128
+ l = tree[2]
129
+
130
+ th = [ "on_#{flavour}", [], l, *tree[3] ]
131
+ atts.each { |ac| th[1] << Flor.dup(ac) }
132
+
133
+ td = [ 'def', [], l ]
134
+ td[1] << [ '_att', [ [ 'msg', [], l ] ], l ]
135
+ td[1] << [ '_att', [ [ 'err', [], l ] ], l ] if flavour == 'error'
136
+ non_att_children.each { |nac| td[1] << Flor.dup(nac) }
137
+
138
+ th[1] << td
139
+
140
+ th
141
+ end
142
+
143
+ def rewrite_as_trap_tree
144
+
145
+ atts = att_children
146
+ signame_i = atts.index { |at| at[1].size == 1 }
147
+
148
+ fail Flor::FlorError.new("signal name not found in #{tree.inspect}", self) \
149
+ unless signame_i
150
+
151
+ tname = atts[signame_i]
152
+ tname = Flor.dup(tname[1][0])
153
+ atts.delete_at(signame_i)
154
+
155
+ l = tree[2]
156
+
157
+ th = [ 'trap', [], l, *tree[3] ]
158
+ th[1] << [ '_att', [ [ 'point', [], l ], [ '_sqs', 'signal', l ] ], l ]
159
+ th[1] << [ '_att', [ [ 'name', [], l ], tname ], l ]
160
+ th[1] << [ '_att', [ [ 'payload', [], l ], [ '_sqs', 'event', l ] ], l ]
161
+ atts.each { |ac| th[1] << Flor.dup(ac) }
162
+
163
+ td = [ 'def', [], l ]
164
+ td[1] << [ '_att', [ [ 'msg', [], l ] ], l ]
165
+ non_att_children.each { |nac| td[1] << Flor.dup(nac) }
166
+
167
+ th[1] << td
168
+
169
+ th
170
+ end
171
+ end
172
+
@@ -0,0 +1,54 @@
1
+
2
+ class Flor::Pro::OnCancel < Flor::Procedure
3
+ #
4
+ # Counterpart to the on_cancel: attribute.
5
+ #
6
+ # ```
7
+ # set f.l []
8
+ # sequence
9
+ # on_cancel (def msg \ push f.l "$(msg.point):$(msg.nid)")
10
+ # push f.l 0
11
+ # cancel '0_1' # cancels the containing sequence
12
+ # push f.l 1
13
+ # push f.l 2
14
+ # ```
15
+ # Ends up with `[ 0, 'cancel:0_1', 2 ]` in the field `l`.
16
+ #
17
+ # ## on and on_cancel
18
+ #
19
+ # "on_cancel" is made to allow for `on cancel`, so that:
20
+ # ```
21
+ # sequence
22
+ # on cancel
23
+ # push f.l msg # a block with a `msg` variable
24
+ # # ...
25
+ # ```
26
+ # gets turned into:
27
+ # ```
28
+ # sequence
29
+ # on_cancel
30
+ # def msg # a anonymous function definition with a `msg` argument
31
+ # push f.l msg
32
+ # # ...
33
+ # ```
34
+ #
35
+ #
36
+ # ## see also
37
+ #
38
+ # On, on_error.
39
+
40
+ name 'on_cancel'
41
+
42
+ def pre_execute
43
+
44
+ unatt_unkeyed_children
45
+ end
46
+
47
+ def receive_non_att
48
+
49
+ store_on(:cancel)
50
+
51
+ super
52
+ end
53
+ end
54
+