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,45 +1,67 @@
1
1
 
2
2
  module Flor
3
3
 
4
- # TODO I need levels, ::Logger has them
5
- # TODO I need log rotation, ::Logger has then
6
- # TODO I need line heads, ::Logger has them (and @progname)
7
4
  # TODO ::Logger has a formatting callback
8
- # TODO I need simply @out.puts...
5
+
6
+ # No log rotation,
7
+ # just dump to stdout (or stderr), see https://12factor.net/logs
9
8
 
10
9
  class Logger
11
10
 
12
11
  # NB: logger configuration entries start with "log_"
13
12
 
13
+ LEVELS_I = %w[ DEBUG INFO WARN ERROR FATAL UNKNOWN ]
14
+
14
15
  def initialize(unit)
15
16
 
16
17
  @unit = unit
17
18
  @unit.hooker.add('logger', self)
18
19
 
19
- @out = prepare_out
20
+ @out = Flor::Logger::Out.prepare(@unit)
20
21
 
21
22
  @uni = @unit.identifier
23
+
24
+ self.level = @unit.conf['log_level'] || 1
22
25
  end
23
26
 
24
27
  def opts; { consumed: true }; end
25
28
 
26
29
  def shutdown
27
30
 
28
- #@file.close if @file
29
31
  @out.close
30
32
  end
31
33
 
34
+ attr_reader :level
35
+
36
+ def level=(i)
37
+
38
+ original = i
39
+ i = LEVELS_I.index(i.to_s.upcase) unless i.is_a?(Integer)
40
+
41
+ fail ArgumentError.new(
42
+ "'log_level' must be between 0 (DEBUG) and 4 (FATAL). " +
43
+ "#{original.inspect} not acceptable"
44
+ ) unless i.is_a?(Integer) && i > -1 && i < 6
45
+
46
+ @level = i
47
+ end
48
+
32
49
  def debug(*m); log(:debug, *m); end
33
- def error(*m); log(:error, *m); end
34
50
  def info(*m); log(:info, *m); end
35
51
  def warn(*m); log(:warn, *m); end
52
+ def error(*m); log(:error, *m); end
53
+ def fatal(*m); log(:fatal, *m); end
54
+ def unknown(*m); log(:unknown, *m); end
36
55
 
37
56
  def log(level, *elts)
38
57
 
58
+ lvl = level.to_s.upcase
59
+ lvi = LEVELS_I.index(lvl)
60
+ return if lvi < @level
61
+
39
62
  n = Time.now.utc
40
63
  stp = Flor.tstamp(n)
41
64
 
42
- lvl = level.to_s.upcase
43
65
  txt = elts.collect(&:to_s).join(' ')
44
66
  err = elts.find { |e| e.is_a?(Exception) }
45
67
 
@@ -59,21 +81,20 @@ module Flor
59
81
 
60
82
  if msg['rewritten'] && @unit.conf['log_tree_rw']
61
83
 
62
- Flor.print_compact_tree(
63
- msg['rewritten'], msg['nid'],
64
- ind: 6, title: "rewrote #{msg['exid']} #{msg['nid']}",
65
- out: @out)
66
- Flor.print_compact_tree(
67
- msg['tree'], msg['nid'],
68
- ind: 6, title: "into #{msg['exid']} #{msg['nid']}",
69
- close: true,
70
- out: @out)
84
+ @out <<
85
+ Flor.to_compact_tree_s(
86
+ msg['rewritten'], msg['nid'],
87
+ ind: 6, title: "rewrote #{msg['exid']} #{msg['nid']}")
88
+ @out << "\n" <<
89
+ Flor.to_compact_tree_s(
90
+ msg['tree'], msg['nid'],
91
+ ind: 6, title: "into #{msg['exid']} #{msg['nid']}",
92
+ close: true)
71
93
  end
72
94
 
73
- if @unit.conf['log_msg']
74
-
75
- Flor.log_message(executor, msg, out: @out)
76
- end
95
+ @out.puts(
96
+ Flor.message_to_one_line_s(executor, msg, out: @out)
97
+ ) if @unit.conf['log_msg'] && msg['point'] != 'end'
77
98
 
78
99
  [] # we're only logging, do not queue further messages
79
100
  end
@@ -160,21 +181,21 @@ module Flor
160
181
 
161
182
  return unless @unit.conf['log_err']
162
183
 
