flor 0.10.0 → 0.11.0

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.
data/CHANGELOG.md CHANGED
@@ -2,6 +2,18 @@
2
2
  # flor CHANGELOG.md
3
3
 
4
4
 
5
+ ## flor 0.11.0 released 2017-03-17
6
+
7
+ - Simplification of the tasker configuration files
8
+ (alignment on hooks configuration files)
9
+ - Introduction of lib/hooks/
10
+ - go for ; and | (same level) and \ (child level)
11
+ - Implement "twig"
12
+ - Implement basic spooler (var/spool/)
13
+ - Introduce runner service
14
+ - Fix Storage#any_message (misuse of Sequel Dataset#count)
15
+
16
+
5
17
  ## flor 0.10.0 released 2017-03-03
6
18
 
7
19
  - Enhance shell, bring in bin/flosh (though not in gem)
data/README.md CHANGED
@@ -21,9 +21,13 @@ Flor is a "Ruby workflow engine", if that makes any sense.
21
21
  * All in all should be easy to maintain (engine itself and executions running
22
22
  on top of it)
23
23
 
24
+ ## Quickstart
25
+
26
+ See [quickstart/](quickstart/).
27
+
24
28
  ## Documentation
25
29
 
26
- see [doc/](doc/).
30
+ See [doc/](doc/).
27
31
 
28
32
 
29
33
  ## LICENSE
data/fail.txt ADDED
@@ -0,0 +1,3 @@
1
+ rspec ./spec/unit/push_and_runs_spec.rb:30 # Flor unit push (run spanning) works
2
+ rspec ./spec/unit/scheduler_spec.rb:371 # Flor unit Flor::Scheduler sch_msg_max_res_time flags as "active" messages that have been reserved for too long
3
+ rspec ./spec/unit/waiter_spec.rb:127 # Flor::Waiter as a launch option lets wait until the scheduler gets idle
data/lib/flor.rb CHANGED
@@ -12,7 +12,7 @@ require 'raabro'
12
12
 
13
13
  module Flor
14
14
 
15
- VERSION = '0.10.0'
15
+ VERSION = '0.11.0'
16
16
  end
17
17
 
18
18
  require 'flor/colours'
data/lib/flor/conf.rb CHANGED
@@ -88,7 +88,7 @@ module Flor
88
88
  def self.get_class(conf, key)
89
89
 
90
90
  if v = conf[key]
91
- Kernel.const_get(v)
91
+ Flor.const_lookup(v)
92
92
  else
93
93
  nil
94
94
  end
@@ -5,13 +5,16 @@ module Flor
5
5
 
6
6
  attr_reader :unit
7
7
  attr_reader :execution
8
+ attr_reader :hooks
8
9
  attr_reader :traps
9
10
 
10
- def initialize(unit, traps, execution)
11
+ def initialize(unit, hooks, traps, execution)
11
12
 
12
13
  @unit = unit
13
14
  @execution = execution
14
- @traps = traps
15
+
16
+ @hooks = hooks # raw hooks if any, fresh from the loader
17
+ @traps = traps # array of Trap instances
15
18
  end
16
19
 
17
20
  def conf; @unit.conf; end
@@ -54,17 +57,32 @@ module Flor
54
57
  counter_add(key, 1)
55
58
  end
56
59
 
60
+ def trigger_trap(trap, message)
61
+
62
+ del, msgs = trap.trigger(self, message)
63
+ @traps.delete(trap) if del
64
+
65
+ msgs
66
+ end
67
+
57
68
  def trigger_hook(hook, message)
58
69
 
59
70
  hook.notify(self, message)
60
71
  end
61
72
 
62
- def trigger_trap(trap, message)
73
+ def trigger_block(block, opts, message)
63
74
 
64
- del, msgs = trap.trigger(self, message)
65
- @traps.delete(trap) if del
75
+ r =
76
+ if block.arity == 1
77
+ block.call(message)
78
+ elsif block.arity == 2
79
+ block.call(message, opts)
80
+ else
81
+ block.call(self, message, opts)
82
+ end
66
83
 
67
- msgs
84
+ r.is_a?(Array) && r.all? { |e| e.is_a?(Hash) } ? r : []
85
+ # be lenient with block hooks, help them return an array
68
86
  end
69
87
 
70
88
  # Given a nid, returns a copy of all the var the node sees
@@ -88,6 +106,16 @@ module Flor
88
106
  vs
89
107
  end
90
108
 
109
+ def traps_and_hooks
110
+
111
+ @htraps = nil if @htraps && @htraps.size != @traps.size
112
+
113
+ @htraps ||= @traps.collect(&:to_hook)
114
+ @hhooks ||= @hooks.collect(&:to_hook)
115
+
116
+ @htraps + @hhooks
117
+ end
118
+
91
119
  protected
92
120
 
