flor 1.2.2 → 1.5.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: f1dae0857de545986e18ad31a41f37a6ead6687b34d3c06de22aafa606369283
4
- data.tar.gz: 9843d0124a2c3948ffc6846e7854854f5eda37d95bd02bfc02cecfefab0e5ea1
3
+ metadata.gz: 352d4347077550a9e577689e8b1f11ad90b7efb0d6fa14e99a228b634852c55f
4
+ data.tar.gz: 81015d54353ff68b48108e09fdfc8933cae2b20ed48ccba1679bebc1b67258da
5
5
  SHA512:
6
- metadata.gz: 185490e0054ffdfe7c078b71955834679a398266e613be5d8bb6b63bc626bf4727bd4fd45249e7dea14cdd91a85c5ad92a1f6b3b120117c7c274b41d5478ba31
7
- data.tar.gz: e6dde7ae40f3ef6d2c7a620d6a07840c6591b745798962d039ac39fa426e4bcef51f3c67e97d9a367bb981918ec5fe98934393eec2cd7e87046b8d491b9a6511
6
+ metadata.gz: c322beec0f868fbf72c79678227cbf2a2fea34611fe340b1fdd680a7d6b1f5a09246af1a78f6923977a061772ef22fc8d20d2d13a0bad2145991f296b4cfd143
7
+ data.tar.gz: 3c3e2ce6a88acbaeeab0bb9d89f8c22e5dfd8fb1a81491f0d54983d21d692e3cd93262c842ed8dbc00e021b2fee1d5f3e3e791d8af72bfc6667c472d712d3541
data/CHANGELOG.md CHANGED
@@ -2,6 +2,28 @@
2
2
  # CHANGELOG.md
3
3
 
4
4
 
5
+ ## flor 1.5.0 released 2021-11-24
6
+
7
+ * Add storage callbacks `on(:pointers, :any) { do_that }`
8
+ * Add `on_receive` (and `on receive`)
9
+
10
+
11
+ ## flor 1.4.0 released 2021-11-10
12
+
13
+ * Add :tree to Execution#to_h
14
+ * Scaffold bin/flotojson
15
+
16
+
17
+ ## flor 1.3.1 released 2021-04-19
18
+
19
+ * Fix flor_pointers var deletion mechanism (type = ' var ')
20
+
21
+
22
+ ## flor 1.3.0 released 2021-04-13
23
+
24
+ * Insert a row flor_pointers for 'failure'
25
+
26
+
5
27
  ## flor 1.2.2 released 2021-03-29
6
28
 
7
29
  * Include data in flor_pointers
@@ -88,7 +88,7 @@ class Flor::Node
88
88
  def domain; Flor.domain(@execution['exid']); end
89
89
 
90
90
  def point; @message['point']; end
91
- def from; @message['from']; end
91
+ def from; @from || @message['from']; end
92
92
 
93
93
  def cnodes; @node['cnodes']; end
94
94
  def cnodes_any?; cnodes && cnodes.any?; end
@@ -149,7 +149,7 @@ class Flor::Node
149
149
  tree = lookup_tree(Flor.parent_nid(nid))
150
150
  return tree[1][cid] if tree
151
151
 
152
- #tree = lookup_tree(Flor.parent_nid(nid, true))
152
+ #tree = lookup_tree(Flor.parent_nid(nid, remove_subnid=true))
153
153
  #return tree[1][cid] if tree
154
154
  #
155
155
  # might become necessary at some point
@@ -5,7 +5,7 @@ class Flor::Procedure < Flor::Node
5
5
  # "Returning vars" variables to pass back to pass upon reply.
6
6
  # In the 'receive' messages, it's a hash under the key 'rvars'.
7
7
  #
8
- RVARS = %w[ idx ]
8
+ RVARS = %w[ idx ].freeze
9
9
 
10
10
  # Attributes that when given alone are turned to "true" attributes.
11
11
  #
@@ -13,7 +13,7 @@ class Flor::Procedure < Flor::Node
13
13
  #
14
14
  # The transformation occurs in Flor::Pro::Att ("_att").
15
15
  #
16
- TRUE_ATTS = %w[ flank off disabled ]
16
+ TRUE_ATTS = %w[ flank off disabled ].freeze
17
17
 
18
18
  class << self
19
19
 
@@ -381,8 +381,17 @@ class Flor::Procedure < Flor::Node
381
381
 
382
382
  receive_when_ended
383
383
 
384
+ elsif should_apply_on_receive?
385
+
386
+ apply_on_receive
387
+
384
388
  else
385
389
 