163
- Flor.print_detail_msg(executor, message, opts.merge(out: @out))
184
+ @out.puts(Flor.msg_to_detail_s(executor, message, opts.merge(flag: true)))
164
185
  end
165
186
 
166
187
  def log_src(source, opts, log_opts={})
167
188
 
168
189
  return unless @unit.conf['log_src']
169
190
 
170
- Flor.print_src(source, opts, log_opts.merge(out: @out))
191
+ @out.puts(Flor.src_to_s(source, opts, log_opts))
171
192
  end
172
193
 
173
194
  def log_tree(tree, nid='0', opts={})
174
195
 
175
196
  return unless @unit.conf['log_tree']
176
197
 
177
- Flor.print_tree(tree, nid, opts.merge(out: @out))
198
+ @out.puts(Flor.tree_to_s(tree, nid, opts.merge(out: @out)))
178
199
  end
179
200
 
180
201
  protected
@@ -207,37 +228,43 @@ module Flor
207
228
  message[0..k + 2 + 4] + "(...len#{i - (k + 2 + 1)})" + message[i..-1]
208
229
  end
209
230
 
210
- def prepare_out
211
-
212
- case (o = @unit.conf['log_out'] || 'stdout')
213
- when false, 'null' then NoOut.new(@unit)
214
- when true, 'stdout' then StdOut.new(@unit, $stdout)
215
- when 'stderr' then StdOut.new(@unit, $stderr)
216
- when /::/ then Flor.const_lookup(o).new(@unit)
217
- else FileOut.new(@unit, o)
218
- end
219
- end
220
-
221
231
  class Out
232
+
222
233
  attr_reader :unit
234
+
223
235
  def initialize(unit); @unit = unit; end
224
236
  def log_colours?; @unit.conf.fetch('log_colours') { :no } == true; end
225
237
  def puts(s); end
226
238
  def flush; end
227
239
  def close; end
240
+
241
+ def self.prepare(unit)
242
+
243
+ case o = unit.conf.fetch('log_out', 1)
244
+ when false, 'null' then NoOut.new(unit)
245
+ when 1, true, 'stdout' then StdOut.new(unit, $stdout)
246
+ when 2, 'stderr' then StdOut.new(unit, $stderr)
247
+ when /::/ then Flor.const_lookup(o).new(unit)
248
+ else FileOut.new(unit, o)
249
+ end
250
+ end
228
251
  end
229
252
 
230
253
  class NoOut < Out
254
+
231
255
  def log_colours?; false; end
232
256
  end
233
257
 
234
258
  class StdOut < Out
259
+
235
260
  def initialize(unit, f); super(unit); @f = f; end
236
261
  def log_colours?
237
262
  lc = @unit.conf.fetch('log_colours') { :no }
238
263
  return lc if [ true, false ].include?(lc)
239
264
  @f.tty?
240
265
  end
266
+ def <<(s); @f << s; self; end
267
+ def print(s); @f.print(s); end
241
268
  def puts(s); @f.puts(s); end
242
269
  def flush; @f.flush; end
243
270
  end
@@ -274,7 +301,7 @@ module Flor
274
301
  @fname = fn
275
302
  end
276
303
 
277
- @file ||= File.open(@fname, 'ab')
304
+ @file ||= File.open(@fname, 'ab:UTF-8')
278
305
  end
279
306
  end
280
307
  end
@@ -15,6 +15,11 @@ module Flor
15
15
  end
16
16
 
17
17
  def fetch_rows(sql); yield([]); end
18
+ #DJV
19
+ # add mising methods from dummy adaptor
20
+ def typecast_value_boolean(opts={});true;end
21
+ def test_connection();true;end
22
+ #DJV
18
23
 
19
24
  DB = Sequel.connect(:adapter => Flor::DummySequelAdapter)
20
25
  end
@@ -47,7 +52,7 @@ module Flor
47
52
  end
48
53
  end
49
54
 
50
- MODELS = [ :executions, :timers, :traces, :traps, :pointers ]
55
+ MODELS = [ :executions, :timers, :traces, :traps, :pointers, :messages ]
51
56
 
52
57
  dir = File.dirname(__FILE__)
53
58
  MODELS.each { |m| require File.join(dir, 'models', "#{m.to_s[0..-2]}.rb") }
@@ -4,6 +4,7 @@ module Flor
4
4
  class Execution < FlorModel
5
5
 
6
6
  def nodes; data['nodes']; end
