flor 1.0.1 → 1.2.2

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: 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