390
+ orn = @node['on_receive_nid']
391
+ @from = orn[1] if orn && orn[0] == from
392
+ #
393
+ # in order to move on to the next child...
394
+
386
395
  receive
387
396
  end
388
397
  end
@@ -550,6 +559,8 @@ class Flor::Procedure < Flor::Node
550
559
  wrap_reply
551
560
  end
552
561
 
562
+ IF_UNLESS = %w[ _if _unless ].freeze
563
+
553
564
  # Grab on_error proc from incoming payload and stores it into parent node.
554
565
  #
555
566
  # Has no effect if there is no parent node.
@@ -562,7 +573,7 @@ class Flor::Procedure < Flor::Node
562
573
  @node; loop do
563
574
  pnode = parent_node(pnode)
564
575
  return unless pnode
565
- break unless %w[ _if _unless ].include?(pnode['heap'])
576
+ break unless IF_UNLESS.include?(pnode['heap'])
566
577
  end
567
578
 
568
579
  flavour = "on_#{key}"
@@ -598,6 +609,8 @@ class Flor::Procedure < Flor::Node
598
609
  wrap('point' => 'entered', 'nid' => nid, 'tags' => ret)
599
610
  end
600
611
 
612
+ WRAP_KEYS = %w[ error cancel timeout ].freeze
613
+
601
614
  def wrap(h={})
602
615
 
603
616
  m = {}
@@ -642,7 +655,7 @@ class Flor::Procedure < Flor::Node
642
655
  # was considering passing the whole vars back (as 'varz'), but
643
656
  # it got in the way... and it might be heavy
644
657
 