93
121
  def make_node(message)
@@ -152,6 +152,8 @@ class Flor::Node
152
152
  @node
153
153
  elsif cat == 'v'
154
154
  lookup_var(@node, mod, key)
155
+ elsif cat == 't'
156
+ lookup_tag(mod, key)
155
157
  else
156
158
  lookup_field(mod, key)
157
159
  end
@@ -371,6 +373,17 @@ class Flor::Node
371
373
  lookup_var_name(parent_node(node), val)
372
374
  end
373
375
 
376
+ def lookup_tag(mod, key)
377
+
378
+ nids =
379
+ @execution['nodes'].inject([]) do |a, (nid, n)|
380
+ a << nid if n['tags'] && n['tags'].include?(key)
381
+ a
382
+ end
383
+
384
+ nids.empty? ? [ '_nul', nil, -1 ] : nids
385
+ end
386
+
374
387
  def lookup_field(mod, key)
375
388
 
376
389
  Flor.deep_get(payload.current, key)[1]
@@ -378,7 +391,8 @@ class Flor::Node
378
391
 
379
392
  def key_split(key) # => category, mode, key
380
393
 
381
- m = key.match(/\A(?:([lgd]?)((?:v|var|variable)|w|f|fld|field)\.)?(.+)\z/)
394
+ m = key.match(
395
+ /\A(?:([lgd]?)((?:v|var|variable)|w|f|fld|field|t|tag)\.)?(.+)\z/)
382
396
 
383
397
  #fail ArgumentError.new("couldn't split key #{key.inspect}") unless m
384
398
  # spare that
@@ -64,6 +64,7 @@ module Flor
64
64
 
