flor 1.0.1 → 1.1.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1e6d591b2767c85e10b697b1839fb9142b52d0409bca73da41baa571f4e3aa8b
4
- data.tar.gz: 6e9b4d916ccf37ee6189aed18a9365b98e79f8b6329cce4f1b4ed89f0acd4230
3
+ metadata.gz: 62c06708e2abc35f1b43c2cf9ecaf73d5f89633433c60af3864450d91ccabf47
4
+ data.tar.gz: 33d4531561c77d0f43cd97a51775d1079a41885650df73583c26050846177087
5
5
  SHA512:
6
- metadata.gz: 1572f392ea4e391674b79b02d09992a3c27c64485c64132ac84c3d37a031436fec5090cf2e2932996c8ee7e2879ce5f502a7c037324d48cba505b15d3924a0bf
7
- data.tar.gz: a8f1272f9b4a992fac31530d9e53b6e8c91fbd8032891087dd546c677fb1c9f8218a051eac9fe54c23c8bb8cdb6b3b727f269768f92fb62d8fb6cb1bdf1318f0
6
+ metadata.gz: b27557dcfbf250d1b0af9c4220a140b66e84aceb0506d947a51a1440fabf385f21b2ce125c08178bf68d5c13b87ee3d9967fdf0a46d5ef3b473991b3329139c4
7
+ data.tar.gz: 48fdc31b116360526afa4fe6b40e0b550c04bc451085aac55005f28add44bb30802029218f73fd898965b54f2a88acb95eb9420e73ca83d494b9fed1fdf64cf7
@@ -2,6 +2,15 @@
2
2
  # CHANGELOG.md
3
3
 
4
4
 
5
+ ## flor 1.1.0 released 2021-01-06
6
+
7
+ * Introduce Tasker #set_payload and #set_vars
8
+ * Introduce the ModuleGanger
9
+ * Allow for domain/dot.json taskers
10
+ * Introduce Flor::StagedBasicTasker
11
+ * Fix service/executor issue in Caller
12
+
13
+
5
14
  ## flor 1.0.1 released 2020-11-23
6
15
 
7
16
  * Accept sto_uri strings pointing to constant like 'DB'
@@ -15,7 +15,7 @@ require 'dense'
15
15
 
16
16
  module Flor
17
17
 
18
- VERSION = '1.0.1'
18
+ VERSION = '1.1.0'
19
19
  end
20
20
 
21
21
  require 'flor/colours'
@@ -254,7 +254,7 @@ module Flor
254
254
  o.each { |ee| ee['_path'] = path if ee.is_a?(Hash) }
255
255
  end
256
256
 
257
- o
257
+ rework_conf(o)
258
258
  end
259
259
 
260
260
  def interpret_path(path, context=nil)
@@ -299,6 +299,31 @@ module Flor
299
299
 
300
300
  ps.last == 'etc' ? File.absolute_path(File.join(dir, '..')) : dir
301
301
  end
302
+
303
+ protected
304
+
305
+ # For now, only the return procedure has to be marshalled back
306
+ # to the "return" string, gh-36
307
+ #
308
+ def rework_conf(o)
309
+
310
+ case o
311
+ when Array
312
+ o.collect { |e|
313
+ rework_conf(e) }
314
+ when Hash
315
+ o.inject({}) { |h, (k, v)|
316
+ h[k] =
317
+ if Flor.is_proc_tree?(v) && v[1]['proc'] == 'return'
318
+ 'return'
319
+ else
320
+ rework_conf(v)
321
+ end
322
+ h }
323
+ else
324
+ o
325
+ end
326
+ end
302
327
  end
303
328
  end
304
329
  end
@@ -20,7 +20,7 @@ module Flor
20
20
  signal cancel
21
21
  terminated failed ceased
22
22
  idle
23
- ]
23
+ ].freeze
24
24
 
25
25
  class << self
26
26
 
@@ -279,13 +279,19 @@ module Flor
279
279
  #
280
280
  # functions about time
281
281
 
282
+ # Used by the storage in its next_time endeavours
283
+ #
284
+ def tstam
285
+ Time.now.utc.strftime('%FT%T')
286
+ end
287
+
282
288
  def isostamp(show_date, show_time, show_usec, time)
283
289
 
284
290
  t = (time || Time.now).utc