645
- %w[ error cancel timeout ]
658
+ WRAP_KEYS
646
659
  .each { |k|
647
660
  co = @node["child_on_#{k}"]
648
661
  next unless co
@@ -978,6 +991,36 @@ class Flor::Procedure < Flor::Node
978
991
 
979
992
  []
980
993
  end
994
+
995
+ def should_apply_on_receive?
996
+
997
+ return false if @message['from_on'] == 'receive'
998
+ #
999
+ # no, since the message comes from an on_receive...
1000
+ # how about nested on_receives?
1001
+
1002
+ orc = @node['on_receive']
1003
+ return false if orc.nil? || orc.empty?
1004
+
1005
+ orn = @node['on_receive_nid']
1006
+ return false if orn && orn[0] == from
1007
+
1008
+ true
1009
+ end
1010
+
1011
+ def apply_on_receive
1012
+
1013
+ determine_fcid_and_ncid
1014
+
1015
+ args = [
1016
+ [ 'msg', @message ],
1017
+ [ 'fcid', @fcid ] ]
1018
+
1019
+ ms = apply(@node['on_receive'][0][1], args, tree[2])
1020
+ @node['on_receive_nid'] = [ ms[0]['nid'], from ]
1021
+
1022
+ ms
1023
+ end
981
1024
  end
982
1025
 
983
1026
 
data/lib/flor/log.rb CHANGED
@@ -343,7 +343,7 @@ module Flor
343
343
  o.puts(tree_to_s(node.lookup_tree(nid), nid, out: o)) if node
344
344
 
345
345
  o.puts "#{_c.dg}node:#{_c.yl}"
346
- o.puts YAML.dump(n.merge('tree' => '(above)'))
346
+ o.puts n ? YAML.dump(n.merge('tree' => '(above)')) : 'nil'
347
347
 
348
348
  o.puts "#{_c.dg}nodes:#{_c.yl}"
349
349
  o.puts nods_to_s(executor, m, opts)
data/lib/flor/parser.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  module Flor
3
4
 
4
5
  def self.parse(input, fname=nil, opts={})
@@ -727,3 +728,4 @@ fail "don't know how to invert #{operation.inspect}" # FIXME
727
728
  sio.string
728
729
  end
729
730
  end
731
+
@@ -99,7 +99,7 @@ class Flor::Pro::UnderscoreApply < Flor::Procedure
99
99
 
100
100
  params.each do |param_key, param_tree|
101
101
  next if param_tree[0] == '_ref'
102
- arg_i = args.index { |arg_key, arg_val| arg_key == param_key }
102
+ arg_i = args.index { |arg_key, _| arg_key == param_key }
103
103
  next unless arg_i
104
104
  arg_key, arg_val = args.delete_at(arg_i)
105
105
  seen << arg_key
data/lib/flor/pcore/on.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+
3
4
  class Flor::Pro::On < Flor::Macro
4
5
  #
5
6
  # Catches signals or errors.
@@ -132,7 +133,7 @@ class Flor::Pro::On < Flor::Macro
132
133
 
133
134
  protected
134
135
 
135
- CATCHES = %w[ error cancel timeout ].freeze
136
+ CATCHES = %w[ error cancel timeout receive ].freeze
136
137
 
137
138
  def find_catch
138
139
 
@@ -0,0 +1,102 @@
1
+ # frozen_string_literal: true
2
+
3
+
4
+ class Flor::Pro::OnReceive < Flor::Procedure
5
+ #
6
+ # Binds a function to the parent node, the function will be called each time
7
+ # the parent node "receives".
8
+ #
9
+ # ```
10
+ # set l []
11
+ # sequence
12
+ # on_receive (def msg \ push l 'a')
13
+ # push l 0
14
+ # push l 1
15
+ # push l 2
16
+ # ```
17
+ # will result in the variable `l` holding `[ 0, 'a', 1, 'a', 2, 'a' ]`.
18
+ #
19
+ # ```
20
+ # set l []
21
+ # sequence
22
+ # push l 0
23
+ # on_receive (def msg \ push l 'a')
24
+ # push l 1
25
+ # push l 2
26
+ # ```
27
+ # will result in the variable `l` holding `[ 0, 1, 'a', 2, 'a' ]`.
28
+ #
29
+ # It's meant to play well with a cursor:
30
+ # ```
31
+ # set l []
32
+ # cursor
33
+ # on_receive (def \ break _ if l[-1] == 1)
34
+ # push l 0
35
+ # push l 1
36
+ # push l 2
37
+ # push l 'z'
38
+ # ```
39
+ # will result in the variable `l` holding `[ 0, 1, 'z' ]`.
40
+ #
41
+ #
42
+ # ## arguments to the on_receive function
43
+ #
44
+ # `msg` and `fcid` (from child id) are passed to the function
45
+ # ```
46
+ # set l []
47
+ # cursor
48
+ # on_receive (def msg, fcid \ push l [ msg.from, fcid ])
49
+ # push l 0
50
+ # push l 1
51
+ # push l 2
52
+ # ```
53
+ # will result in the variable `l` holding
54
+ # `[ 0, [ '0_1_1', 1 ], 1, [ '0_1_2', 2 ], 2, [ '0_1_3', 3 ] ]`.
55
+ #
56
+ #
57
+ # ## on_receive and on receive
58
+ #
59
+ # A "lighter" notation is available (it's translated automatically to a
60
+ # `on_receive`):
61
+ # ```
62
+ # set l []
63
+ # cursor
64
+ # on receive
65
+ # push l $msg.from
66
+ # break _ if l.-1 == 1
67
+ # push l 0
68
+ # push l 1
69
+ # push l 2
70
+ # ```
71
+ #
72
+ # Please note the `$msg` variable made available to the `on receive` block.
73
+ #
74
+ #
75
+ # ## concurrence and on_receive
76
+ #
77
+ # Please not that `concurrence` has its own `on_receive` with a slightly
78
+ # different behaviour.
79
+
80
+ name 'on_receive'
81
+
82
+ def pre_execute
83
+
84
+ unatt_unkeyed_children
85
+
86
+ @node['rets'] = []
87
+ end
88
+
89
+ def receive_last
90
+
91
+ prc = @node['rets'].find { |r| Flor.is_func_tree?(r) }
92
+
93
+ store_on(:receive, prc)
94
+
95
+ ms = super
96
+
97
+ ms.first['from_on'] = 'receive'
98
+
99
+ ms
100
+ end
101
+ end
102
+
@@ -139,7 +139,7 @@ class Flor::Pro::Concurrence < Flor::Procedure
139
139
  # + 12 34
140
140
  # + 56 78
141
141
  # ```
142
- # One can even express the function has a 'block':
142
+ # One can even express the function as a 'block':
143
143
  # ```
144
144
  # concurrence tag: 'x'
145
145
  # on_receive
@@ -0,0 +1,90 @@
1
+ # frozen_string_literal: true
2
+
3
+ # flotojson.rb
4
+
5
+ require 'flor'
6
+
7
+ FLAGS_WITH_VALUE = []
8
+
9
+ flags = {}
10
+ files = []
11
+
12
+ if (ARGV & [ '-h', '--help']).any?
13
+ puts
14
+ puts "bin/flotojson [flags] filename"
15
+ puts
16
+ puts " turns a flor .flo process definition to its tree representation"
17
+ puts
18
+ puts " flags:"
19
+ puts " --pp pretty prints instead of dumping as JSON"
20
+ puts
21
+ puts " --pj"
22
+ puts " --jp pretty prints the JSON output"
23
+ puts
24
+ puts " -y"
25
+ puts " -yaml dumps as YAML"
26
+ puts
27
+ puts " -h"
28
+ puts " --help prints this help message"
29
+ puts
30
+ exit 0
31
+ end
32
+
33
+ args = ARGV.dup
34
+
35
+ loop do
36
+
37
+ a = args.shift; break unless a
38
+
39
+ if a.size > 1 && a[0, 1] == '-'
40
+ flags[a] = FLAGS_WITH_VALUE.include?(a) ? a.shift : true
41
+ else
42
+ files << a
43
+ end
44
+ end
45
+
46
+ #STDERR.puts flags.inspect
47
+ #STDERR.puts files.inspect
48
+
49
+ # t =
50
+ # tree.is_a?(String) ?
51
+ # Flor.parse(tree, opts[:fname] || opts[:path], opts) :
52
+ # tree
53
+ #
54
+ # unless t
55
+ #
56
+ # #h = opts.merge(prune: false, rewrite: false, debug: 0)
57
+ # #Raabro.pp(Flor.parse(tree, h[:fname], h))
58
+ # # TODO re-parse and indicate what went wrong...
59
+ #
60
+ # fail ArgumentError.new(
61
+ # "flow parsing failed: " + tree.inspect[0, 35] + '...')
62
+ # end
63
+
64
+ fname = files.first
65
+
66
+ content =
67
+ if fname
68
+ abort("File #{fname.inspect} not found") unless File.exist?(fname)
69
+ File.read(fname)
70
+ else
71
+ STDIN.read
72
+ end
73
+
74
+ tree = Flor.parse(content, fname, {})
75
+
76
+ if flags['--pp']
77
+ pp tree
78
+ elsif flags['--jp'] || flags['--pj']
79
+ puts(
80
+ JSON.pretty_generate(tree)
81
+ .gsub(/\[\s+/, '[ ')
82
+ .gsub(/\]\s+/, ']')
83
+ .gsub(/,\s+(\d+)\s+\]/, ', \1]')
84
+ )
85
+ elsif flags['-y'] || flags['--yaml']
86
+ puts YAML.dump(tree)
87
+ else
88
+ puts JSON.dump(tree)
89
+ end
90
+
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+
3
4
  module Flor