7
+ def zero_node; nodes['0']; end
7
8
 
8
9
  def tags
9
10
 
@@ -19,6 +20,12 @@ module Flor
19
20
  .find { |n| n['failure'] && n['status'] != 'triggered-on-error' }
20
21
  end
21
22
 
23
+ def failed_nodes
24
+
25
+ nodes.values
26
+ .select { |n| n['failure'] && n['status'] != 'triggered-on-error' }
27
+ end
28
+
22
29
  def full_tree
23
30
 
24
31
  tree = nodes['0']['tree']
@@ -26,12 +33,16 @@ module Flor
26
33
  nodes.each do |nid, n|
27
34
  next if nid == '0'
28
35
  t = n['tree']; next unless t
29
- # TODO
30
36
  end
31
37
 
32
38
  tree
33
39
  end
34
40
 
41
+ def zero_variables
42
+
43
+ zero_node['vars']
44
+ end
45
+
35
46
  # class methods
36
47
 
37
48
  def self.by_status(s)
@@ -0,0 +1,7 @@
1
+
2
+ module Flor
3
+
4
+ class Message < FlorModel
5
+ end
6
+ end
7
+
@@ -10,21 +10,22 @@ module Flor
10
10
  opts[:consumed] = tconsumed
11
11
 
12
12
  opts[:point] = tpoints.split(',') if tpoints
13
- opts[:heap] = theaps.split(',') if theaps
14
- opts[:heat] = theats.split(',') if theats
13
+ opts[:tag] = do_split(ttags) if ttags
14
+ opts[:heap] = do_split(theaps) if theaps
15
+ opts[:heat] = do_split(theats) if theats
15
16
 
16
17
  opts[:name] = data['names']
17
18
 
18
19
  case trange
19
- when 'execution'
20
- opts[:exid] = exid
21
- when 'subdomain'
22
- opts[:subdomain] = Flor.domain(exid)
23
- when 'domain'
24
- opts[:domain] = Flor.domain(exid)
25
- else #'subnid' # default
26
- opts[:exid] = exid
27
- opts[:subnid] = true
20
+ when 'execution'
21
+ opts[:exid] = exid
22
+ when 'subdomain'
23
+ opts[:subdomain] = Flor.domain(exid)
24
+ when 'domain'
25
+ opts[:domain] = Flor.domain(exid)
26
+ else #'subnid' # default
27
+ opts[:exid] = exid
28
+ opts[:subnid] = true
28
29
  end
29
30
 
30
31
  [ "trap#{id}", opts, self, nil ]
@@ -72,8 +73,13 @@ module Flor
72
73
  msg['trap_id'] = self.id
73
74
 
74
75
  if vs = msg['vars']
76
+
75
77
  k = vs.keys.find { |k| k != 'arguments' } || 'msg'
76
78
  vs[k] = message
79
+
80
+ if sig = message['point'] == 'signal' && message['name']
81
+ vs['sig'] = sig
82
+ end
77
83
  end
78
84
 
79
85
  if dat['pl'] == 'event'
@@ -81,22 +87,30 @@ module Flor
81
87
  msg['payload'] = Flor.dup(message['payload'])
82
88
  end
83
89
 
84
- {
85
- 'point' => 'trigger',
90
+ { 'point' => 'trigger',
86
91
  'exid' => self.exid,
87
92
  'nid' => self.nid,
88
93
  'type' => 'trap',
89
94
  'trap' => to_hash,
90
95
  'trap_id' => self.id,
91
96
  'message' => msg,
92
- 'sm' => message['m'],
93
- #'dbg' => xx
94
- }#.tap { |m| pp m }
97
+ 'sm' => message['m'] }
98
+ #'dbg' => xx
99
+ #.tap { |m| pp m['message'] }
100
+ #.tap { |m| pp m }
95
101
  end
96
102
 
97
103
  def to_hash
98
104
 
99
- values.inject({}) { |h, (k, v)| h[k.to_s ] = v if k != :content; h }
105
+ values
106
+ .inject({}) { |h, (k, v)| h[k.to_s ] = v if k != :content; h }
107
+ end
108
+
109
+ def do_split(v)
110
+
111
+ v
112
+ .split(',')
113
+ .collect { |e| Flor.is_regex_string?(e) ? Flor.to_regex(e) : e }
100
114
  end
101
115
  end
102
116
  end
@@ -5,7 +5,7 @@ module Flor
5
5
 
