flor 1.4.0 → 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: 298a6b2c9d3752b561ee8933ae00ed7a3f63682fb758dbe899097c6f7f56c00d
4
- data.tar.gz: 3e2602737b9de7a452549d0f8ed34752618b1322a5508d178abe6559d549c045
3
+ metadata.gz: 352d4347077550a9e577689e8b1f11ad90b7efb0d6fa14e99a228b634852c55f
4
+ data.tar.gz: 81015d54353ff68b48108e09fdfc8933cae2b20ed48ccba1679bebc1b67258da
5
5
  SHA512:
6
- metadata.gz: 28ae10c78676391537dde7cd7e0082338650c6aeba60fbcffa2ca5a4cb2dd2f168d7ff61019472c5f6f3d2216cabfe6e9bccb7cdae4856b513e0663f4d4ec362
7
- data.tar.gz: 6379738cddfc5b74d37a30fa23260924649866b49dc670f37a0540a47a5079062288851ce07306350e227d73d0fd873f2199c3ea01b3399bce61572b8cebe497
6
+ metadata.gz: c322beec0f868fbf72c79678227cbf2a2fea34611fe340b1fdd680a7d6b1f5a09246af1a78f6923977a061772ef22fc8d20d2d13a0bad2145991f296b4cfd143
7
+ data.tar.gz: 3c3e2ce6a88acbaeeab0bb9d89f8c22e5dfd8fb1a81491f0d54983d21d692e3cd93262c842ed8dbc00e021b2fee1d5f3e3e791d8af72bfc6667c472d712d3541
data/CHANGELOG.md CHANGED
@@ -2,6 +2,12 @@
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
+
5
11
  ## flor 1.4.0 released 2021-11-10
6
12
 
7
13
  * Add :tree to Execution#to_h
@@ -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
 
@@ -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
@@ -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
 
@@ -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
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.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.4.0
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-11-10 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