4
5
 
5
6
  # TODO ::Logger has a formatting callback
@@ -11,7 +12,7 @@ module Flor
11
12
 
12
13
  # NB: logger configuration entries start with "log_"
13
14
 
14
- LEVELS_I = %w[ DEBUG INFO WARN ERROR FATAL UNKNOWN ]
15
+ LEVELS_I = %w[ DEBUG INFO WARN ERROR FATAL UNKNOWN ].freeze
15
16
 
16
17
  def initialize(unit)
17
18
 
@@ -20,7 +20,29 @@ module Flor
20
20
  #end
21
21
 
22
22
  def nodes; data['nodes']; end
23
+
23
24
  def zero_node; nodes['0']; end
25
+
26
+ # Returns the nids, the lower in the tree, the earlier in the returned
27
+ # array.
28
+ #
29
+ def sorted_nids
30
+
31
+ nodes.keys
32
+ .inject([]) { |a, nid|
33
+ l = nid.split('_').length
34
+ (a[l] ||= []) << nid
35
+ a }
36
+ .compact
37
+ .collect(&:sort)
38
+ .flatten(1)
39
+ end
40
+
41
+ def lowest_node
42
+
43
+ nodes[sorted_nids.first]
44
+ end
45
+
24
46
  def closing_messages; data['closing_messages']; end
25
47
 
26
48
  def execution(reload=false); self; end
@@ -47,14 +69,16 @@ module Flor
47
69
 
48
70
  def full_tree
49
71
 
50
- tree = nodes['0']['tree']
72
+ nids = sorted_nids
73
+ nid0 = nids.shift
51
74
 
52
- #nodes.each do |nid, n|
53
- # next if nid == '0'
54
- # t = n['tree']; next unless t
55
- #end
56
- #
57
- # FIXME
75
+ return nil unless nid0
76
+
77
+ tree = Flor.dup(nodes[nid0]['tree'])
78
+
79
+ nids.each { |nid|
80
+ next unless nid.split('_', 2).first == nid0
81
+ replace_sub_tree(tree, nid, nodes[nid]['tree']) }
58
82
 
59
83
  tree
60
84
  end
@@ -80,21 +104,22 @@ module Flor
80
104
  cs = m[:counts] = {}
