flor 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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