flor 1.0.1 → 1.1.0

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