81
105
  is = m[:nids] = { tasks: [], failures: [] }
82
106
 
83
- fs = 0
84
- ts = 0
107
+ cs[:failures] = 0
108
+ cs[:tasks] = 0
109
+ cs[:nodes] = nodes.count
110
+ #
85
111
  nodes.each do |k, v|
86
112
  if v['task']
87
- ts += 1
113
+ cs[:tasks] += 1
88
114
  is[:tasks] << k
89
115
  end
90
116
  if v['failure']
91
- fs += 1
117
+ cs[:failures] += 1
92
118
  is[:failures] << k
93
119
  end
94
120
  end
95
- cs[:nodes] = nodes.count
96
- cs[:failures] = fs
97
- cs[:tasks] = ts
121
+
122
+ h[:tree] = full_tree
98
123
 
99
124
  h
100
125
  end
@@ -149,6 +174,30 @@ module Flor
149
174
  lookup_node(query, opts)['nid']
150
175
  end
151
176
 
177
+ protected
178
+
179
+ def replace_sub_tree(tree, nid, t)
180
+
181
+ return unless t
182
+ return if nid.index('-') # stay vanilla
183
+
184
+ snid = nid.split('_').collect(&:to_i)[1..-1]
185
+ a = get_child_array(tree, snid)
186
+
187
+ return unless a # shouldn't we fail?
188
+
189
+ a[snid.first] = Flor.dup(t)
190
+ end
191
+
192
+ def get_child_array(tree, snid)
193
+
194
+ return nil if tree.nil?
195
+ return nil if snid.length < 1
196
+ return nil unless tree[1].is_a?(Array)
197
+ return tree[1] if snid.length == 1
198
+ n = snid.shift; get_child_array(tree[1][n], snid)
199
+ end
200
+
152
201
  class << self
153
202
 
154
203
  def by_status(s)
@@ -113,27 +113,6 @@ module Flor
113
113
  @ganger.shutdown
114
114
  end
115
115
 
116
- def on_start_exc(e)
117
-
118
- io = StringIO.new
119
-
120
- head, kind =
121
- e.is_a?(StandardError) ? [ '=sch', 'error' ] : [ '!sch', 'exception' ]
122
- thr = Thread.current
123
-
124
- t = head[0, 2] + Time.now.to_f.to_s.split('.').last
125
- io.puts ' /' + t + ' ' + head * 17
126
- io.puts " |#{t} + in #{self.class}#start"
127
- io.puts " |#{t} db: #{@storage.db.class} #{@storage.db.object_id}"
128
- io.puts " |#{t} thread: t#{thr.object_id} #{thr.inspect}"
129
- io.puts " |#{t} #{kind}: #{e.inspect}"
130
- io.puts " |#{t} backtrace:"
131
- e.backtrace.each { |l| io.puts "|#{t} #{l}" }
132
- io.puts ' \\' + t + ' ' + (head * 17) + ' .'
133
-
134
- io.string
135
- end
136
-
137
116
  def start
138
117
 
139
118
  # TODO heartbeat, every x minutes, when idle, log something
@@ -242,18 +221,18 @@ module Flor
242
221
  end
243
222
  end
244
223
 
224
+ RETURN_KEYS = %w[ exid nid payload tasker cause ].freeze
225
+
245
226
  def return(message)
246
227
 
247
- m =
228
+ queue(
248
229
  if message['point'] == 'failed'
249
230
  message
250
231
  else
251
232
  message
252
- .select { |k, _| %w[ exid nid payload tasker cause ].include?(k) }
233
+ .select { |k, _| RETURN_KEYS.include?(k) }
253
234
  .merge!('point' => 'return')
254
- end
255
-
256
- queue(m)
235
+ end)
257
236
 
258
237
  nil
259
238
  end
@@ -432,7 +411,7 @@ module Flor
432
411
  ex ? ex.execution : nil
433
412
  end
434
413
 
435
- DUMP_KEYS = %w[ timestamp executions timers traps pointers ]
414
+ DUMP_KEYS = %w[ timestamp executions timers traps pointers ].freeze
436
415
 
437
416
  # Dumps all or some of the executions to a JSON string.
438
417
  # See Scheduler#load for importing.
@@ -557,6 +536,27 @@ module Flor
557
536
 
558
537
  protected
559
538
 
