flor 1.0.1 → 1.2.2

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: f1dae0857de545986e18ad31a41f37a6ead6687b34d3c06de22aafa606369283
4
+ data.tar.gz: 9843d0124a2c3948ffc6846e7854854f5eda37d95bd02bfc02cecfefab0e5ea1
5
5
  SHA512:
6
- metadata.gz: 1572f392ea4e391674b79b02d09992a3c27c64485c64132ac84c3d37a031436fec5090cf2e2932996c8ee7e2879ce5f502a7c037324d48cba505b15d3924a0bf
7
- data.tar.gz: a8f1272f9b4a992fac31530d9e53b6e8c91fbd8032891087dd546c677fb1c9f8218a051eac9fe54c23c8bb8cdb6b3b727f269768f92fb62d8fb6cb1bdf1318f0
6
+ metadata.gz: 185490e0054ffdfe7c078b71955834679a398266e613be5d8bb6b63bc626bf4727bd4fd45249e7dea14cdd91a85c5ad92a1f6b3b120117c7c274b41d5478ba31
7
+ data.tar.gz: e6dde7ae40f3ef6d2c7a620d6a07840c6591b745798962d039ac39fa426e4bcef51f3c67e97d9a367bb981918ec5fe98934393eec2cd7e87046b8d491b9a6511
data/CHANGELOG.md CHANGED
@@ -2,6 +2,36 @@
2
2
  # CHANGELOG.md
3
3
 
4
4
 
5
+ ## flor 1.2.2 released 2021-03-29
6
+
7
+ * Include data in flor_pointers
8
+ * Ensure flor_pointers name is a string
9
+
10
+
11
+ ## flor 1.2.1 released 2021-03-22
12
+
13
+ * If conf sto_db_logger is false, do not attach a logger to the db connection
14
+
15
+
16
+ ## flor 1.2.0 released 2021-03-15
17
+
18
+ * Add #attd, #attl, #att_texts to Flor::Pointer
19
+
20
+
21
+ ## flor 1.1.1 released 2021-03-03
22
+
23
+ * Use YAML to have more compact msg_to_detail_s
24
+
25
+
26
+ ## flor 1.1.0 released 2021-01-06
27
+
28
+ * Introduce Tasker #set_payload and #set_vars
29
+ * Introduce the ModuleGanger
30
+ * Allow for domain/dot.json taskers
31
+ * Introduce Flor::StagedBasicTasker
32
+ * Fix service/executor issue in Caller
33
+
34
+
5
35
  ## flor 1.0.1 released 2020-11-23
6
36
 
7
37
  * Accept sto_uri strings pointing to constant like 'DB'
data/LICENSE.txt CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- Copyright (c) 2015-2020, John Mettraux, jmettraux+flor@gmail.com
2
+ Copyright (c) 2015-2021, John Mettraux, jmettraux+flor@gmail.com
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
 
2
2
  # flor
3
3
 
