flor 1.4.0 → 1.6.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: 298a6b2c9d3752b561ee8933ae00ed7a3f63682fb758dbe899097c6f7f56c00d
4
- data.tar.gz: 3e2602737b9de7a452549d0f8ed34752618b1322a5508d178abe6559d549c045
3
+ metadata.gz: b28e7de7c950544c32279d8a63917911ae9d12be4154a1bcc0539c8ae343b6d6
4
+ data.tar.gz: 1838e6d66710983c5adce4dc19b799bed0a34bf5427ee8156c516749d740e703
5
5
  SHA512:
6
- metadata.gz: 28ae10c78676391537dde7cd7e0082338650c6aeba60fbcffa2ca5a4cb2dd2f168d7ff61019472c5f6f3d2216cabfe6e9bccb7cdae4856b513e0663f4d4ec362
7
- data.tar.gz: 6379738cddfc5b74d37a30fa23260924649866b49dc670f37a0540a47a5079062288851ce07306350e227d73d0fd873f2199c3ea01b3399bce61572b8cebe497
6
+ metadata.gz: f75fd74bd118784e7cf3285bff5f55d4e97c67f4666332712dc9b878c267690247468372ec3970d6cff570f90f235b31ae182279aec87f77d442d7cfa1c6536a
7
+ data.tar.gz: 8c4b251c6bd1cc514580aae6430dcc70489ca046940e8a9e23104260f48be0a5049ac70310bcecfa3fdb62d00a8e603c644e177ab1db080e38bea04c1c538fa7
data/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
  # CHANGELOG.md
3
3
 
4
4
 
5
+ ## flor 1.6.0 released 2023-01-13
6
+
7
+ * Add #fei to Message, Pointer, Timer, Trace and Trap models
8
+ * Allow for // comments in Flor language
9
+ * Call post_task when tasker hands back task
10
+
11
+
12
+ ## flor 1.5.0 released 2021-11-24
13
+
14
+ * Add storage callbacks `on(:pointers, :any) { do_that }`
15
+ * Add `on_receive` (and `on receive`)
16
+
17
+
5
18
  ## flor 1.4.0 released 2021-11-10
6
19
 
7
20
  * Add :tree to Execution#to_h
data/LICENSE.txt CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- Copyright (c) 2015-2021, John Mettraux, jmettraux+flor@gmail.com
2
+ Copyright (c) 2015-2023, 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/Makefile CHANGED
@@ -1,10 +1,10 @@
1
1
 
2
2
  ## gem tasks ##
3
3
 
4
- NAME = \
5
- $(shell ruby -e "s = eval(File.read(Dir['*.gemspec'][0])); puts s.name")
6
- VERSION = \
7
- $(shell ruby -e "s = eval(File.read(Dir['*.gemspec'][0])); puts s.version")
4
+ NAME != \
5
+ ruby -e "s = eval(File.read(Dir['*.gemspec'][0])); puts s.name"
6
+ VERSION != \
7
+ ruby -e "s = eval(File.read(Dir['*.gemspec'][0])); puts s.version"
8
8
 
9
9
  count_lines:
10
10
  find lib -name "*.rb" | xargs cat | ruby -e "p STDIN.readlines.count { |l| l = l.strip; l[0, 1] != '#' && l != '' }"
data/README.md CHANGED
@@ -47,7 +47,7 @@ require 'flor/unit'
47
47
  # uncomment to see the flor activity
48
48
 
49
49
  sto_uri = 'sqlite://flor_qs.db'
50
- sto_uri = 'jdbc:sqlite://flor_qs.db' if RUBY_PLATFORM.match(/java/)
50
+ sto_uri = 'jdbc:sqlite://flor_qs.db' if RUBY_PLATFORM.match?(/java/)
51
51
 
52
52
  flor = Flor::Unit.new(loader: Flor::HashLoader, sto_uri: sto_uri)
53
53
  # instantiate flor unit
data/lib/flor/colours.rb CHANGED
@@ -27,7 +27,7 @@ module Flor
27
27
  class Colours
28
28
 
29
29
  Flor::COLS.each do |k, v|