285
291
  s = StringIO.new
286
292
 
287
- s << t.strftime('%Y-%m-%d') if show_date
288
- s << t.strftime('T%H:%M:%S') if show_time
293
+ s << t.strftime('%F') if show_date # YYYY-mm-dd
294
+ s << t.strftime('T%T') if show_time # THH:MM:SS
289
295
  s << sprintf('.%06d', t.usec) if show_time && show_usec
290
296
  s << 'Z' if show_time
291
297
 
@@ -367,11 +373,18 @@ module Flor
367
373
  sub_domain?(dom, sub)
368
374
  end
369
375
 
376
+ def dot_join(*elts)
377
+
378
+ elts.collect(&:to_s).select { |e| e.length > 0 }.join('.')
379
+ end
380
+
370
381
  def sub_domain?(dom, sub)
371
382
 
372
- dom == '' ||
373
- sub == dom ||
374
- sub[0, dom.length + 1] == dom + '.'
383
+ d = dom.is_a?(Array) ? dot_join(*dom) : dom.to_s
384
+
385
+ d == '' ||
386
+ sub == d ||
387
+ sub[0, d.length + 1] == d + '.'
375
388
  end
376
389
  alias subdomain? sub_domain?
377
390
 
@@ -324,6 +324,7 @@ module Flor
324
324
  def msg_to_detail_s(executor, m, opts={})
325
325
 
326
326
  return if m['_detail_msg_flag']
327
+ #
327
328
  m['_detail_msg_flag'] = true if opts[:flag]
328
329
 
329
330
  o = StringIO.new
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'irb'
4
+
5
+ #require 'sequel'
6
+ require 'flor'
7
+ require 'flor/unit'
8
+
9
+
10
+ p [ RUBY_VERSION, RUBY_PLATFORM ]
11
+
12
+ puts
13
+
14
+ ENV.each do |k, v|
15
+ next unless k.match?(/RUBY|GEM/)
16
+ puts "* #{k}: #{v}"
17
+ end
18
+
19
+ ARGV.each do |arg|
20
+ if arg.match(/:/)
21
+ DB = Sequel.connect(arg)
22
+ p DB
23
+ end
24
+ end
25
+
26
+ #MODELS = [ :executions, :timers, :traces, :traps, :pointers, :messages ]
27
+ if defined?(DB)
28
+ Flor::Message.dataset = DB[:flor_messages]
29
+ end
30
+
31
+ ARGV.clear
32
+ IRB.start
33
+
@@ -15,11 +15,16 @@ module Flor::Tools
15
15
  def initialize(argv=nil)
16
16
 
17
17
  env = ENV['FLOR_ENV'] || 'shell'
18
- @root = "envs/#{env}"
18
+
19
+ @root = File.directory?(env) ? env : "envs/#{env}"
19
20
 
20
21
  prepare_home
21
22
 
22
- @unit = Flor::Unit.new("#{@root}/etc/conf.json")
23
+ over_conf = {}
24
+ #
25
+ c = ENV['FLOR_STO_URI']; over_conf['sto_uri'] = c if c
26
+
27
+ @unit = Flor::Unit.new("#{@root}/etc/conf.json", over_conf)
23
28
 
24
29
  @unit.conf['unit'] = 'cli'
25
30
  #unit.hooker.add('journal', Flor::Journal)
@@ -31,7 +36,11 @@ module Flor::Tools
31
36
  @mute = false
32
37
  @paging = true
33
38
 
34
- @unit.start
39
+ if ENV['FLOR_NO_START']
40
+ @unit.check_migration_version
41
+ else
42
+ @unit.start
43
+ end
35
44
 
36
45
  @flow_path = File.join(@root, 'home/scratch.flo')
37
46
  @ra_flow_path = File.join(@root, 'home/ra_scratch.flo')
@@ -22,6 +22,7 @@ require 'flor/unit/loader'
22
22
  require 'flor/unit/hloader'
23
23
  require 'flor/unit/ganger'
24
24
  require 'flor/unit/spooler'
25
+ require 'flor/unit/gangers'
25
26
  require 'flor/unit/taskers'
26
27
 
27
28
  Flor.load_procedures('punit')
@@ -107,9 +107,11 @@ module Flor
107
107
  #
108
108
  # call
109
109
 