4
- [![Build Status](https://secure.travis-ci.org/floraison/flor.svg)](http://travis-ci.org/floraison/flor)
4
+ [![tests](https://github.com/floraison/flor/workflows/test/badge.svg)](https://github.com/floraison/flor/actions)
5
5
  [![Gem Version](https://badge.fury.io/rb/flor.svg)](http://badge.fury.io/rb/flor)
6
6
 
7
7
  Flor is a "Ruby workflow engine", if that makes any sense.
data/lib/flor.rb CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'pp'
4
4
  require 'json'
5
+ require 'yaml'
5
6
  require 'logger'
6
7
  require 'thread'
7
8
  require 'digest'
@@ -15,7 +16,7 @@ require 'dense'
15
16
 
16
17
  module Flor
17
18
 
18
- VERSION = '1.0.1'
19
+ VERSION = '1.2.2'
19
20
  end
20
21
 
21
22
  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
data/lib/flor/flor.rb CHANGED
@@ -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
 
@@ -323,6 +329,11 @@ module Flor
323
329
  isostamp(false, true, true, t)
324
330
  end
325
331
 
332
+ def monow
333
+
334
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
335
+ end
336
+
326
337
  # def to_time(ts)
327
338
  #
328
339
  # m = ts.match(/\A(\d{4})(\d{2})(\d{2})\.(\d{2})(\d{2})(\d{2})(\d+)([uU]?)\z/)
@@ -367,11 +378,18 @@ module Flor
367
378
  sub_domain?(dom, sub)
368
379
  end
369
380
 
381
+ def dot_join(*elts)
382
+
383
+ elts.collect(&:to_s).select { |e| e.length > 0 }.join('.')
384
+ end
385
+
370
386
  def sub_domain?(dom, sub)
371
387
 
372
- dom == '' ||
373
- sub == dom ||
374
- sub[0, dom.length + 1] == dom + '.'
388
+ d = dom.is_a?(Array) ? dot_join(*dom) : dom.to_s
389
+
390
+ d == '' ||
391
+ sub == d ||
392
+ sub[0, d.length + 1] == d + '.'
375
393
  end
376
394
  alias subdomain? sub_domain?
377
395
 
data/lib/flor/log.rb CHANGED
@@ -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
@@ -333,18 +334,22 @@ module Flor
333
334
  n = executor.execution['nodes'][nid]
334
335
  node = n ? Flor::Node.new(executor, n, m) : nil
335
336
 
336
- o.puts "#{_c.dg}<Flor.msg_to_detail_s>#{_c.rs}#{_c.yl}"
337
- o.puts Flor.to_pretty_s(m)
338
- o.puts "#{_c.dg}payload:#{_c.yl}"
339
- o.puts Flor.to_pretty_s(m['payload'], 0)
340
- o.puts "#{_c.dg}tree:"
337
+ o.puts "#{_c.rs}#{_c.dg}<Flor.msg_to_detail_s>"
338
+
339
+ o.puts "#{_c.dg}message:#{_c.yl}"
340
+ o.puts YAML.dump(m)
341
+
342
+ o.puts "#{_c.dg}tree:#{_c.yl}"
341
343
  o.puts(tree_to_s(node.lookup_tree(nid), nid, out: o)) if node
344
+
342
345
  o.puts "#{_c.dg}node:#{_c.yl}"
343
- o.puts(Flor.to_pretty_s(n)) if n
344
- o.puts "#{_c.dg}nodes:"
346
+ o.puts YAML.dump(n.merge('tree' => '(above)'))
347
+
348
+ o.puts "#{_c.dg}nodes:#{_c.yl}"
345
349
  o.puts nods_to_s(executor, m, opts)
346
350
  z = executor.execution['nodes'].size
347
351
  o.puts "#{_c.yl}#{z} node#{z == 1 ? '' : 's'}."
352
+
348
353
  o.puts "#{_c.dg}</Flor.msg_to_detail_s>#{_c.rs}"
349
354
 
350
355
  o.string
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'flor/punit/m_ram'
3
+ require 'flor/punit/m_receive_and_merge'
4
4
 
5
5
 
6
6
  # Parent class for "c-for-each" and "c-map"
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'flor/punit/m_ram'
3
+ require 'flor/punit/m_receive_and_merge'
4
4
 
5
5
 
6
6
  class Flor::Pro::Concurrence < Flor::Procedure
@@ -387,10 +387,11 @@ class Flor::Pro::Concurrence < Flor::Procedure
387
387
 
388
388
  REWRITE_AS_ATTS = %w[
389
389
  on_receive on_merge
390
- child_on_error children_on_error ]
391
- #
392
- # heads of the child nodes that should get rewritten as attributes
393
- # of the concurrence ...
390
+ child_on_error children_on_error
391
+ ].freeze
392
+ #
393
+ # heads of the child nodes that should get rewritten as attributes
394
+ # of the concurrence ...
394
395
 
395
396
  def pre_execute_rewrite
396
397
 
File without changes
@@ -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')
data/lib/flor/unit.rb CHANGED
@@ -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)
@@ -23,7 +23,7 @@ module Flor
23
23
  @exitstatus = exitstatus
24
24
  end
25
25
 
26
- def pid; @process.pid; end
26
+ def pid; @process.pid; rescue; nil; end
27
27
  end
28
28
 
29
29
  def spawn(conf, data)
@@ -41,7 +41,7 @@ module Flor
41
41
  henv.each { |k, v| builder.environment.put(k, v) }
42
42
 
43
43
  process = builder.start
44
- pid = process.pid
44
+ pid = process.respond_to?(:pid) ? process.pid : nil
45
45
 
46
46
  o = process.outputStream.to_io
47
47
  i = process.inputStream.to_io
@@ -61,10 +61,12 @@ module Flor
61
61
 
62
62
  rescue => err
63
63
 
64
- Process.detach(pid) \
65
- if pid
66
- (Process.kill(9, pid) rescue nil) \
67
- unless Flor.no?(conf['on_error_kill'])
64
+ if pid
65
+ Process.detach(pid)
66
+ (Process.kill(9, pid) rescue nil) unless Flor.no?(conf['on_error_kill'])
67
+ else
68
+ process.destroy rescue nil
69
+ end
68
70
 
69
71
  raise err if err.is_a?(SpawnError)
70
72
  raise WrappedSpawnError.new(conf, { to: to, t0: t0, pid: pid }, err)
@@ -72,7 +74,6 @@ module Flor
72
74
  ensure
73
75
 
74
76
  [ i, o, f ].each { |x| x.close rescue nil }
75
-
76
77
  end
77
78
 
78
79
  module CmdParser include Raabro
@@ -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
 
@@ -30,7 +30,7 @@ module Flor
30
30
  #end
31
31
 
32
32
  # If the pointer is a "var" pointer, returns the full value
33
- # for the variable, as fund in the execution's node "0".
33
+ # for the variable, as found in the execution's node "0".
34
34
  #
35
35
  def full_value
36
36
 
@@ -38,6 +38,25 @@ module Flor
38
38
 
39
39
  node['vars'][name]
40
40
  end
41
+
42
+ def attd
43
+
44
+ data['atts'].inject({}) { |h, (k, v)| h[k] = v if k; h }
45
+
46
+ rescue; []
47
+ end
48
+
49
+ def attl
50
+
51
+ data['atts'].inject([]) { |a, (k, v)| a << v if k == nil; a }
52
+
53
+ rescue; []
54
+ end
55
+
56
+ def att_texts
57
+
58
+ attl.select { |e| e.is_a?(String) }
59
+ end
41
60
  end
42
61
  end
43
62
 
@@ -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,15 @@ 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
+ :content
17
+ ].freeze
18
+
10
19
  attr_reader :unit, :db, :models
11
20
 
12
21
  attr_reader :mutex
@@ -352,8 +361,7 @@ module Flor
352
361
 
353
362
  @db[:flor_messages]
354
363
  .import(
355
- [ :domain, :exid, :point, :content,
356
- :status, :ctime, :mtime, :cunit, :munit ],
364
+ MESSAGE_COLUMNS,
357
365
  unstored.map { |m|
358
366
  [ Flor.domain(m['exid']), m['exid'], m['point'], to_blob(m),
359
367
  'created', n, n, u, u ] }) \
@@ -517,25 +525,6 @@ module Flor
517
525
  end
518
526
  end
519
527
 
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
528
  def fetch_next_time
540
529
 
541
530
  t =
@@ -598,14 +587,13 @@ module Flor
598
587
 
599
588
  @db[:flor_messages]
600
589
  .where(
601
- id: messages.collect { |m| m['mid'] }.compact)
590
+ id: messages.collect { |m| m['mid'] }.uniq.compact)
602
591
  .update(
603
592
  status: 'consumed', mtime: n, munit: u)
604
593
 
605
594
  @db[:flor_messages]
606
595
  .import(
607
- [ :domain, :exid, :point, :content,
608
- :status, :ctime, :mtime, :cunit, :munit ],
596
+ MESSAGE_COLUMNS,
609
597
  messages
610
598
  .select { |m|
611
599
  ! m['mid'] && POINTS_TO_ARCHIVE.include?(m['point']) }
@@ -621,19 +609,16 @@ module Flor
621
609
 
622
610
  @db[:flor_messages]
623
611
  .where(
624
- id: messages.collect { |m| m['mid'] }.compact)
612
+ id: messages.collect { |m| m['mid'] }.uniq.compact)
625
613
  .delete
626
614
  end
627
615
  end
628
616
 
629
617
  def load_timers
630
618
 
631
- now = Flor.tstamp
632
- no = now[0, now.rindex('.')]
633
-
634
619
  timers
635
620
  .where(status: 'active')
636
- .where { ntime <= no }
621
+ .where { ntime <= Flor.tstam }
637
622
  .order(:ntime)
638
623
  .all
639
624
 
@@ -703,9 +688,9 @@ module Flor
703
688
 
704
689
  def update_pointers(exe, status, now)
705
690
 
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...
691
+ # Q Should we archive old pointers?
692
+ # A Well, it might be better to only archive the execution and leave
693
+ # in there enough information...
709
694
 
710
695
  exid = exe['exid']
711
696
 
@@ -731,23 +716,25 @@ module Flor
731
716
 
732
717
  ts = node['tags']
733
718
  ts.each { |t|
734
- a << [ dom, exid, nid, 'tag', t, nil, now, u ] } if ts
719
+ a << [ dom, exid, nid, 'tag', t, nil, now, u, nil ] } if ts
735
720
 
736
721
  vs = nid == '0' ? node['vars'] : nil
737
722
  vs.each { |k, v|
738
723
  case v; when Numeric, String, TrueClass, FalseClass, NilClass
739
- a << [ dom, exid, '0', 'var', k, v.to_s, now, u ]
724
+ a << [ dom, exid, '0', 'var', k, v.to_s, now, u, v ]
740
725
  when Array, Hash
741
726
  s = '(array)'; s = '(object)' if v.is_a?(Hash)
742
- a << [ dom, exid, '0', 'var', k, s, now, u ]
727
+ a << [ dom, exid, '0', 'var', k, s, now, u, v ]
743
728
  else
744
- a << [ dom, exid, '0', 'var', k, nil, now, u ]
729
+ a << [ dom, exid, '0', 'var', k, nil, now, u, v ]
745
730
  end } if vs
746
731
 
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
732
+ if ta = node['task']
733
+ tasker = ta['tasker']
734
+ n = ta['name']; name = n.is_a?(String) ? n : JSON.dump(n)
735
+ content = { message: node['message'], atts: node['atts'] }
736
+ a << [ dom, exid, nid, 'tasker', tasker, name, now, u, content ]
737
+ end
751
738
 
752
739
  a }