30
- if v.match(/\A\d/)
30
+ if v.match(/\A\d/) # Ruby 2.3 doesn't have String#match?
31
31
  class_eval(%{
32
32
  def #{k}(s=nil)
33
33
  s ? "[#{v}m" + s + "" : "[#{v}m"
@@ -42,7 +42,7 @@ module Flor
42
42
  class NoColours
43
43
 
44
44
  Flor::COLS.each do |k, v|
45
- if v.match(/\A\d/)
45
+ if v.match(/\A\d/) # Ruby 2.3 doesn't have String#match?
46
46
  class_eval("def #{k}(s=''); s; end")
47
47
  else
48
48
  class_eval("alias #{k} #{v}")
@@ -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
@@ -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
@@ -982,6 +991,36 @@ class Flor::Procedure < Flor::Node
982
991
 
983
992
  []
984
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
985
1024
  end
986
1025
 
987
1026
 
data/lib/flor/flor.rb CHANGED
@@ -199,7 +199,7 @@ module Flor
199
199
  def is_array_of_messages?(o)
200
200
 
201
201
  o.is_a?(Array) &&
202
- o.all? { |e| is_message?(o) }
202
+ o.all? { |e| is_message?(e) }
203
203
  end
204
204
 
205
205
  def h_fetch(h, *keys)
data/lib/flor/parser.rb CHANGED
@@ -6,6 +6,9 @@ module Flor
6
6
 
7
7
  #Raabro.pp(Flor::Parser.parse(input, debug: 2), colours: true)
8
8
  #Raabro.pp(Flor::Parser.parse(input, debug: 3), colours: true)
9
+ #
10
+ # turn one or the other when debugging the parser...
11
+
9
12
  opts = fname if fname.is_a?(Hash) && opts.empty?
10
13
 
11
14
  if r = Flor::Parser.parse(input, opts)
@@ -49,7 +52,7 @@ module Flor
49
52
  def semicolon(i); str(nil, i, ';'); end
50
53
  def comma(i); str(nil, i, ','); end
51
54
  def dquote(i); str(nil, i, '"'); end
52
- def slash(i); str(nil, i, '/'); end
55
+ def slash(i); rex(nil, i, /\/(?!\/)/); end
53
56
  def dollar(i); str(nil, i, '$'); end
54
57
  def pipepipe(i); str(nil, i, '||'); end
55
58
 
@@ -95,7 +98,11 @@ module Flor
95
98
  seq(nil, i, :dot, :rf_symbol)
96
99
  end
97
100
  def rf_index(i); alt(nil, i, :rf_dot_idx, :rf_sqa_idx); end
98
- def rf_symbol(i); rex(:refsym, i, /[^.:;| \b\f\n\r\t"',()\[\]{}#\\]+/); end
101
+ #
102
+ def rf_symbol(i)
103
+ rex(:refsym, i, /([^.:;| \b\f\n\r\t"',()\[\]{}#\\\/]|\/(?!\/))+/)
104
+ # anything but... a slash is ok, but not a double slash...
105
+ end
99
106
  #
100
107
  def reference(i); seq(:ref, i, :rf_symbol, :rf_index, '*'); end
101
108
 
@@ -165,7 +172,7 @@ module Flor
165
172
  }x)
166
173
  end
167
174
 
168
- def comment(i); rex(nil, i, /#[^\r\n]*/); end
175
+ def comment(i); rex(nil, i, /(#|\/\/)[^\r\n]*/); end
169
176
 
170
177
  def eol(i); seq(nil, i, :wstar, :comment, '?', :rnstar); end
171
178
  def eol_wstar(i); seq(nil, i, :wstar, :comment, '?', :rnstar, :wstar); end
@@ -210,7 +217,7 @@ module Flor
210
217
  # %w[ equ == != <> ], %w[ lgt < > <= >= ], %w[ sum + - ], %w[ prd * / % ],
211
218
 
212
219
  def ssmod(i); str(:sop, i, /%/); end
213
- def ssprd(i); rex(:sop, i, /[\*\/]/); end
220
+ def ssprd(i); rex(:sop, i, /(\*|\/(?!\/))/); end
214
221
  def sssum(i); rex(:sop, i, /[+-]/); end
215
222
  def sslgt(i); rex(:sop, i, /(<=?|>=?)/); end
216
223
  def ssequ(i); rex(:sop, i, /(==?|!=|<>)/); end
@@ -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
@@ -16,7 +16,16 @@ if (ARGV & [ '-h', '--help']).any?
16
16
  puts " turns a flor .flo process definition to its tree representation"
17
17
  puts
18
18
  puts " flags:"
19
- puts " --pp pretty prints instead of dumping as JSON"
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"
20
29
  puts
21
30
  exit 0
22
31
  end
@@ -53,11 +62,28 @@ end
53
62
  # end
54
63
 
55
64
  fname = files.first
56
- content = File.read(fname)
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
+
57
74
  tree = Flor.parse(content, fname, {})
58
75
 
59
76
  if flags['--pp']
60
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)
61
87
  else
62
88
  puts JSON.dump(tree)
63
89
  end
@@ -110,10 +110,16 @@ module Flor
110
110
  pt = message['point']
111
111
 
112
112
  ms = [ "call_#{pt}", "on_#{pt}", :on_message, :on, pt ]
113
- ms = ms + [ :on_cancel, :cancel ] if pt == 'detask'
113
+ case pt
114
+ when 'detask' then ms = ms + [ :on_cancel, :cancel ]
115
+ when 'return' then ms = [ :on_return, :return, :post_task ] # /!\
116
+ end
114
117
 
115
118
  m = ms.find { |mm| o.respond_to?(mm) }
116
119
 
120
+ return [ message ] if pt == 'return' && ! m
121
+ # don't call if :post_task not present
122
+
117
123
  fail(
118
124
  "#{k.class.to_s.downcase} #{k} doesn't respond to " +
119
125
  ms[0..-2].collect { |e| "##{e}" }.join(', ') + ", or ##{ms[-1]}"
@@ -316,10 +322,12 @@ module Flor
316
322
 
317
323
  def to_messages(o)
318
324
 
319
- case o
320
- when Hash then [ o ]
321
- when Array then o
322
- else []
325
+ if Flor.is_array_of_messages?(o)
326
+ o
327
+ elsif Flor.is_message?(o)
328
+ [ o ]
329
+ else
330
+ []
323
331
  end
324
332
  end
325
333
 
@@ -48,7 +48,7 @@ module Flor
48
48
 
49
49
  protected
50
50
 
51
- CLOSING_POINTS = %w[ task terminated ceased ]
51
+ CLOSING_POINTS = %w[ task terminated ceased ].freeze
52
52
  #
53
53
  # point for messages that, after consumption, are conserved
54
54
  # in the execution's "closing_messages" array
@@ -160,14 +160,18 @@ module Flor
160
160
  def return(message)
161
161
 
162
162
  n = @execution['nodes'][message['nid']] || {}
163
- m = n['message'] || {}
164
- c = m['cause']
163
+ c = (n['message'] || {})['cause']
165
164
 
166
- rm = message.dup
167
- rm['point'] = 'receive'
168
- rm['cause'] = c if c # preserve 'cause' for routing
165
+ ms =
166
+ if n['task']
167
+ @unit.ganger.task(self, message)
168
+ else
169
+ [ message.dup ]
170
+ end
169
171
 
170
- [ rm ]
172
+ ms.each { |m|
173
+ m['point'] = 'receive'
174
+ m['cause'] = c if c }
171
175
  end
172
176
 
173
177
  def schedule(message)
@@ -47,16 +47,14 @@ module Flor
47
47
  def task(executor, message)
48
48
 
49
49
  domain = message['exid'].split('-', 2).first
50
- tname = message['tasker']
50
+ #tname = message['tasker']
51
+ tname = determine_tasker_name(executor, message)
51
52
 
52
53
  tconf =
53
54
  ( ! message['routed'] &&
54
55
  (@unit.loader.tasker(domain, 'ganger', message) ||
55
56
  @unit.loader.tasker(domain, 'tasker', message))) ||
56
57
  @unit.loader.tasker(domain, tname, message)
57
- #puts "=" * 80
58
- #pp tconf
59
- #puts "=" * 80
60
58
 
61
59
  fail ArgumentError.new(
62
60
  "tasker #{tname.inspect} not found"
@@ -66,6 +64,7 @@ module Flor
66
64
 
67
65
  points = [ nil, message['point'] ]
68
66
  points << 'detask' if points.include?('cancel')
67
+ points << 'task' if points.include?('return')
69
68
 
70
69
  tconf = tconf.find { |h| points.include?(h['point']) }
71
70
  end
@@ -154,6 +153,17 @@ module Flor
154
153
 
155
154
  vars
156
155
  end
156
+
157
+ def determine_tasker_name(executor, message)
158
+
159
+ tname = message['tasker']
160
+
161
+ return tname if tname
162
+
163
+ n = executor.node(message)
164
+
165
+ n['task']['tasker']
166
+ end
157
167
  end
158
168
  end
159
169
 
@@ -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
 
@@ -24,6 +24,8 @@ module Flor
24
24
  def nid; data['nid']; end
25
25
  def tasker; data['tasker']; end
26
26
  alias payload data
27
+
28
+ def fei; [ exid, nid ].join('-') rescue nil; end
27
29
  end
28
30
  end
29
31
 
@@ -29,6 +29,8 @@ module Flor
29
29
  # # we don't care, pointers are cleaned anyway when the flow dies
30
30
  #end
31
31
 
32
+ def fei; [ exid, nid ].join('-'); end
33
+
32
34
  # If the pointer is a "var" pointer, returns the full value
33
35
  # for the variable, as found in the execution's node "0".
34
36
  #
@@ -27,6 +27,8 @@ module Flor
27
27
  # index [ :exid, :nid ]
28
28
  #end
29
29
 
30
+ def fei; [ exid, nid ].join('-'); end
31
+
30
32
  def to_trigger_message
31
33
 
32
34
  d = self.data(false)
@@ -17,6 +17,8 @@ module Flor
17
17
  #
18
18
  # index :exid
19
19
  #end
20
+
21
+ def fei; [ exid, nid ].join('-') rescue nil; end
20
22
  end
21
23
  end
22
24
 
@@ -34,6 +34,8 @@ module Flor
34
34
  # index [ :exid, :nid ]
35
35
  #end
36
36
 
37
+ def fei; [ exid, nid ].join('-') rescue nil; end
38
+
37
39
  def to_hook
38
40
 
39
41
  opts = {}
@@ -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
@@ -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 }
@@ -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)
@@ -418,26 +424,29 @@ module Flor
418
424
  now = Flor.tstamp
419
425
  u = @unit.identifier
420
426
 
421
- synchronize do
427
+ id =
428
+ synchronize do
422
429
 
423
- @db[:flor_timers]
424
- .insert(
425
- domain: Flor.domain(message['exid']),
426
- exid: message['exid'],
427
- nid: message['nid'],
428
- onid: message['onid'] || message['nid'],
429
- bnid: message['nid'],
430
- type: type,
431
- schedule: string,
432
- ntime: next_time,
433
- content: to_blob(message),
434
- count: 0,
435
- status: 'active',
436
- ctime: now,
437
- mtime: now,
438
- cunit: u,
439
- munit: u)
440
- 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)
441
450
 
442
451
  @unit.wake_up
443
452
 
@@ -502,6 +511,8 @@ module Flor
502
511
  munit: u)
503
512
  end
504
513
 
514
+ callback(:traps, :insert, id)
515
+
505
516
  traps[id]
506
517
 
507
518
  rescue => err
@@ -548,6 +559,21 @@ module Flor
548
559
  nil
549
560
  end
550
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
+
551
577
  protected
552
578
 
553
579
  def migration_table_and_column(opts={})
@@ -640,10 +666,11 @@ module Flor
640
666
  def reschedule_timer(t)
641
667
 
642
668
  w = { id: t.id.to_i, status: 'active', mtime: t.mtime, munit: t.munit }
669
+ r = nil
643
670
 
644
671
  if t.type != 'at' && t.type != 'in'
645
672
 
646
- @db[:flor_timers]
673
+ r = @db[:flor_timers]
647
674
  .where(w)
648
675
  .update(
649
676
  count: t.count.to_i + 1,
@@ -652,9 +679,11 @@ module Flor
652
679
  mtime: Flor.tstamp,
653
680
  munit: @unit.identifier)
654
681
 
682
+ callback(:timers, :update, w, t)
683
+
655
684
  elsif @archive
656
685
 
657
- @db[:flor_timers]
686
+ r = @db[:flor_timers]
658
687
  .where(w)
659
688
  .update(
660
689
  count: t.count.to_i + 1,
@@ -662,12 +691,18 @@ module Flor
662
691
  mtime: Flor.tstamp,
663
692
  munit: @unit.identifier)
664
693
 
694
+ callback(:timers, :update, w, t)
695
+
665
696
  else
666
697
 
667
- @db[:flor_timers]
698
+ r = @db[:flor_timers]
668
699
  .where(w)
669
700
  .delete
701
+
702
+ callback(:timers, :delete, w, t)
670
703
  end
704
+
705
+ r
671
706
  end
672
707
 
673
708
  def remove_nodes(exe, status, now)
@@ -787,6 +822,8 @@ module Flor
787
822
  .import(
788
823
  POINTER_COLUMNS,
789
824
  pointers)
825
+
826
+ callback(:pointers, :update, exid)
790
827
  end
791
828
 
792
829
  #def pointer_columns
@@ -835,6 +872,23 @@ module Flor
835
872
  .inject([]) { |a, elt| a << [ a.last, elt ].compact.join('.'); a }
836
873
  end
837
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
+
838
892
  class DbLogger
839
893
 
840
894
  def initialize(unit); @unit = unit; end
@@ -854,11 +908,11 @@ module Flor
854
908
  fail ArgumentError.new("no 'sto_uri' conf, cannot connect to db") \
855
909
  unless uri
856
910
 
857
- return Kernel.const_get(uri) \
858
- if uri.is_a?(String) && uri.match(/\A[A-Z]+\z/)
859
- # for cases where uri == 'DB'
860
-
861
- Sequel.connect(uri)
911
+ begin
912
+ Kernel.const_get(uri)
913
+ rescue NameError
914
+ Sequel.connect(uri)
915
+ end
862
916
  end
863
917
 
864
918
  def connect
@@ -169,9 +169,12 @@ module Flor
169
169
 
170
170
  pt = @message['point']
171
171
 
172
- ms = [ "post_#{pt}" ]; ms << :post_cancel if pt == 'detask'
172
+ #ms = [ "post_#{pt}" ]; ms << :post_cancel if pt == 'detask'
173
+ #call_one_of(ms)
173
174
  #
174
- call_one_of(ms)
175
+ # :post_task is called by, well, the caller
176
+ #
177
+ call_one_of([ :post_detask, :post_cancel ]) if pt == 'detask'
175
178
 
176
179
  msg = derive_message(message)
177
180
 
data/lib/flor.rb CHANGED
@@ -16,7 +16,7 @@ require 'dense'
16
16
 
17
17
  module Flor
18
18
 
19
- VERSION = '1.4.0'
19
+ VERSION = '1.6.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.4.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Mettraux
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-11-10 00:00:00.000000000 Z
11
+ date: 2023-01-13 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
@@ -289,7 +290,7 @@ metadata:
289
290
  mailing_list_uri: https://groups.google.com/forum/#!forum/floraison
290
291
  homepage_uri: https://github.com/floraison/flor
291
292
  source_code_uri: https://github.com/floraison/flor
292
- post_install_message:
293
+ post_install_message:
293
294
  rdoc_options: []
294
295
  require_paths:
295
296
  - lib
@@ -304,8 +305,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
304
305
  - !ruby/object:Gem::Version
305
306
  version: '0'
306
307
  requirements: []
307
- rubygems_version: 3.0.3
308
- signing_key:
308
+ rubygems_version: 3.1.6
309
+ signing_key:
309
310
  specification_version: 4
310
311
  summary: A Ruby workflow engine
311
312
  test_files: []