110
- p = message['point']
111
- ms = [ "on_#{p}", :on_message, :on, p ]
112
- ms = ms + [ :on_cancel, :cancel ] if p == 'detask'
110
+ pt = message['point']
111
+
112
+ ms = [ "call_#{pt}", "on_#{pt}", :on_message, :on, pt ]
113
+ ms = ms + [ :on_cancel, :cancel ] if pt == 'detask'
114
+
113
115
  m = ms.find { |mm| o.respond_to?(mm) }
114
116
 
115
117
  fail(
@@ -121,7 +123,7 @@ module Flor
121
123
  case o.method(m).arity
122
124
  when 1 then o.send(m, message)
123
125
  when 2 then o.send(m, conf, message)
124
- when 3 then o.send(m, executor, conf, message)
126
+ when 3 then o.send(m, service, conf, message)
125
127
  when -1 then o.send(m, {
126
128
  service: service, configuration: conf, message: message })
127
129
  else o.send(m)
@@ -109,15 +109,13 @@ module Flor
109
109
  rescue Exception => exc
110
110
 
111
111
  # TODO eventually, have a dump dir
112
+
112
113
  fn =
113
- [
114
- 'flor',
115
- @unit.conf['env'], @unit.identifier, @exid,
116
- 'r' + counter('runs').to_s
117
- ].collect(&:to_s).join('_') + '.dump'
114
+ [ 'flor', @unit.conf['env'], @unit.identifier, @exid,
115
+ 'r' + counter('runs').to_s ].collect(&:to_s).join('_') + '.dump'
118
116
 
119
117
  @unit.logger.error(
120
- "#{self.class}#do_run()", exc, "(dumping to #{fn})")
118
+ "#{self.class}#do_run()", exc, "(dumping to #{fn} ...)")
121
119
 
122
120
  File.open(fn, 'wb') do |f|
