flor 1.2.2 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 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