6
6
  attr_reader :conf, :env
7
7
 
8
- attr_reader :hooker, :storage, :loader, :ganger, :runner
8
+ attr_reader :hooker, :storage, :loader, :ganger, :caller
9
9
  attr_reader :logger
10
10
 
11
11
  attr_reader :thread_status
@@ -27,8 +27,8 @@ module Flor
27
27
 
28
28
  @loader =
29
29
  (Flor::Conf.get_class(@conf, 'loader') || Flor::Loader).new(self)
30
- @runner =
31
- (Flor::Conf.get_class(@conf, 'runner') || Flor::Runner).new(self)
30
+ @caller =
31
+ (Flor::Conf.get_class(@conf, 'caller') || Flor::Caller).new(self)
32
32
  @hooker =
33
33
  (Flor::Conf.get_class(@conf, 'hooker') || Flor::Hooker).new(self)
34
34
  @storage =
@@ -257,6 +257,11 @@ module Flor
257
257
  queue(*prepare_message('cancel', [ exid, *as ]))
258
258
  end
259
259
 
260
+ def kill(exid, *as)
261
+
262
+ queue(*prepare_message('kill', [ exid, *as ]))
263
+ end
264
+
260
265
  def signal(name, h={})
261
266
 
262
267
  h[:payload] ||= {}
@@ -358,20 +363,18 @@ module Flor
358
363
  puts on_start_exc(ex)
359
364
  end
360
365
 
361
- def prepare_on_receive_last(msg, opts)
366
+ def prepare_re_apply_messages(msg, opts)
362
367
 
363
368
  fail ArgumentError.new("missing 'payload' to re_apply") \
364
369
  unless msg['payload']
365
370
 
366
- t = Flor::Lang.parse(opts[:tree], 're_apply', {})
371
+ t = Flor.parse(opts[:tree], 're_apply', {})
367
372
 
368
- [
369
- { 'point' => 'execute',
373
+ [ { 'point' => 'execute',
370
374
  'exid' => msg['exid'], 'nid' => msg['nid'],
371
375
  'from' => 'parent',
372
376
  'tree' => t,
373
- 'payload' => msg['payload'] }
374
- ]
377
+ 'payload' => msg['payload'] } ]
375
378
  end
376
379
 
377
380
  def prepare_message(point, args)
@@ -387,6 +390,11 @@ module Flor
387
390
  msg = { 'point' => point }
388
391
  opts = {}
389
392
 
393
+ if point == 'kill'
394
+ msg['point'] = 'cancel'
395
+ msg['flavour'] = 'kill'
396
+ end
397
+
390
398
  h.each do |k, v|
391
399
  if %w[ exid name nid payload on_receive_last ].include?(k)
392
400
  msg[k] = v
@@ -396,7 +404,7 @@ module Flor
396
404
  end
397
405
 
398
406
  if opts[:re_apply]
399
- msg['on_receive_last'] = prepare_on_receive_last(msg, opts)
407
+ msg['on_receive_last'] = prepare_re_apply_messages(msg, opts)
400
408
  end
401
409
 
402
410
  fail ArgumentError.new('missing :exid key') \
@@ -11,6 +11,8 @@ module Flor
11
11
  attr_reader :mutex
12
12
  # might be useful for some implementations
13
13
 
14
+ attr_accessor :archive
15
+
14
16
  def initialize(unit)
15
17
 
16
18
  @unit = unit
@@ -199,6 +201,7 @@ module Flor
199
201
  @db[:flor_messages]
200
202
  .select(:exid)
201
203
  .exclude(exid: _exids_being_processed)
204
+ .exclude(status: %w[ reserved consumed ])
202
205
  .limit(exe_count)
203
206
  @db[:flor_messages]
204
207
  .where(exid: _exids, status: 'created')
@@ -281,22 +284,14 @@ module Flor
281
284
  []
282
285
  end
283
286
 
284
- def consume(messages)
287
+ POINTS_TO_ARCHIVE = %w[ terminated failed ceased ]
285
288
 
286
- synchronize do
289
+ def consume(messages)
287
290
 
288
- if @archive
289
- @db[:flor_messages]
290
- .where(
291
- id: messages.collect { |m| m['mid'] }.compact)
292
- .update(
293
- status: 'consumed', mtime: Flor.tstamp, munit: @unit.identifier)
294
- else
295
- @db[:flor_messages]
296
- .where(
297
- id: messages.collect { |m| m['mid'] }.compact)
298
- .delete
299
- end
291
+ if @archive
292
+ consume_and_archive(messages)
293
+ else
294
+ consume_and_discard(messages)
300
295
  end