539
+ def on_start_exc(e)
540
+
541
+ io = StringIO.new
542
+
543
+ head, kind =
544
+ e.is_a?(StandardError) ? [ '=sch', 'error' ] : [ '!sch', 'exception' ]
545
+ thr = Thread.current
546
+
547
+ t = head[0, 2] + Time.now.to_f.to_s.split('.').last
548
+ io.puts ' /' + t + ' ' + head * 17
549
+ io.puts " |#{t} + in #{self.class}#start"
550
+ io.puts " |#{t} db: #{@storage.db.class} #{@storage.db.object_id}"
551
+ io.puts " |#{t} thread: t#{thr.object_id} #{thr.inspect}"
552
+ io.puts " |#{t} #{kind}: #{e.inspect}"
553
+ io.puts " |#{t} backtrace:"
554
+ e.backtrace.each { |l| io.puts "|#{t} #{l}" }
555
+ io.puts ' \\' + t + ' ' + (head * 17) + ' .'
556
+
557
+ io.string
558
+ end
559
+
560
560
  def extract_dump_and_load_filters(opts)
561
561
 
562
562
  o = lambda { |k| v = opts[k] || opts["#{k}s".to_sym]; v ? Array(v) : nil }
@@ -598,6 +598,8 @@ module Flor
598
598
  puts(on_start_exc(ex))
599
599
  end
600
600
 
601
+ PREP_KEYS = %w[ exid name nid payload on_receive_last ].freeze
602
+
601
603
  def prepare_message(point, args)
602
604
 
603
605
  h = args
@@ -612,7 +614,7 @@ module Flor
612
614
  opts = {}
613
615
 
614
616
  h.each do |k, v|
615
- if %w[ exid name nid payload on_receive_last ].include?(k)
617
+ if PREP_KEYS.include?(k)
616
618
  msg[k] = v
617
619
  else
618
620
  opts[k.to_sym] = v
@@ -16,7 +16,7 @@ module Flor
16
16
  :content
17
17
  ].freeze
18
18
 
19
- attr_reader :unit, :db, :models
19
+ attr_reader :unit, :db, :models, :callbacks
20
20
 
21
21
  attr_reader :mutex
22
22
  # might be useful for some implementations
@@ -31,6 +31,8 @@ module Flor
31
31
  @archive = @unit.conf['sto_archive']
32
32
  @mutex = @unit.conf['sto_sync'] ? Mutex.new : nil
33
33
 
34
+ @callbacks = {}
35
+
34
36
  connect
35
37
  end
36
38
 
@@ -191,6 +193,8 @@ module Flor
191
193
  mtime: now,
192
194
  munit: u)
193
195
 
196
+ callback(:executions, :update, id)
197
+
194
198
  else
195
199
 
196
200
  exe['id'] =
@@ -205,6 +209,8 @@ module Flor
205
209
  cunit: u,
206
210
  munit: u)
207
211
  .to_i
212
+
213
+ callback(:executions, :insert, exe['id'])
208
214
  end
209
215
 
210
216
  remove_nodes(exe, status, now)
@@ -220,6 +226,9 @@ module Flor
220
226
  raise err
221
227
  end
222
228
 
229
+ CRECON_STATUSES = %w[ created consumed ].freeze
230
+ RESCON_STATUSES = %w[ reserved consumed ].freeze
231
+
223
232
  def load_messages(exe_count)
224
233
 
225
234
  exe_count += 2
@@ -230,12 +239,12 @@ module Flor
230
239
  _exids_being_processed =
231
240
  @db[:flor_messages]
232
241
  .select(:exid)
233
- .exclude(status: %w[ created consumed ])
242
+ .exclude(status: CRECON_STATUSES)
234
243
  _exids =
235
244
  @db[:flor_messages]
236
245
  .select(:exid)
237
246
  .exclude(exid: _exids_being_processed)
238
- .exclude(status: %w[ reserved consumed ])
247
+ .exclude(status: RESCON_STATUSES)
239
248
  .limit(exe_count)
240
249
  @db[:flor_messages]
241
250
  .where(exid: _exids, status: 'created')
@@ -318,7 +327,7 @@ module Flor
318
327
  []
319
328
  end
320
329
 
321
- POINTS_TO_ARCHIVE = %w[ terminated failed ceased ]
330
+ POINTS_TO_ARCHIVE = %w[ terminated failed ceased ].freeze
322
331
 
323
332
  def consume(messages)
324
333
 
@@ -415,26 +424,29 @@ module Flor
415
424
  now = Flor.tstamp
416
425
  u = @unit.identifier
417
426
 
418
- synchronize do
427
+ id =
428
+ synchronize do
419
429
 