123
121
  f.puts(Flor.to_pretty_s({
@@ -134,19 +132,26 @@ module Flor
134
132
  f.puts(on_do_run_exc(exc))
135
133
  end
136
134
 
135
+ @unit.logger.error(
136
+ "#{self.class}#do_run()", exc, "(dumped to #{fn})")
137
+
137
138
  #puts on_do_run_exc(exc)
138
139
  # dump notification above
139
140
  end
140
141
 
141
142
  def task(message)
142
143
 
143
- return error_reply(
144
- node(message['nid']),
145
- message,
146
- "don't know how to apply #{message['tasker'].inspect}"
147
- ) if message['routed'] == false
148
- #
149
- # use an error message similar to what the core executor would emit
144
+ if message['routed'] == false
145
+
146
+ t = message['tasker']
147
+ n = node(message['nid'])
148
+
149
+ msg = n['heat0'] != t ?
150
+ "tasker #{t.inspect} not found" :
151
+ "don't know how to apply #{t.inspect}"
152
+
153
+ return error_reply(n, message, msg)
154
+ end
150
155
 
151
156
  @unit.ganger.task(self, message)
152
157
  end
@@ -54,6 +54,9 @@ module Flor
54
54
  (@unit.loader.tasker(domain, 'ganger', message) ||
55
55
  @unit.loader.tasker(domain, 'tasker', message))) ||
56
56
  @unit.loader.tasker(domain, tname, message)
57
+ #puts "=" * 80
58
+ #pp tconf
59
+ #puts "=" * 80
57
60
 
58
61
  fail ArgumentError.new(
59
62
  "tasker #{tname.inspect} not found"
@@ -0,0 +1,125 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Flor
4
+
5
+ # A ModuleGanger accepts a `module:` conf entry that points to a Ruby
6
+ # module. The tasker implementations are searched for among the classes
7
+ # in the given module.
8
+ #
9
+ # Among the tasker classes (classes that respond to on_task, on_detask, ...)
10
+ # it selects the first tasker that matches the tasker name.
11
+ #
12
+ class ModuleGanger
13
+
14
+ def initialize(service, conf, message)
15
+
16
+ @service = service
17
+ @conf = conf
18
+ @message = message
19
+ end
20
+
21
+ def task
22
+
23
+ tas = @message['tasker']
24
+ clas = list_tasker_classes
25
+ cla = clas.find { |c| tasker_name(c) == tas }
26
+
27
+ return [ Flor.dup_and_merge(@message, 'routed' => false) ] \
28
+ unless cla
29
+
30
+ call_tasker(cla)
31
+ end
32
+
33
+ alias detask task
34
+
35
+ protected
36
+
37
+ def list_tasker_classes
38
+
39
+ mod_name = @conf['module']
40
+
41
+ fail ArgumentError.new('ganger module: configuration entry missing') \
42
+ unless mod_name
43
+
44
+ mod = Flor.const_lookup(mod_name) rescue nil
45
+
46
+ fail ArgumentError.new("ganger cannot find module #{mod_name.inspect}") \
47
+ unless mod
48
+
49
+ list_classes(mod, [])
50
+ end
51
+
52
+ def list_classes(start, r)
53
+
54
+ # place leave classes on top if possible
55
+ # within a level, sort alphabetically
56
+
57
+ clas = start.constants.collect { |co| start.const_get(co) }
58
+ clas, mods = clas.partition { |c| c.is_a?(Class) }
59
+
60
+ mods.each { |m| list_classes(m, r) }
61
+ r.concat(clas.select { |c| tasker?(c) }.sort_by { |c| c.name })
62
+
63
+ r
64
+ end
65
+
66
+ TASKER_METHODS = [
67
+ :on, :on_message,
68
+ :task, :on_task,
69
+ :detask, :on_detask, :cancel, :on_cancel
70
+ ].freeze
71
+
72
+ def tasker?(cla)
73
+
74
+ return true if (TASKER_METHODS & cla.public_instance_methods).any?
75
+ return true if (TASKER_METHODS & cla.public_methods).any?
76
+ false
77
+ end
78
+
79
+ def tasker_name(cla)
80
+
81
+ if cla.public_instance_methods.include?(:tasker_name)
82
+
83
+ unless cla.respond_to?(:_ganged)
84
+ class << cla
85
+ attr_accessor :_ganged
86
+ end
87
+ cla._ganged = cla.allocate
88
+ end
89
+
90
+ call_tasker_name(cla._ganged)
91
+
92
+ elsif cla.public_methods.include?(:tasker_name)
93
+
94
+ call_tasker_name(cla)
95
+
96
+ else
97
+
98
+ cla.name.split('::').last.gsub(/Tasker\z/, '')
99
+ .gsub(/([a-z])([A-Z])/) { |_| $1 + '_' + $2.downcase }
100
+ .gsub(/([A-Z])/) { |c| c.downcase }
101
+ end
102
+ end
103
+
104
+ def call_tasker_name(o)
105
+
106
+ case i = o.method(:tasker_name).arity
107
+ when 1 then o.tasker_name(@message)
108
+ when 2 then o.tasker_name(@conf, @message)
109
+ when 3 then o.tasker_name(@service, @conf, @message)
110
+ when -1 then o.tasker_name(
111
+ service: @service, conf: @conf, message: @message)
112
+ else o.tasker_name
113
+ end
114
+ end
115
+
116
+ def call_tasker(c)
117
+
118
+ cnf = @conf.merge('class' => c)
119
+
120
+ @service.unit.caller
121
+ .call(@service, cnf, @message)
122
+ end
123
+ end
124
+ end
125
+
@@ -46,7 +46,7 @@ module Flor
46
46
  .collect { |pa| [ pa, expose_d(pa, {}) ] }
47
47
  .select { |pa, d| Flor.sub_domain?(d, domain) }
48
48
  .sort_by { |pa, d| d.count('.') }
49
- .inject({}) { |vars, (pa, _)| vars.merge!(eval(pa, {})) }
49
+ .inject({}) { |vars, (pa, _)| vars.merge!(eval_variables(pa, {})) }
50
50
  end
51
51
 
52
52
  #def procedures(path)
@@ -87,25 +87,28 @@ module Flor
87
87
  .select { |pa| pa.index('/lib/taskers/') }
88
88
  .collect { |pa| [ pa, *expose_dn(pa, {}) ] }
89
89
  .select { |pa, d, n|
90
- Flor.sub_domain?([ d, n ].join('.'), domain) ||
90
+ Flor.sub_domain?([ d, n ], domain) ||
91
91
  (n == name && Flor.sub_domain?(d, domain)) }
92
92
  .sort_by { |pa, d, n| d.count('.') }
93
93
  .last
94
94
 
95
95
  return nil unless pat
96
96
 
97
- conf = eval(pat, message)
97
+ conf = eval_tasker_conf(pat, message)
98
98
 
99
99
  return conf if nam == name
100
100
 
101
- conf = conf[name]
101
+ cnf = conf[name]
102
102
 
103
- return nil unless conf
103
+ return nil unless cnf
104
104
 
105
- (conf.is_a?(Array) ? conf : [ conf ])
106
- .each { |h| h['_path'] = pat }
105
+ extras = conf.select { |_, v| ! v.is_a?(Hash) }
106
+ extras['_path'] = pat
107
107
 
108
- conf
108
+ (cnf.is_a?(Array) ? cnf : [ cnf ])
109
+ .each { |h| h.merge!(extras) }
110
+
111
+ cnf
109
112
  end
110
113
 
111
114
  def hooks(domain)
@@ -119,8 +122,8 @@ module Flor
119
122
  .select { |pa, d| Flor.sub_domain?(d, domain) }
120
123
  .sort_by { |pa, d| d.count('.') }
121
124
  .collect { |pa, d|
122
- eval(pa, {})
123
- .each_with_index { |h, i| h['_path'] = pa + ":#{i}" } }
125
+ eval_hook_conf(pa, {})
126
+ .each_with_index { |h, i| h['_path'] = "#{pa}:#{i}" } }
124
127
  .flatten(1)
125
128
  end
126
129
 
@@ -247,6 +250,35 @@ module Flor
247
250
  end
248
251
  end
249
252
 
253
+ def eval_variables(path, context)
254
+ eval(path, context)
255
+ end
256
+ def eval_tasker_conf(path, context)
257
+ eval(path, context)
258
+ end
259
+ # TODO like in eval_hook_conf, reject fautly tasker confs...
260
+ # TODO like in eval_hook_conf, reject fautly variables...
261
+
262
+ def eval_hook_conf(path, context)
263
+
264
+ a = eval(path, context)
265
+
266
+ fail ArgumentError.new(
267
+ "hook conf at #{path} must be an array of hashes"
268
+ ) unless a.is_a?(Array)
269
+
270
+ a.each do |e|
271
+ fail ArgumentError.new(
272
+ "hook conf at #{path} has non-hash entry #{e.inspect}"
273
+ ) unless e.is_a?(Hash)
274
+ fail ArgumentError.new(
275
+ "hook conf at #{path} has incorrect point #{e['point'].inspect}"
276
+ ) unless e['point'].is_a?(String)
277
+ end
278
+
279
+ a
280
+ end
281
+
250
282
  def eval(path, context)
251
283
 
252
284
  ext =
@@ -71,7 +71,8 @@ module Flor
71
71
  dbi = ' ' + dbi if dbi.length > 0
72
72
 
73
73
  txt = elts.collect(&:to_s).join(' ')
74
- err = elts.find { |e| e.is_a?(Exception) }
74
+
75
+ err = find_err(elts)
75
76
 
76
77
  head = "#{stp} #{@uni}#{dbi} #{lvl} "
77
78
 
@@ -198,7 +199,8 @@ module Flor
198
199
 
199
200
  return unless @unit.conf['log_err']
200
201
 
201
- @out.puts(Flor.msg_to_detail_s(executor, message, opts.merge(flag: true)))
202
+ s = Flor.msg_to_detail_s(executor, message, opts.merge(flag: true))
203
+ @out.puts(s) if s
202
204
  end
203
205
 
204
206
  def log_src(source, opts, log_opts={})
@@ -245,6 +247,13 @@ module Flor
245
247
  message[0..k + 2 + 4] + "(...len#{i - (k + 2 + 1)})" + message[i..-1]
246
248
  end
247
249
 
250
+ def find_err(elts)
251
+
252
+ elts.find { |e| e.is_a?(Exception) } ||
253
+ (defined?(Java) &&
254
+ elts.find { |e| e.class.ancestors.include?(Java::JavaLang::Error) })
255
+ end
256
+
248
257
  class Out
249
258
 
250
259
  attr_reader :unit
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+
3
4
  module Flor
4
5
 
5
6
  class Message < FlorModel
@@ -19,6 +20,10 @@ module Flor
19
20
  #
20
21
  # index :exid
21
22
  #end
23
+
24
+ def nid; data['nid']; end
25
+ def tasker; data['tasker']; end
26
+ alias payload data
22
27
  end
23
28
  end
24
29
 
@@ -138,11 +138,7 @@ module Flor
138
138
 
139
139
  # TODO heartbeat, every x minutes, when idle, log something
140
140
 
141
- fail(
142
- "database not ready, " +
143
- "db ver: #{@storage.db_version.inspect}, " +
144
- "mig ver: #{@storage.migration_version}"
145
- ) if !! @conf['sto_migration_check'] && @storage.ready?
141
+ check_migration_version
146
142
 
147
143
  @thread_status = :running
148
144
 
@@ -162,6 +158,15 @@ module Flor
162
158
  self
163
159
  end
164
160
 
161
+ def check_migration_version
162
+
163
+ fail(
164
+ "database not ready, " +
165
+ "db ver: #{@storage.db_version.inspect}, " +
166
+ "mig ver: #{@storage.migration_version}"
167
+ ) if !! @conf['sto_migration_check'] && @storage.ready?
168
+ end
169
+
165
170
  def stop
166
171
 
167
172
  @thread_status = :stop
@@ -590,7 +595,7 @@ module Flor
590
595
 
591
596
  rescue Exception => ex
592
597
 
593
- puts on_start_exc(ex)
598
+ puts(on_start_exc(ex))
594
599
  end
595
600
 
596
601
  def prepare_message(point, args)
@@ -678,9 +683,9 @@ module Flor
678
683
  def should_wake_up?
679
684
 
680
685
  return true if @wake_up
681
- return true if Time.now - @reloaded_at >= reload_after
686
+ return true if (Time.now - @reloaded_at) >= reload_after
682
687
 
683
- @next_time && @next_time <= Flor.tstamp.split('.').first
688
+ @next_time && (@next_time <= Flor.tstam)
684
689
  end
685
690
 
686
691
  def unreserve_messages
@@ -7,6 +7,14 @@ module Flor
7
7
 
8
8
  class Storage
9
9
 
10
+ MESSAGE_COLUMNS = [
11
+ :domain, :exid, :point, :content,
12
+ :status, :ctime, :mtime, :cunit, :munit
13
+ ].freeze
14
+ POINTER_COLUMNS = [
15
+ :domain, :exid, :nid, :type, :name, :value, :ctime, :cunit
16
+ ].freeze
17
+
10
18
  attr_reader :unit, :db, :models
11
19
 
12
20
  attr_reader :mutex
@@ -352,8 +360,7 @@ module Flor
352
360
 
353
361
  @db[:flor_messages]
354
362
  .import(
355
- [ :domain, :exid, :point, :content,
356
- :status, :ctime, :mtime, :cunit, :munit ],
363
+ MESSAGE_COLUMNS,
357
364
  unstored.map { |m|
358
365
  [ Flor.domain(m['exid']), m['exid'], m['point'], to_blob(m),
359
366
  'created', n, n, u, u ] }) \
@@ -517,25 +524,6 @@ module Flor
517
524
  end
518
525
  end
519
526
 
520
- def put_task_pointer(msg, tname, tconf)
521
-
522
- exid = msg['exid']
523
- dom = Flor.domain(exid)
524
-
525
- synchronize do
526
-
527
- @db[:flor_pointers]
528
- .insert(
529
- domain: dom,
530
- exid: exid,
531
- nid: msg['nid'],
532
- type: 'tasker',
533
- name: tname,
534
- ctime: Flor.tstamp,
535
- cunit: @unit.identifier)
536
- end
537
- end
538
-
539
527
  def fetch_next_time
540
528
 
541
529
  t =
@@ -598,14 +586,13 @@ module Flor
598
586
 
599
587
  @db[:flor_messages]
600
588
  .where(
601
- id: messages.collect { |m| m['mid'] }.compact)
589
+ id: messages.collect { |m| m['mid'] }.uniq.compact)
602
590
  .update(
603
591
  status: 'consumed', mtime: n, munit: u)
604
592
 
605
593
  @db[:flor_messages]
606
594
  .import(
607
- [ :domain, :exid, :point, :content,
608
- :status, :ctime, :mtime, :cunit, :munit ],
595
+ MESSAGE_COLUMNS,
609
596
  messages
610
597
  .select { |m|
611
598
  ! m['mid'] && POINTS_TO_ARCHIVE.include?(m['point']) }
@@ -621,19 +608,16 @@ module Flor
621
608
 
622
609
  @db[:flor_messages]
623
610
  .where(
624
- id: messages.collect { |m| m['mid'] }.compact)
611
+ id: messages.collect { |m| m['mid'] }.uniq.compact)
625
612
  .delete
626
613
  end
627
614
  end
628
615
 
629
616
  def load_timers
630
617
 
631
- now = Flor.tstamp
632
- no = now[0, now.rindex('.')]
633
-
634
618
  timers
635
619
  .where(status: 'active')
636
- .where { ntime <= no }
620
+ .where { ntime <= Flor.tstam }
637
621
  .order(:ntime)
638
622
  .all
639
623
 
@@ -703,9 +687,9 @@ module Flor
703
687
 
704
688
  def update_pointers(exe, status, now)
705
689
 
706
- # Q Should we archive old pointers?
707
- # Well, it might be better to only archive the execution and leave
708
- # in there enough information...
690
+ # Q Should we archive old pointers?
691
+ # A Well, it might be better to only archive the execution and leave
692
+ # in there enough information...
709
693
 
710
694
  exid = exe['exid']
711
695
 
@@ -731,23 +715,25 @@ module Flor
731
715
 
732
716
  ts = node['tags']
733
717
  ts.each { |t|
734
- a << [ dom, exid, nid, 'tag', t, nil, now, u ] } if ts
718
+ a << [ dom, exid, nid, 'tag', t, nil, now, u, nil ] } if ts
735
719
 
