flor 1.4.0 → 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: 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