420
- @db[:flor_timers]
421
- .insert(
422
- domain: Flor.domain(message['exid']),
423
- exid: message['exid'],
424
- nid: message['nid'],
425
- onid: message['onid'] || message['nid'],
426
- bnid: message['nid'],
427
- type: type,
428
- schedule: string,
429
- ntime: next_time,
430
- content: to_blob(message),
431
- count: 0,
432
- status: 'active',
433
- ctime: now,
434
- mtime: now,
435
- cunit: u,
436
- munit: u)
437
- end
430
+ @db[:flor_timers]
431
+ .insert(
432
+ domain: Flor.domain(message['exid']),
433
+ exid: message['exid'],
434
+ nid: message['nid'],
435
+ onid: message['onid'] || message['nid'],
436
+ bnid: message['nid'],
437
+ type: type,
438
+ schedule: string,
439
+ ntime: next_time,
440
+ content: to_blob(message),
441
+ count: 0,
442
+ status: 'active',
443
+ ctime: now,
444
+ mtime: now,
445
+ cunit: u,
446
+ munit: u)
447
+ end
448
+
449
+ callback(:timers, :insert, id)
438
450
 
439
451
  @unit.wake_up
440
452
 
@@ -499,6 +511,8 @@ module Flor
499
511
  munit: u)
500
512
  end
501
513
 
514
+ callback(:traps, :insert, id)
515
+
502
516
  traps[id]
503
517
 
504
518
  rescue => err
@@ -545,6 +559,21 @@ module Flor
545
559
  nil
546
560
  end
547
561
 
562
+ def on(key, actions=[], &block)
563
+
564
+ as =
565
+ case actions
566
+ when :any, 'any' then []
567
+ when Array then actions
568
+ when Symbol then [ actions ]
569
+ when String then actions.split(/\s*[;,]\s*/)
570
+ else []
571
+ end
572
+ .collect(&:to_sym)
573
+
574
+ (@callbacks[key] ||= []) << [ as, block ]
575
+ end
576
+
548
577
  protected
549
578
 
550
579
  def migration_table_and_column(opts={})
@@ -637,10 +666,11 @@ module Flor
637
666
  def reschedule_timer(t)
638
667
 
639
668
  w = { id: t.id.to_i, status: 'active', mtime: t.mtime, munit: t.munit }
669
+ r = nil
640
670
 
641
671
  if t.type != 'at' && t.type != 'in'
642
672
 
643
- @db[:flor_timers]
673
+ r = @db[:flor_timers]
644
674
  .where(w)
645
675
  .update(
646
676
  count: t.count.to_i + 1,
@@ -649,9 +679,11 @@ module Flor
649
679
  mtime: Flor.tstamp,
650
680
  munit: @unit.identifier)
651
681
 
682
+ callback(:timers, :update, w, t)
683
+
652
684
  elsif @archive
653
685
 
654
- @db[:flor_timers]
686
+ r = @db[:flor_timers]
655
687
  .where(w)
656
688
  .update(
657
689
  count: t.count.to_i + 1,
@@ -659,12 +691,18 @@ module Flor
659
691
  mtime: Flor.tstamp,
660
692
  munit: @unit.identifier)
661
693
 
694
+ callback(:timers, :update, w, t)
695
+
662
696
  else
663
697
 
664
- @db[:flor_timers]
698
+ r = @db[:flor_timers]
665
699
  .where(w)
666
700
  .delete
701
+
702
+ callback(:timers, :delete, w, t)
667
703
  end
704
+
705
+ r
668
706
  end
669
707
 
670
708
  def remove_nodes(exe, status, now)
@@ -686,6 +724,8 @@ module Flor
686
724
  # done in update_pointers
687
725
  end
688
726
 
727
+ FP_TYPES = %w[ var ].freeze
728
+
689
729
  def update_pointers(exe, status, now)
690
730
 
691
731
  # Q Should we archive old pointers?
@@ -701,7 +741,7 @@ module Flor
701
741
 
702
742
  @db[:flor_pointers]
703
743
  .where(exid: exid)
704
- .where(Sequel.|({ type: %w[ var ] }, Sequel.~(nid: exe['nodes'].keys)))
744
+ .where(Sequel.|({ type: FP_TYPES }, Sequel.~(nid: exe['nodes'].keys)))
705
745
  .delete
706
746
  #
707
747
  # Delete all pointer to vars, their value might have changed,
@@ -714,10 +754,14 @@ module Flor
714
754
  pointers = exe['nodes']