736
720
  vs = nid == '0' ? node['vars'] : nil
737
721
  vs.each { |k, v|
738
722
  case v; when Numeric, String, TrueClass, FalseClass, NilClass
739
- a << [ dom, exid, '0', 'var', k, v.to_s, now, u ]
723
+ a << [ dom, exid, '0', 'var', k, v.to_s, now, u, nil ]
740
724
  when Array, Hash
741
725
  s = '(array)'; s = '(object)' if v.is_a?(Hash)
742
- a << [ dom, exid, '0', 'var', k, s, now, u ]
726
+ a << [ dom, exid, '0', 'var', k, s, now, u, nil ]
743
727
  else
744
- a << [ dom, exid, '0', 'var', k, nil, now, u ]
728
+ a << [ dom, exid, '0', 'var', k, nil, now, u, nil ]
745
729
  end } if vs
746
730
 
747
- #ta = node['heap'] == 'task' ? node['task'] : nil
748
- ta = node['task']
749
- a << [ dom, exid, nid, 'tasker', ta['tasker'], ta['name'], now, u ] \
750
- if ta
731
+ if ta = node['task']
732
+ tasker = ta['tasker']
733
+ name = ta['name']
734
+ content = { message: node['message'], atts: node['atts'] }
735
+ a << [ dom, exid, nid, 'tasker', tasker, name, now, u, content ]
736
+ end
751
737
 