301
296
 
302
297
  rescue => err
@@ -422,6 +417,15 @@ module Flor
422
417
  id =
423
418
  synchronize do
424
419
 
420
+ #points = att_a('point', 'points', nil) ### TODO
421
+ #tags = att_a('tag', 'tags', nil) #
422
+ #heats = att_a('heat', 'heats', nil) #
423
+ #heaps = att_a('heap', 'heaps', nil) #
424
+ #names = att_a('name', 'names', nil) #
425
+ #
426
+ #opts[:heap] = theaps.split(',') if theaps
427
+ #opts[:heat] = theats.split(',') if theats
428
+ #
425
429
  @db[:flor_traps]
426
430
  .insert(
427
431
  domain: dom,
@@ -430,10 +434,10 @@ module Flor
430
434
  onid: tra['onid'] || tra['nid'],
431
435
  bnid: tra['bnid'],
432
436
  trange: tra['range'],
433
- tpoints: tra['points'],
434
- ttags: tra['tags'],
435
- theats: tra['heats'],
436
- theaps: tra['heaps'],
437
+ tpoints: commaify(tra['points']),
438
+ ttags: commaify(tra['tags']),
439
+ theats: commaify(tra['heats']),
440
+ theaps: commaify(tra['heaps']),
437
441
  content: to_blob(tra),
438
442
  status: 'active',
439
443
  ctime: now,
@@ -509,6 +513,62 @@ module Flor
509
513
 
510
514
  protected
511
515
 
516
+ def _commaify(o)
517
+
518
+ if Flor.is_regex_tree?(o)
519
+ o[1].to_s
520
+ else #if o.is_a?(String)
521
+ o.split(/\s*,\s*/).join(',')
522
+ end
523
+ end
524
+
525
+ def commaify(o)
526
+
527
+ return nil unless o
528
+
529
+ o = [ o ] if Flor.is_regex_tree?(o)
530
+ o = [ o ] unless o.is_a?(Array)
531
+
532
+ o.collect { |e| _commaify(e) }.join(',')
533
+ end
534
+
535
+ def consume_and_archive(messages)
536
+
537
+ transync do
538
+
539
+ n = Flor.tstamp
540
+ u = @unit.identifier
541
+
542
+ @db[:flor_messages]
543
+ .where(
544
+ id: messages.collect { |m| m['mid'] }.compact)
545
+ .update(
546
+ status: 'consumed', mtime: n, munit: u)
547
+
548
+ @db[:flor_messages]
549
+ .import(
550
+ [ :domain, :exid, :point, :content,
551
+ :status, :ctime, :mtime, :cunit, :munit ],
552
+ messages
553
+ .select { |m|
554
+ ! m['mid'] && POINTS_TO_ARCHIVE.include?(m['point']) }
555
+ .map { |m|
556
+ [ Flor.domain(m['exid']), m['exid'], m['point'], to_blob(m),
557
+ 'consumed', n, n, u, u ] })
558
+ end
559
+ end
560
+
561
+ def consume_and_discard(messages)
562
+
563
+ synchronize do
564
+
565
+ @db[:flor_messages]
566
+ .where(
567
+ id: messages.collect { |m| m['mid'] }.compact)
568
+ .delete
569
+ end
570
+ end
571
+
512
572
  def load_timers
513
573
 
514
574
  now = Flor.tstamp
@@ -664,11 +724,11 @@ module Flor
664
724
 
665
725
  f =
666
726
  case type
667
- when 'cron' then Fugit.parse_cron(string) || Fugit.parse_nat(string)
668
- when 'at' then Fugit.parse_at(string)
669
- when 'in' then Fugit.parse_duration(string)
670
- #when 'every' then Fugit.parse_duration(string)
671
- else Fugit.parse(string)
727
+ when 'cron' then Fugit.parse_cron(string) || Fugit.parse_nat(string)
728
+ when 'at' then Fugit.parse_at(string)
729
+ when 'in' then Fugit.parse_duration(string)
730
+ #when 'every' then Fugit.parse_duration(string)
731
+ else Fugit.parse(string)
672
732
  end
673
733
 
674
734
  nt = f.is_a?(Time) ? f : f.next_time(from || Time.now) # local...