715
755
  .inject([]) { |a, (nid, node)|
716
756
 
757
+ # add a pointer for each tag
758
+
717
759
  ts = node['tags']
718
760
  ts.each { |t|
719
761
  a << [ dom, exid, nid, 'tag', t, nil, now, u, nil ] } if ts
720
762
 
763
+ # add a pointer for each var (if nid == '0')
764
+
721
765
  vs = nid == '0' ? node['vars'] : nil
722
766
  vs.each { |k, v|
723
767
  case v; when Numeric, String, TrueClass, FalseClass, NilClass
@@ -729,6 +773,8 @@ module Flor
729
773
  a << [ dom, exid, '0', 'var', k, nil, now, u, v ]
730
774
  end } if vs
731
775
 
776
+ # add a pointer for the task if any
777
+
732
778
  if ta = node['task']
733
779
  tasker = ta['tasker']
734
780
  n = ta['name']; name = n.is_a?(String) ? n : JSON.dump(n)
@@ -736,6 +782,29 @@ module Flor
736
782
  a << [ dom, exid, nid, 'tasker', tasker, name, now, u, content ]
737
783
  end
738
784
 
785
+ # add a pointer for the error if any
786
+
787
+ if fa = node['failure']
788
+
789
+ #puts "-" * 80; pp node; puts "-" * 80
790
+ a <<
791
+ if er = fa['error']
792
+ ni = fa['from'] || nid # not nid /!\
793
+ nam = "#{er['kla']} l#{er['lin']}"
794
+ val = er['msg']
795
+ con = { error: fa, nid: ni }
796
+ [ dom, exid, ni, 'failure', nam, val, now, u, con ]
797
+ else
798
+ nam = fa['tasker'] || 'failure'
799
+ val = [ fa['attl'] || [], fa['attd'] || {} ]
800
+ .collect(&:inspect).join(' ')
801
+ con = { error: fa, nid: nid }
802
+ [ dom, exid, nid, 'failure', nam, val, now, u, con ]
803
+ end
804
+ end
805
+
806
+ # done
807
+
739
808
  a }
740
809
 
741
810
  cps = @db[:flor_pointers] # current pointers
@@ -747,20 +816,14 @@ module Flor
747
816
  #
748
817
  # don't insert when already inserted
749
818
 
750
- #if pointer_columns.include?(:content)
751
819
  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)
820
+
760
821
  @db[:flor_pointers]
761
822
  .import(
762
823
  POINTER_COLUMNS,
763
824
  pointers)
825
+
826
+ callback(:pointers, :update, exid)
764
827
  end
765
828
 
766
829
  #def pointer_columns
@@ -809,6 +872,23 @@ module Flor
809
872
  .inject([]) { |a, elt| a << [ a.last, elt ].compact.join('.'); a }
810
873
  end
811
874
 
875
+ def callback(table, action, *rest)
876
+
877
+ (@callbacks[table] || [])
878
+ .each { |as, block|
879
+ call_back(block, table, action, *rest) \
880
+ if as.empty? || as.include?(action) }
881
+ end
882
+
883
+ def call_back(block, table, action, *rest)
884
+
885
+ block.call(
886
+ *(
887
+ block.arity < 0 ?
888
+ [ table, action, *rest ] :
889
+ [ table, action, *rest ][0, block.arity]))
890
+ end
891
+
812
892
  class DbLogger
813
893
 
814
894
  def initialize(unit); @unit = unit; end
data/lib/flor.rb CHANGED
@@ -16,7 +16,7 @@ require 'dense'
16
16
 
17
17
  module Flor
18
18
 
19
- VERSION = '1.2.2'
19
+ VERSION = '1.5.0'
20
20
  end
21
21
 
22
22
  require 'flor/colours'
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.2.2
4
+ version: 1.5.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: 2021-03-29 00:00:00.000000000 Z
11
+ date: 2021-11-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: munemo
@@ -204,6 +204,7 @@ files:
204
204
  - lib/flor/pcore/on.rb
205
205
  - lib/flor/pcore/on_cancel.rb
206
206
  - lib/flor/pcore/on_error.rb
207
+ - lib/flor/pcore/on_receive.rb
207
208
  - lib/flor/pcore/push.rb
208
209
  - lib/flor/pcore/rand.rb
209
210
  - lib/flor/pcore/range.rb
@@ -249,6 +250,7 @@ files:
249
250
  - lib/flor/to_string.rb
250
251
  - lib/flor/tools/env.rb
251
252
  - lib/flor/tools/firb.rb
253
+ - lib/flor/tools/flotojson.rb
252
254
  - lib/flor/tools/shell.rb
253
255
  - lib/flor/tools/shell_out.rb
254
256
  - lib/flor/tt.rb