752
738
  a }
753
739
 
@@ -755,17 +741,35 @@ module Flor
755
741
  .where(exid: exid)
756
742
  .select(:nid, :type, :name)
757
743
  .all
758
- pointers.reject! { |_, _, ni, ty, na, _, _, _|
744
+ pointers.reject! { |_, _, ni, ty, na, _, _, _, _|
759
745
  cps.find { |cp| cp[:nid] == ni && cp[:type] == ty && cp[:name] == na } }
760
746
  #
761
747
  # don't insert when already inserted
762
748
 
749
+ if pointer_columns.include?(:content)
750
+ pointers.each { |ptr|
751
+ c = ptr[8]; ptr[8] = to_blob(c) if c }
752
+ else
753
+ pointers.each { |ptr|
754
+ ptr.pop }
755
+ end
756
+
763
757
  @db[:flor_pointers]
764
758
  .import(
765
- [ :domain, :exid, :nid, :type, :name, :value, :ctime, :cunit ],
759
+ pointer_columns,
766
760
  pointers)
767
761
  end
768
762
 
763
+ def pointer_columns
764
+
765
+ @pointer_columns ||=
766
+ if @db[:flor_pointers].columns.include?(:content)
767
+ POINTER_COLUMNS + [ :content ]
768
+ else
769
+ POINTER_COLUMNS
770
+ end
771
+ end
772
+
769
773
  def determine_type_and_schedule(message)
770
774
 
771
775
  t, s = message['type'], message['string']
@@ -29,6 +29,29 @@ module Flor
29
29
  alias task_name taskname
30
30
 
31
31
  def vars; @message['vars']; end
32
+ alias variables vars
33
+
34
+ def set_payload(h)
35
+ fail TypeError.new("not a hash but a #{fs.class}") unless h.is_a?(Hash)
36
+ @message['payload'] = h
37
+ end
38
+ alias set_fields set_payload
39
+
40
+ def set_vars(h)
41
+ fail TypeError.new("not a hash but a #{fs.class}") unless h.is_a?(Hash)
42
+ @message['vars'] = h
43
+ end
44
+ alias set_variables set_vars
45
+
46
+ #def merge_into_payload(h)
47
+ # @message['payload'].merge(h)
48
+ #end
49
+ #alias merge_into_fields merge_into_payload
50
+ #def merge_into_vars(h)
51
+ # @message['vars'].merge(h)
52
+ #end
53
+ #
54
+ # no for now, payload.merge(h) and vars.merge(h) do suffice
32
55
 
33
56
  def execution
34
57
 
@@ -112,5 +135,50 @@ module Flor
112
135
  h
113
136
  end
114
137
  end
138
+
139
+ # A BasicTasker with stages (pre / on / post)
140
+ #
141
+ class StagedBasicTasker < BasicTasker
142
+
143
+ def call_task
144
+
145
+ call_one_of(:pre_task)
146
+ call_one_of(:on_task, :task)
147
+ end
148
+
149
+ def call_detask
150
+
151
+ call_one_of(:pre_detask, :pre_cancel)
152
+ call_one_of(:on_detask, :on_cancel, :detask, :cancel)
153
+ end
154
+
155
+ protected
156
+
157
+ def call_one_of(*ms)
158
+
159
+ m = ms.flatten.find { |mm| respond_to?(mm) }
160
+
161
+ send(m) if m
162
+ end
163
+
164
+ def reply(message=@message, force=false)
165
+
166
+ fail ArgumentError.new(
167
+ "argument to reply must be a Hash but is #{message.class}"
168
+ ) unless message.is_a?(Hash)
169
+
170
+ pt = @message['point']
171
+
172
+ ms = [ "post_#{pt}" ]; ms << :post_cancel if pt == 'detask'
173
+ #
174
+ call_one_of(ms)
175
+
176
+ msg = derive_message(message)
177
+
178
+ @ganger.return(msg) if force || @ganger
179
+
180
+ [] # very important, return no further messages
181
+ end
182
+ end
115
183
  end
116
184
 
@@ -53,7 +53,7 @@ module Flor
53
53
 
54
54
  @mutex.synchronize do
55
55
 
56
- return false unless match?(message)
56
+ return false unless msg_match?(message)
57
57
 
58
58
  @serie.shift
59
59
  return false if @serie.any?
@@ -151,7 +151,7 @@ module Flor
151
151
 
152
152
  protected
153
153
 
154
- def match?(message)
154
+ def msg_match?(message)
155
155
 
156
156
  mpoint = message['point']
157
157
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flor
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Mettraux
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-23 00:00:00.000000000 Z
11
+ date: 2021-01-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: munemo
@@ -248,6 +248,7 @@ files:
248
248
  - lib/flor/punit/trap.rb
249
249
  - lib/flor/to_string.rb
250
250
  - lib/flor/tools/env.rb
251
+ - lib/flor/tools/firb.rb
251
252
  - lib/flor/tools/shell.rb
252
253
  - lib/flor/tools/shell_out.rb
253
254
  - lib/flor/tt.rb
@@ -257,6 +258,7 @@ files:
257
258
  - lib/flor/unit/dump.rb
258
259
  - lib/flor/unit/executor.rb
259
260
  - lib/flor/unit/ganger.rb
261
+ - lib/flor/unit/gangers.rb
260
262
  - lib/flor/unit/hloader.rb
261
263
  - lib/flor/unit/hook.rb
262
264
  - lib/flor/unit/hooker.rb