65
65
  super(
66
66
  TransientUnit.new(conf),
67
+ [], # no hooks
67
68
  [], # no traps
68
69
  {
69
70
  'exid' => Flor.generate_exid('eval', 'u0'),
@@ -166,8 +167,17 @@ module Flor
166
167
  ls.reject! { |l| l.strip[0, 1] == '#' }
167
168
  s = ls.join("\n").strip
168
169
  end
170
+
171
+ a, b = s[0, 1], s[-1, 1]
172
+
169
173
  s =
170
- "{\n#{s}\n}" unless s[0, 1] == '{' && s[-1, 1] == '}'
174
+ if (a == '{' && b == '}') || (a == '[' && b == ']')
175
+ s
176
+ elsif s.match(/[^\r\n{]+:/) || s == ''
177
+ "{\n#{s}\n}"
178
+ else
179
+ "[\n#{s}\n]"
180
+ end
171
181
 
172
182
  vs = Hash.new { |h, k| k }
173
183
  class << vs
@@ -188,11 +198,16 @@ module Flor
188
198
  fail ae
189
199
  end
190
200
 
191
- h = Flor.dup(r['payload']['ret'])
201
+ o = Flor.dup(r['payload']['ret'])
192
202
 
193
- h.merge!('_path' => path) unless path.match(/[\r\n]/)
203
+ if o.is_a?(Hash)
204
+ o['_path'] = path unless path.match(/[\r\n]/)
205
+ o['root'] ||= Flor.relativize_path(vs['root'])
206
+ elsif o.is_a?(Array)
207
+ o.each { |e| e['_path'] = path } unless path.match(/[\r\n]/)
208
+ end
194
209
 
195
- h
210
+ o
196
211
  end
197
212
 
198
213
  def self.determine_root(path)
data/lib/flor/djan.rb CHANGED
@@ -156,7 +156,7 @@ module Flor
156
156
  indent_space(opts)
157
157
 
158
158
  if (
159
- x.match(/\A[^: \b\f\n\r\t"',()\[\]{}#\\]+\z/) == nil ||
159
+ x.match(/\A[^: \b\f\n\r\t"',()\[\]{}#\\+%\/><^!=-]+\z/) == nil ||
160
160
  x.to_i.to_s == x ||
161
161
  x.to_f.to_s == x
162
162
  )
data/lib/flor/flor.rb CHANGED
@@ -11,6 +11,11 @@ module Flor
11
11
  #
12
12
  # miscellaneous functions
13
13
 
14
+ def self.env_i(k)
15
+
16
+ v = ENV[k]; (v && v.match(/\A\d+\z/)) ? v.to_i : nil
17
+ end
18
+
14
19
  def self.dup(o)
15
20
 
16
21
  Marshal.load(Marshal.dump(o))
@@ -49,7 +54,7 @@ module Flor
49
54
  h['kla'] = o.class.to_s
50
55
  t = nil
51
56
 
52
- if o.is_a?(Exception)
57
+ if o.is_a?(::Exception)
53
58
 
54
59
  h['msg'] = o.message
55
60
 
@@ -68,6 +73,8 @@ module Flor
68
73
  end
69
74
 
70
75
  h['trc'] = t[0..(t.rindex { |l| l.match(/\/lib\/flor\//) }) + 1] if t
76
+ h['cwd'] = Dir.pwd
77
+ h['rlp'] = $: if o.is_a?(::LoadError)
71
78
 
72
79
  h
73
80
  end
@@ -89,7 +96,7 @@ module Flor
89
96
 
90
97
  path = path[from.length + 1..-1] if path[0, from.length] == from
91
98
 
92
- path
99
+ path || '.'
93
100
  end
94
101
 
95
102
  def self.is_array_of_messages?(o)
@@ -101,6 +108,25 @@ module Flor
101
108
  e.keys.all? { |k| k.is_a?(String) } }
102
109
  end
103
110
 
111
+ def self.h_fetch_a(h, *keys)
112
+
113
+ default = keys.last.is_a?(String) ? [] : keys.pop
114
+
115
+ k = keys.find { |k| h.has_key?(k) }
116
+ v = k ? h[k] : nil
117
+
118
+ v_to_a(v) || default
119
+ end
120
+
121
+ def self.v_to_a(o)
122
+
123
+ return o if o.is_a?(Array)
124
+ return o.split(',') if o.is_a?(String)
125
+ return nil if o.nil?
126
+
127
+ fail ArgumentError.new("cannot turn instance of #{o.class} into an array")
128
+ end
129
+
104
130
  #
105
131
  # functions about time
106
132
 
@@ -132,6 +158,15 @@ module Flor
132
158
  isostamp(true, false, false, t)
133
159
  end
134
160
 
161
+ def self.tamp(t=Time.now)
162
+
163
+ t = t.utc
164
+ s = StringIO.new
165
+ s << t.strftime('%Y%m%dT%H%M%S') << sprintf('.%06dZ', t.usec)
166
+
167
+ s.string
168
+ end
169
+
135
170
  # hour stamp
136
171
  #
137
172
  def self.hstamp(t=Time.now)
data/lib/flor/parser.rb CHANGED
@@ -10,7 +10,6 @@ module Flor
10
10
  def dot(i); str(nil, i, '.'); end
11
11
  def colon(i); str(nil, i, ':'); end
12
12
  def comma(i); str(nil, i, ','); end
13
- def bslash(i); str(nil, i, '\\'); end
14
13
 
15
14
  def pstart(i); str(nil, i, '('); end
16
15
  def pend(i); str(nil, i, ')'); end
@@ -72,11 +71,10 @@ module Flor
72
71
  def postval(i); rep(nil, i, :eol, 0); end
73
72
 
74
73
  def comma_eol(i); seq(nil, i, :comma, :eol, :ws_star); end
75
- def bslash_eol(i); seq(nil, i, :bslash, :eol, :ws_star); end
76
- def sep(i); alt(nil, i, :comma_eol, :bslash_eol, :ws_star); end
74
+ def sep(i); alt(nil, i, :comma_eol, :ws_star); end
77
75
 
78
76
  def comma_qmark_eol(i); seq(nil, i, :comma, '?', :eol); end
79
- def coll_sep(i); alt(nil, i, :bslash_eol, :comma_qmark_eol, :ws_star); end
77
+ def coll_sep(i); alt(nil, i, :comma_qmark_eol, :ws_star); end
80
78
 
81
79
  def ent(i); seq(:ent, i, :key, :postval, :colon, :postval, :exp, :postval); end
82
80
  def ent_qmark(i); rep(nil, i, :ent, 0, 1); end
@@ -125,15 +123,20 @@ module Flor
125
123
  alias exp eor
126
124
 
127
125
  def key(i); seq(:key, i, :exp); end
128
- def keycol(i); seq(nil, i, :key, :ws_star, :colon, :eol); end
126
+ def keycol(i); seq(nil, i, :key, :ws_star, :colon, :eol, :ws_star); end
129
127
 
130
128
  def att(i); seq(:att, i, :sep, :keycol, '?', :exp); end
131
129
  def head(i); seq(:head, i, :exp); end
132
- def indent(i); rex(:indent, i, /[|; \t]*/); end
130
+ def indent(i); rex(:indent, i, /[ \t]*/); end
133
131
  def node(i); seq(:node, i, :indent, :head, :att, '*'); end
134
132
 
133
+ def linjoin(i); rex(nil, i, /[ \t]*[\\|;][ \t]*/); end
134
+ def outjnl(i); seq(nil, i, :linjoin, :comment, '?', :retnew); end
135
+ def outnlj(i); seq(nil, i, :ws_star, :comment, '?', :retnew, :linjoin); end
136
+ def outdent(i); alt(:outdent, i, :outjnl, :outnlj, :eol); end
137
+
135
138
  def line(i)
136
- seq(:line, i, :node, '?', :eol)
139
+ seq(:line, i, :node, '?', :outdent)
137
140
  end
138
141
  def panode(i)
139
142
  seq(:panode, i, :pstart, :eol, :ws_star, :line, '*', :eol, :pend)
@@ -151,7 +154,7 @@ module Flor
151
154
 
152
155
  def rewrite_par(t)
153
156
 
154
- Nod.new(t.lookup(:node)).to_a
157
+ Nod.new(t.lookup(:node), nil).to_a
155
158
  end
156
159
 
157
160
  def rewrite_ref(t); [ t.string, [], ln(t) ]; end
@@ -237,7 +240,7 @@ module Flor
237
240
  attr_accessor :parent, :indent
238
241
  attr_reader :children
239
242
 
240
- def initialize(tree)
243
+ def initialize(tree, outdent)
241
244
 
242
245
  @parent = nil
243
246
  @indent = -1
@@ -245,15 +248,21 @@ module Flor
245
248
  @children = []
246
249
  @line = 0
247
250
 
251
+ @outdent = outdent ? outdent.strip : nil
252
+ @outdent = nil if @outdent && @outdent.size < 1
253
+
248
254
  read(tree) if tree
249
255
  end
250
256
 
251
257
  def append(node)
252
258
 
253
- if node.indent == :east
254
- node.indent = self.indent + 2
255
- elsif node.indent == :south
256
- node.indent = self.indent
259
+ if @outdent
260
+ if @outdent.index('\\')
261
+ node.indent = self.indent + 2
262
+ elsif @outdent.index('|') || @outdent.index(';')
263
+ node.indent = self.indent
264
+ end
265
+ @outdent = nil
257
266
  end
258
267
 
259
268
  if node.indent > self.indent
@@ -270,6 +279,21 @@ module Flor
270
279
 
271
280
  cn = @children.collect(&:to_a)
272
281
 
282
+ # make sure `[ 1 2 3 ] timeout: '2h'`
283
+ # turns into `_arr timeout: '2h'; 1 | 2 | 3`
284
+
285
+ if (
286
+ @head.is_a?(Array) &&
287
+ (@head[0] == '_arr' || @head[0] == '_obj') &&
288
+ #Flor.is_array_of_trees?(@head[1]) &&
289
+ cn.all? { |c| c[0] == '_att' && c[1].size > 1 }
290
+ )
291
+ cn = @head[1] + cn
292
+ @head = @head[0]
293
+ att, non_att = cn.partition { |c| c[0] == '_att' }
294
+ cn = att + non_att
295
+ end
296
+
273
297
  # detect if/unless suffix
274
298
 
275
299
  atts =
@@ -279,7 +303,6 @@ module Flor
279
303
  c.size == 1 && %w[ if unless ].include?(c[0][0]) && c[0][1] == []
280
304
  }
281
305
 
282
- #return cn.first if @head == 'sequence' && @line == 0 && cn.size == 1
283
306
  return [ @head, cn, @line ] unless i
284
307
 
285
308
  # rewrite if/unless suffix
@@ -295,20 +318,20 @@ module Flor
295
318
 
296
319
  def read(tree)
297
320
 
298
- if it = tree.lookup(:indent)
299
-
300
- s = it.string
301
- semicount = s.count(';')
302
- pipe = s.index('|')
303
-
304
- @indent =
305
- if semicount == 1 then :east
306
- elsif semicount > 1 || pipe then :south
307
- else s.length; end
308
- end
321
+ #if it = tree.lookup(:indent)
322
+ # #s = it.string
323
+ # #semicount = s.count(';')
324
+ # #pipe = s.index('|')
325
+ # #
326
+ # #@indent =
327
+ # # if semicount == 1 then :east
328
+ # # elsif semicount > 1 || pipe then :south
329
+ # # else s.length; end
330
+ #end
331
+ @indent = tree.lookup(:indent).string.length
309
332
 
310
333
  ht = tree.lookup(:head)
311
- @line = Lang.line_number(ht)
334
+ @line = Flor::Lang.line_number(ht)
312
335
 
313
336
  @head = Flor::Lang.rewrite(ht.c0)
314
337
  @head = @head[0] if @head[0].is_a?(String) && @head[1] == []
@@ -341,10 +364,13 @@ module Flor
341
364
 
342
365
  def rewrite_flor(t)
343
366
 
344
- prev = root = Nod.new(nil)
367
+ prev = root = Nod.new(nil, nil)
345
368
 
346
- t.gather(:node).each do |nt|
347
- n = Nod.new(nt)
369
+ #t.gather(:node).each do |nt|
370
+ t.gather(:line).each do |lt|
371
+ nt = lt.lookup(:node); next unless nt
372
+ ot = lt.lookup(:outdent).string
373
+ n = Nod.new(nt, ot)
348
374
  prev.append(n)
349
375
  prev = n
350
376
  end