753
740
 
@@ -755,17 +742,36 @@ module Flor
755
742
  .where(exid: exid)
756
743
  .select(:nid, :type, :name)
757
744
  .all
758
- pointers.reject! { |_, _, ni, ty, na, _, _, _|
745
+ pointers.reject! { |_, _, ni, ty, na, _, _, _, _|
759
746
  cps.find { |cp| cp[:nid] == ni && cp[:type] == ty && cp[:name] == na } }
760
747
  #
761
748
  # don't insert when already inserted
762
749
 
750
+ #if pointer_columns.include?(:content)
751
+ pointers.each { |ptr| c = ptr[8]; ptr[8] = to_blob(c) if c }
752
+ #else
753
+ # pointers.each { |ptr| ptr.pop }
754
+ #end
755
+
756
+ #@db[:flor_pointers]
757
+ # .import(
758
+ # pointer_columns,
759
+ # pointers)
763
760
  @db[:flor_pointers]
764
761
  .import(
765
- [ :domain, :exid, :nid, :type, :name, :value, :ctime, :cunit ],
762
+ POINTER_COLUMNS,
766
763
  pointers)
767
764
  end
768
765
 
766
+ #def pointer_columns
767
+ # @pointer_columns ||=
768
+ # if @db[:flor_pointers].columns.include?(:content)
769
+ # POINTER_COLUMNS + [ :content ]
770
+ # else
771
+ # POINTER_COLUMNS
772
+ # end
773
+ #end
774
+
769
775
  def determine_type_and_schedule(message)
770
776
 
771
777
  t, s = message['type'], message['string']
@@ -845,8 +851,11 @@ module Flor
845
851
  # NB: -1 means "check at every use"
846
852
  end
847
853
 
848
- @db_logger = DbLogger.new(@unit)
849
- @db.loggers << @db_logger
854
+ if @unit.conf['sto_db_logger'] != false
855
+
856
+ @db_logger = DbLogger.new(@unit)
857
+ @db.loggers << @db_logger
858
+ end
850
859
  end
851
860
 
852
861
  class << self
@@ -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.2.2
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-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: munemo
@@ -237,7 +237,7 @@ files:
237
237
  - lib/flor/punit/do_trap.rb
238
238
  - lib/flor/punit/every.rb
239
239
  - lib/flor/punit/graft.rb
240
- - lib/flor/punit/m_ram.rb
240
+ - lib/flor/punit/m_receive_and_merge.rb
241
241
  - lib/flor/punit/on_timeout.rb
242
242
  - lib/flor/punit/part.rb
243
243
  - lib/flor/punit/schedule.rb
@@ -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