flor 1.6.1 → 1.6.3

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: 162cdcdf2bca8c6f41feb24643e32306cd65183d30a3a6562b8a1d51b151192e
4
- data.tar.gz: 48c5664d39b6efc8783121806cfd7faa24f26714e00a1a5af9694ca9dd68d09e
3
+ metadata.gz: 047da4d33003f19f54b5d646fa488e59a63878f25e5a4b39006375ce9a9b22c4
4
+ data.tar.gz: e4d955a4984e3eb781ad10eaa657cc3b9432ef67495402f9286a98a479215685
5
5
  SHA512:
6
- metadata.gz: 94825467c26f7cb714bd0744979023164655eed579d19b6662a2e7b75d8fa7dc170f21c4ff5ce6e3d12fcd53b90a86999fcd10389481edf362a0c7ab4d469481
7
- data.tar.gz: 8c4f9c54c2284f6fd72ecfc1d6f9f83e7a436f446ae2eec06efd8978aaf34e609f1a8d06f3320b384a01fe64c702abaf8c20c87080830f745ee0dfb7d12f4ec5
6
+ metadata.gz: 7be8459b6ce6f98a957c37950682ed558526455e9f329ee952d16f148693b81bb95a98741f83302525d37f9d4f5170d451d393ebbbac686342f7c078d2e171ad
7
+ data.tar.gz: 81a86bafd33ee08eab5561c3693f030008ff1d65903d70f706376e0f6d917483354b1b7c4f8b20cf2d14a8257644488cd307ecd2a660100d12a9aa975cf55364
data/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
  # CHANGELOG.md
3
3
 
4
4
 
5
+ ## flor 1.6.3 released 2025-05-29
6
+
7
+ * Stop Scheduler#notify swallowing errors, gh-43
8
+ * Rework max_execution_count conf and use, gh-41
9
+ * Introduce Flor.pp(o, opts={}) and Flor.pp_to_s
10
+ * Set skip_transaction: true when reimporting ptrs
11
+
12
+
13
+ ## flor 1.6.2 released 2023-12-03
14
+
15
+ * Prevent djan failing on negative indent for hash keys
16
+
17
+
5
18
  ## flor 1.6.1 released 2023-10-20
6
19
 
7
20
  * Use djan in Flor.msg_to_detail_s(executor, message)
data/CREDITS.md CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  ## Contributors
5
5
 
6
+ * pscholze - https://github.com/pscholze
6
7
  * Olle Jonsson - https://github.com/olleolleolle
7
8
  * Ryan Scott - https://github.com/Subtletree
8
9
  * Jeffrey Hicks - https://github.com/jrhicks
data/LICENSE.txt CHANGED
@@ -1,5 +1,5 @@
1
1
 
2
- Copyright (c) 2015-2023, John Mettraux, jmettraux+flor@gmail.com
2
+ Copyright (c) 2015-2025, 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/README.md CHANGED
@@ -6,8 +6,10 @@
6
6
 
7
7
  Flor is a "Ruby workflow engine", if that makes any sense.
8
8
 
9
+ <!--
9
10
  * [floraison mailing list](https://groups.google.com/forum/#!forum/floraison)
10
11
  * [twitter.com/@flor_workflow](https://twitter.com/flor_workflow)
12
+ -->
11
13
 
12
14
 
13
15
  ## use
@@ -36,7 +38,7 @@ Using flor in your Ruby project requires you to clearly separate business proces
36
38
 
37
39
  ## quickstart
38
40
 
39
- This quickstart sets up a flor unit tied to a SQLite database, resets the databse, binds two taskers and then launches a flow execution involving the two taskers. Finally, it prints out the resulting workitem as the execution has just terminated.
41
+ This quickstart sets up a flor unit tied to a SQLite database, resets the database, binds two taskers and then launches a flow execution involving the two taskers. Finally, it prints out the resulting workitem as the execution has just terminated.
40
42
 
41
43
  ```ruby
42
44
  require 'flor/unit'
@@ -94,7 +96,7 @@ p r['payload']['log']
94
96
 
95
97
  This quickstart is at [doc/quickstart0/](doc/quickstart0/), it's a minimal, one-file Ruby quickstart.
96
98
 
97
- There is also [doc/quickstart1/](doc/quickstart1/), a more complex example, that shows a flor setup, where taskers and flows are layed out in a flor directory tree.
99
+ There is also [doc/quickstart1/](doc/quickstart1/), a more complex example, that shows a flor setup, where taskers and flows are laid out in a flor directory tree.
98
100
 
99
101
 
100
102
  ## documentation
@@ -114,6 +116,8 @@ See [doc/](doc/).
114
116
  * [floraison/flack](https://github.com/floraison/flack) - a flor wrapping [Rack](https://github.com/rack/rack) app
115
117
  * [floraison/fugit](https://github.com/floraison/fugit) - a time library for flor and [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler)
116
118
  * [floraison/raabro](https://github.com/floraison/raabro) - the PEG library flor uses for its parsing needs
119
+ * [kiebor81/flor-language-service](https://github.com/kiebor81/flor-language-service) - a Visual Studio Code language service for the Flor language
120
+ * [flor.vim](https://github.com/floraison/flor/blob/master/misc/flor.vim) - a Vim syntax file for flor
117
121
 
118
122
 
119
123
  ## blog posts and presentations
@@ -138,10 +142,13 @@ There are various other Ruby and Ruby on Rails projects about workflows and busi
138
142
  * [Petri Flow](https://github.com/hooopo/petri_flow) - "Petri Net Workflow Engine for Ruby" (Rails)
139
143
  * [Pallets](https://github.com/linkyndy/pallets) - "Simple and reliable workflow engine, written in Ruby"
140
144
  * [Gush](https://github.com/chaps-io/gush) - "Fast and distributed workflow runner using ActiveJob and Redis"
145
+ * [Rukawa](https://github.com/joker1007/rukawa) - Hyper simple workflow engine powered by concurrent-ruby
146
+ * [ChronoForge](https://github.com/radioactive-labs/chrono_forge) - durable workflow engine for Ruby on Rails that provides reliable state management, error recovery, and workflow orchestration through ActiveJob
147
+ * [StepperMotor](https://github.com/stepper-motor/stepper_motor) - Effortless step workflows for Rails
141
148
 
142
149
  There is a [workflow engine](https://ruby.libhunt.com/categories/5786-workflow-engine) category on [Awesome Ruby](https://ruby.libhunt.com/).
143
150
 
144
- If you want your engine/library to be added in this list, don't hesitate to ask me on [Gitter](https://gitter.im/floraison/flor) or via a pull request.
151
+ If you want your engine/library to be added in this list, don't hesitate to ask me on via an [issue](https://github.com/floraison/flor/issues) or a pull request.
145
152
 
146
153
  It's not limited to Ruby, but there is a wider list at [meirwah/awesome-workflow-engines](https://github.com/meirwah/awesome-workflow-engines).
147
154
 
data/flor.gemspec CHANGED
@@ -40,7 +40,7 @@ A Ruby workflow engine (ruote next generation)
40
40
  #s.add_runtime_dependency 'rufus-lru', '~> 1.1'
41
41
  s.add_runtime_dependency 'munemo', '~> 1.0' # >= 1.0 and < 2
42
42
  s.add_runtime_dependency 'raabro', '~> 1.1' # >= 1.1 and < 2
43
- s.add_runtime_dependency 'fugit', '~> 1.2' # >= 1.2 and < 2
43
+ s.add_runtime_dependency 'fugit', '~> 1.10', '>= 1.11.1' # >= 1.2 and < 2
44
44
  s.add_runtime_dependency 'dense', '~> 1.1' # >= 1.1 and < 2
45
45
 
46
46
  s.add_runtime_dependency 'sequel', '~> 5.0' # >= 5.0 and < 6
data/lib/flor/colours.rb CHANGED
@@ -53,78 +53,78 @@ module Flor
53
53
  @colours = Colours.new
54
54
  @no_colours = NoColours.new
55
55
 
56
- def self.no_colours
56
+ class << self
57
57
 
58
- @no_colours
59
- end
58
+ def no_colours
60
59
 
61
- def self.colours(opts={})
60
+ @no_colours
61
+ end
62
62
 
63
- #opts =
64
- # case opts
65
- # when Hash then opts
66
- # when Colours, NoColours then { color: opts }
67
- # else { out: opts }
68
- # end
63
+ def colours(opts={})
69
64
 
70
- c = nil;
71
- [ :color, :colour, :colors, :colours ].each do |k|
72
- if opts.has_key?(k); c = opts[k]; break; end
73
- end
65
+ #opts =
66
+ # case opts
67
+ # when Hash then opts
68
+ # when Colours, NoColours then { color: opts }
69
+ # else { out: opts }
70
+ # end
74
71
 
75
- return @colours if c == true
76
- return @no_colours if c == false
72
+ c = nil;
73
+ [ :color, :colour, :colors, :colours ].each do |k|
74
+ if opts.has_key?(k); c = opts[k]; break; end
75
+ end
77
76
 
78
- o = opts[:out] || $stdout
77
+ return @colours if c == true
78
+ return @no_colours if c == false
79
79
 
80
- return @colours if (
81
- (o.respond_to?(:log_colours?) ? o.log_colours? : o.tty?) ||
82
- ($0[-6..-1] == '/rspec' &&
83
- (ARGV.include?('--tty') || ARGV.include?('--color'))))
80
+ o = opts[:out] || $stdout
84
81
 
85
- @no_colours
86
- end
82
+ return @colours if (
83
+ (o.respond_to?(:log_colours?) ? o.log_colours? : o.tty?) ||
84
+ ($0[-6..-1] == '/rspec' &&
85
+ (ARGV.include?('--tty') || ARGV.include?('--color'))))
87
86
 
88
- def self.decolour(s)
87
+ @no_colours
88
+ end
89
89
 
90
- s.gsub(/\x1b\[\d+(;\d+)?m/, '')
91
- end
90
+ def decolour(s)
92
91
 
93
- def self.no_colour_length(s)
92
+ s.gsub(/\x1b\[\d+(;\d+)?m/, '')
93
+ end
94
94
 
95
- decolour(s).length
96
- end
95
+ def no_colour_length(s)
97
96
 
98
- def self.truncate_string(s, maxlen, post='...')
97
+ decolour(s).length
98
+ end
99
99
 
100
- ncl = no_colour_length(s)
101
- r = StringIO.new
102
- l = 0
100
+ def truncate_string(s, maxlen, post='...')
103
101
 
104
- s.scan(/(\x1b\[\d+(?:;\d+)?m|[^\x1b]+)/) do |ss, _|
105
- if ss[0, 1] == ""
106
- r << ss
107
- else
102
+ ncl = no_colour_length(s)
103
+ r = StringIO.new
104
+ l = 0
105
+
106
+ s.scan(/(\x1b\[\d+(?:;\d+)?m|[^\x1b]+)/) do |ss, _|
107
+ if ss[0, 1] == ""
108
+ r << ss
109
+ else
108
110
  #p({ r: r.string, l: l, ssl: ss.length, maxlen: maxlen, reml: maxlen - l })
109
- ss = ss[0, maxlen - l]
110
- r << ss
111
- l += ss.length
112
- break if l >= maxlen
111
+ ss = ss[0, maxlen - l]
112
+ r << ss
113
+ l += ss.length
114
+ break if l >= maxlen
115
+ end
113
116
  end
114
- end
115
117
 
116
- return r.string if l < maxlen
117
-
118
- if post.is_a?(String)
119
- r << post
120
- elsif post.is_a?(Proc)
121
- r << post.call(ncl, maxlen, s)
122
- end
118
+ return r.string if l < maxlen
123
119
 
124
- r.string
125
- end
120
+ if post.is_a?(String)
121
+ r << post
122
+ elsif post.is_a?(Proc)
123
+ r << post.call(ncl, maxlen, s)
124
+ end
126
125
 
127
- class << self
126
+ r.string
127
+ end
128
128
 
129
129
  alias decolor decolour
130
130
 
data/lib/flor/conf.rb CHANGED
@@ -30,8 +30,8 @@ module Flor
30
30
  # reload/resync with db after how much time? (defaults to 60 (seconds))
31
31
  # minimizes communication with db in idle periods
32
32
  #
33
- # * :sch_max_executors
34
- # How many executor thread at most? (defaults to 7, 1 is OK in certain
33
+ # * :sch_max_executors or :sch_max_executor_count
34
+ # How many executor threads at most? (defaults to 7, 1 is OK in certain
35
35
  # environments)
36
36
  #
37
37
  # * :exe_max_messages
@@ -101,7 +101,7 @@ module Flor
101
101
  c.merge!(interpret_env)
102
102
  end
103
103
 
104
- c.merge!(over_conf)
104
+ c.merge!(Flor.to_string_keyed_hash(over_conf))
105
105
  end
106
106
 
107
107
  def get_class(conf, key)
@@ -156,7 +156,7 @@ module Flor
156
156
  end
157
157
  #
158
158
  # vars: variables
159
- # vdomain: variable domain (used in conjuction with the loader)
159
+ # vdomain: variable domain (used in conjunction with the loader)
160
160
  # cnid: closure nid
161
161
  # dbg: used to debug messages (useful @node['dbg'] when 'receive')
162
162
 
@@ -872,7 +872,7 @@ class Flor::Procedure < Flor::Node
872
872
  end
873
873
 
874
874
  # Called by the executor, in turns call cancel and cancel_when_ methods
875
- # which may be overriden.
875
+ # which may be overridden.
876
876
  #
877
877
  def do_cancel
878
878
 
@@ -908,7 +908,7 @@ class Flor::Procedure < Flor::Node
908
908
  end
909
909
 
910
910
  # Called by the executor, in turns call kill and kill_when_ methods
911
- # which may be overriden.
911
+ # which may be overridden.
912
912
  #
913
913
  def do_kill
914
914
 
@@ -941,7 +941,7 @@ class Flor::Procedure < Flor::Node
941
941
  [] # no effect
942
942
  end
943
943
 
944
- # The core cancel work, is overriden by some procedure implementations.
944
+ # The core cancel work, is overridden by some procedure implementations.
945
945
  #
946
946
  def cancel
947
947
 
@@ -20,12 +20,12 @@ module Flor
20
20
  @archive = nil
21
21
  end
22
22
 
23
- def notify(executor, msg)
23
+ def notify(executor, message)
24
24
 
25
- return [] if msg['consumed']
25
+ return [] if message['consumed']
26
26
 
27
- @logger.notify(executor, msg)
28
- @journal << msg
27
+ @logger.notify(executor, message)
28
+ @journal << message
29
29
 
30
30
  []
31
31
  end
data/lib/flor/core.rb CHANGED
@@ -2,80 +2,93 @@
2
2
 
3
3
  module Flor
4
4
 
5
- def self.generate_exid(domain, unit)
5
+ class << self
6
6
 
7
- @exid_counter ||= 0
8
- @exid_mutex ||= Mutex.new
7
+ def generate_exid(domain, unit)
9
8
 
10
- t = Time.now.utc
9
+ @exid_counter ||= 0
10
+ @exid_mutex ||= Mutex.new
11
11
 
12
- sus =
13
- @exid_mutex.synchronize do
12
+ t = Time.now.utc
14
13
 
15
- sus = t.sec * 100000000 + t.usec * 100 + @exid_counter
14
+ sus =
15
+ @exid_mutex.synchronize do
16
16
 
17
- @exid_counter = @exid_counter + 1
18
- @exid_counter = 0 if @exid_counter > 99
17
+ sus = t.sec * 100000000 + t.usec * 100 + @exid_counter
19
18
 
20
- Munemo.to_s(sus)
21
- end
19
+ @exid_counter = @exid_counter + 1
20
+ @exid_counter = 0 if @exid_counter > 99
22
21
 
23
- t = t.strftime('%Y%m%d.%H%M')
22
+ Munemo.to_s(sus)
23
+ end
24
24
 
25
- "#{domain}-#{unit}-#{t}.#{sus}"
26
- end
25
+ t = t.strftime('%Y%m%d.%H%M')
27
26
 
28
- def self.make_launch_msg(exid, tree, opts)
27
+ "#{domain}-#{unit}-#{t}.#{sus}"
28
+ end
29
29
 
30
- t =
31
- tree.is_a?(String) ?
32
- Flor.parse(tree, opts[:fname] || opts[:path], opts) :
33
- tree
30
+ def make_launch_msg(exid, tree, opts)
34
31
 
35
- unless t
32
+ t =
33
+ tree.is_a?(String) ?
34
+ Flor.parse(tree, opts[:fname] || opts[:path], opts) :
35
+ tree
36
36
 
37
- #h = opts.merge(prune: false, rewrite: false, debug: 0)
38
- #Raabro.pp(Flor.parse(tree, h[:fname], h))
39
- # TODO re-parse and indicate what went wrong...
37
+ unless t
38
+
39
+ #h = opts.merge(prune: false, rewrite: false, debug: 0)
40
+ #Raabro.pp(Flor.parse(tree, h[:fname], h))
41
+ # TODO re-parse and indicate what went wrong...
42
+
43
+ fail ArgumentError.new(
44
+ "flow parsing failed: " + tree.inspect[0, 35] + '...')
45
+ end
46
+
47
+ pl = opts[:payload] || opts[:fields] || {}
48
+ vs = opts[:variables] || opts[:vars] || {}
40
49
 
41
50
  fail ArgumentError.new(
42
- "flow parsing failed: " + tree.inspect[0, 35] + '...')
43
- end
51
+ "given launch payload should be a Hash, but it's a #{pl.class}"
52
+ ) unless pl.is_a?(Hash)
53
+ fail ArgumentError.new(
54
+ "given launch variables should come in a Hash, but it's a #{vs.class}"
55
+ ) unless vs.is_a?(Hash)
44
56
 
45
- pl = opts[:payload] || opts[:fields] || {}
46
- vs = opts[:variables] || opts[:vars] || {}
57
+ msg = {
58
+ 'point' => 'execute',
59
+ 'exid' => exid,
60
+ 'nid' => '0',
61
+ 'tree' => t,
62
+ 'payload' => pl,
63
+ 'vars' => vs }
47
64
 
48
- fail ArgumentError.new(
49
- "given launch payload should be a Hash, but it's a #{pl.class}"
50
- ) unless pl.is_a?(Hash)
51
- fail ArgumentError.new(
52
- "given launch variables should come in a Hash, but it's a #{vs.class}"
53
- ) unless vs.is_a?(Hash)
65
+ msg['vdomain'] = opts[:vdomain] \
66
+ if opts.has_key?(:vdomain)
54
67
 
55
- msg = {
56
- 'point' => 'execute',
57
- 'exid' => exid,
58
- 'nid' => '0',
59
- 'tree' => t,
60
- 'payload' => pl,
61
- 'vars' => vs }
68
+ msg
69
+ end
62
70
 
63
- msg['vdomain'] = opts[:vdomain] \
64
- if opts.has_key?(:vdomain)
71
+ def load_procedures(dir)
65
72
 
66
- msg
67
- end
73
+ dirpath =
74
+ if dir.match(/\A[.\/]/)
75
+ File.join(dir, '*.rb')
76
+ else
77
+ File.join(File.dirname(__FILE__), dir, '*.rb')
78
+ end
68
79
 
69
- def self.load_procedures(dir)
80
+ Dir[dirpath].sort.each { |path| require(path) }
81
+ end
70
82
 
71
- dirpath =
72
- if dir.match(/\A[.\/]/)
73
- File.join(dir, '*.rb')
74
- else
75
- File.join(File.dirname(__FILE__), dir, '*.rb')
76
- end
83
+ def pp(o, opts={})
77
84
 
78
- Dir[dirpath].sort.each { |path| require(path) }
85
+ puts Flor.to_djan(o, { indent: 2, width: true }.merge(opts))
86
+ end
87
+
88
+ def pp_to_s(o, opts={})
89
+
90
+ Flor.to_djan(o, { indent: 2, width: true }.merge(opts))
91
+ end
79
92
  end
80
93
  end
81
94
 
data/lib/flor/djan.rb CHANGED
@@ -140,7 +140,7 @@ module Flor
140
140
  #
141
141
  kml, vml =
142
142
  x.inject([ 0, 0 ]) { |(kl, vl), (k, v)|
143
- [ [ kl, len(k, opts) ].max, [ vl, len(v, opts) ].max ] }
143
+ [ [ kl, len(k.to_s, opts) ].max, [ vl, len(v, opts) ].max ] }
144
144
  kml += 1
145
145
  #
146
146
  if i && w && i + kml + 1 + vml < w
data/lib/flor/parser.rb CHANGED
@@ -697,42 +697,45 @@ fail "don't know how to invert #{operation.inspect}" # FIXME
697
697
  alias rewrite_panode rewrite_flor
698
698
  end # module Parser
699
699
 
700
- def self.unescape_u(cs)
700
+ class << self
701
701
 
702
- s = ''; 4.times { s << cs.next }
702
+ def unescape_u(cs)
703
703
 
704
- [ s.to_i(16) ].pack('U*')
705
- end
704
+ s = ''; 4.times { s << cs.next }
705
+
706
+ [ s.to_i(16) ].pack('U*')
707
+ end
706
708
 
707
- def self.unescape(s)
709
+ def unescape(s)
708
710
 
709
- sio = StringIO.new
711
+ sio = StringIO.new
710
712
 
711
- cs = s.each_char
713
+ cs = s.each_char
712
714
 
713
- loop do
715
+ loop do
714
716
 
715
- c = cs.next
717
+ c = cs.next
716
718
 
717
- break unless c
719
+ break unless c
718
720
 
719
- if c == '\\'
720
- case cn = cs.next
721
- when 'u' then sio.print(unescape_u(cs))
722
- when '\\', '"', '\'' then sio.print(cn)
723
- when 'b' then sio.print("\b")
724
- when 'f' then sio.print("\f")
725
- when 'n' then sio.print("\n")
726
- when 'r' then sio.print("\r")
727
- when 't' then sio.print("\t")
728
- else sio.print("\\#{cn}")
721
+ if c == '\\'
722
+ case cn = cs.next
723
+ when 'u' then sio.print(unescape_u(cs))
724
+ when '\\', '"', '\'' then sio.print(cn)
725
+ when 'b' then sio.print("\b")
726
+ when 'f' then sio.print("\f")
727
+ when 'n' then sio.print("\n")
728
+ when 'r' then sio.print("\r")
729
+ when 't' then sio.print("\t")
730
+ else sio.print("\\#{cn}")
731
+ end
732
+ else
733
+ sio.print(c)
729
734
  end
730
- else
731
- sio.print(c)
732
735
  end
733
- end
734
736
 
735
- sio.string
737
+ sio.string
738
+ end
736
739
  end
737
740
  end
738
741
 
@@ -11,7 +11,7 @@ class Flor::Pro::Andor < Flor::Procedure
11
11
  # and
12
12
  # false
13
13
  # true
14
- # # => evalutes to false
14
+ # # => evaluates to false
15
15
  # ```
16
16
  #
17
17
  # ```
@@ -13,7 +13,7 @@ class Flor::Pro::Apply < Flor::Procedure
13
13
  # apply sum 1 2
14
14
  # ```
15
15
  #
16
- # It is usually used implicitely, as in
16
+ # It is usually used implicitly, as in
17
17
  # ```
18
18
  # sequence
19
19
  # define sum a b
@@ -88,7 +88,7 @@ class Flor::Pro::Case < Flor::Procedure
88
88
  #
89
89
  # ### defaulting to f.ret
90
90
  #
91
- # When nothing is explicitely provided for consideration by "case", the
91
+ # When nothing is explicitly provided for consideration by "case", the
92
92
  # incoming `f.ret` is used.
93
93
  #
94
94
  # ```
@@ -103,7 +103,7 @@ class Flor::Pro::Define < Flor::Procedure
103
103
 
104
104
  return [ tre, sig ] unless wrapped?(sig)
105
105
 
106
- # There is a parenthese around the parameters, let's unwrap that...
106
+ # There are parentheses around the parameters, let's unwrap that...
107
107
 
108
108
  hed = Flor.dup(tre[1][0, off])
109
109
  sig = Flor.dup(sig)
@@ -2,7 +2,7 @@
2
2
 
3
3
  class Flor::Pro::Fail < Flor::Procedure
4
4
  #
5
- # Explicitely raises an error.
5
+ # Explicitly raises an error.
6
6
  #
7
7
  # ```
8
8
  # fail "not enough water in the tank"
data/lib/flor/pcore/if.rb CHANGED
@@ -43,6 +43,27 @@ class Flor::Pro::If < Flor::Procedure
43
43
  #
44
44
  # Currently, if an "else if" is needed, it's better to use [cond](cond.md).
45
45
  #
46
+ # ## improving readability with else and then
47
+ #
48
+ # "then" and "else" can be aliased to "sequence" and be used within if to
49
+ # make the flow definition easier to read, and especially less confusing.
50
+ #
51
+ # ```
52
+ # # at the top of the workflow definition
53
+ # # alias "then" and "else" to "sequence"
54
+ #
55
+ # set then sequence
56
+ # set else sequence
57
+ #
58
+ # # ...
59
+ #
60
+ # if (f.age > 3)
61
+ # then
62
+ # set f.designation 'child'
63
+ # else
64
+ # set f.designation 'baby'
65
+ # order_baby_food _
66
+ # ```
46
67
  #
47
68
  # ## see also
48
69
  #
@@ -146,12 +146,11 @@ class Flor::Macro::Iterator < Flor::Macro
146
146
 
147
147
  def rewrite_iterator_tree(procedure_name)
148
148
 
149
- atts = att_children
150
-
151
149
  l = tree[2]
152
150
 
153
151
  th = [ procedure_name, [], l, *tree[3] ]
154
- atts.each { |ac| th[1] << Flor.dup(ac) }
152
+
153
+ att_children.each { |ac| th[1] << Flor.dup(ac) }
155
154
 
156
155
  if non_att_children.any?
157
156
 
@@ -156,7 +156,7 @@ class Flor::Pro::Match < Flor::Pro::Case
156
156
  #
157
157
  # ## "bind"
158
158
  #
159
- # "bind" binds explicitely a value and allows for a sub-pattern.
159
+ # "bind" binds explicitly a value and allows for a sub-pattern.
160
160
  # ```
161
161
  # match [ 1 4 ]
162
162
  # [ 1 (bind y (or 2 3)) ]; "match y:$(y)"
@@ -17,7 +17,7 @@ class Flor::Pro::Reverse < Flor::Procedure
17
17
  # reverse _ # sets f.ret to [ 4, 6, 5 ]
18
18
  # ```
19
19
  #
20
- # Will fail if it finds nothing reversable.
20
+ # Will fail if it finds nothing reversible.
21
21
  #
22
22
  # # see also
23
23
  #
@@ -49,7 +49,7 @@ class Flor::Pro::Set < Flor::Procedure
49
49
  # set __2 g h
50
50
  # [ 9 10 11 12 13 ]
51
51
  # # ==> g: 11, h: 12
52
- # # `__` is not prefixed by a var name, so it justs discard
52
+ # # `__` is not prefixed by a var name, so it just discards
53
53
  # # what it captures
54
54
  # set i j___
55
55
  # [ 14 15 16 17 18 19 ]
@@ -2,7 +2,7 @@
2
2
 
3
3
  class Flor::Pro::Until < Flor::Procedure
4
4
  #
5
- # Loops until or while a condiation evalutates to true.
5
+ # Loops until or while a condiation evaluates to true.
6
6
  #
7
7
  # ```
8
8
  # set i 0
@@ -156,6 +156,7 @@ class Flor::Pro::ConcurrentIterator < Flor::Procedure
156
156
  is.each { |i| t1[i][1][0][0] = '_reff' }
157
157
 
158
158
  @node['tree'] = [ t[0], t1, *t[2..-1] ]
159
+ Flor.pp @node['tree']
159
160
  end
160
161
  end
161
162
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  class Flor::Pro::Signal < Flor::Procedure
4
4
  #
5
- # Used in conjuction with "on".
5
+ # Used in conjunction with "on".
6
6
  #
7
7
  # An external (or internal) agent may send a signal to an execution and the
8
8
  # execution may have a "on" handler for it.
@@ -139,7 +139,7 @@ class Flor::Pro::Trap < Flor::Procedure
139
139
  #
140
140
  # The range limit determines what is the range, or scope of the trap.
141
141
  # By default the trap only care about nodes below its parent. In other words,
142
- # the trap binds itself to its parent and observes the messages occuring
142
+ # the trap binds itself to its parent and observes the messages occurring
143
143
  # in the execution in the branch of nodes whose root is the parent.
144
144
  #
145
145
  # The possible values for `range:` are:
@@ -5,58 +5,61 @@ module Flor
5
5
  # used in procedure, cancel, until, cursor specs
6
6
  # when "walking" and "stepping"
7
7
 
8
- def self.to_s(o=nil, k=nil)
8
+ class << self
9
9
 
10
- return 'Flor' if o == nil && k == nil
11
- # should it emerge somewhere...
10
+ def to_s(o=nil, k=nil)
12
11
 
13
- return o.collect { |e| Flor.to_s(e, k) }.join("\n") if o.is_a?(Array)
12
+ return 'Flor' if o == nil && k == nil
13
+ # should it emerge somewhere...
14
14
 
15
- if o.is_a?(Hash)
15
+ return o.collect { |e| Flor.to_s(e, k) }.join("\n") if o.is_a?(Array)
16
16
 
17
- return send("message_#{k}_to_s", o) if k && o['point'].is_a?(String)
18
- return message_to_s(o) if o['point'].is_a?(String)
17
+ if o.is_a?(Hash)
19
18
 
20
- return send("node_#{k}_to_s", o) if k && o.has_key?('parent')
21
- return node_to_s(o) if o['parent'].is_a?(String)
19
+ return send("message_#{k}_to_s", o) if k && o['point'].is_a?(String)
20
+ return message_to_s(o) if o['point'].is_a?(String)
21
+
22
+ return send("node_#{k}_to_s", o) if k && o.has_key?('parent')
23
+ return node_to_s(o) if o['parent'].is_a?(String)
24
+ end
25
+
26
+ return [ o, k ].inspect if k
27
+ o.inspect
22
28
  end
23
29
 
24
- return [ o, k ].inspect if k
25
- o.inspect
26
- end
30
+ def message_to_s(m)
27
31
 
28
- def self.message_to_s(m)
32
+ s = StringIO.new
33
+ s << '(msg ' << m['nid'] << ' ' << m['point']
34
+ %w[ from flavour ].each { |k|
35
+ s << ' ' << k << ':' << m[k].to_s if m.has_key?(k) }
36
+ s << ')'
29
37
 
30
- s = StringIO.new
31
- s << '(msg ' << m['nid'] << ' ' << m['point']
32
- %w[ from flavour ].each { |k|
33
- s << ' ' << k << ':' << m[k].to_s if m.has_key?(k) }
34
- s << ')'
38
+ s.string
39
+ end
35
40
 
36
- s.string
37
- end
41
+ def node_status_to_s(n)
38
42
 
39
- def self.node_status_to_s(n)
43
+ stas = n['status'].reverse
40
44
 
41
- stas = n['status'].reverse
45
+ s = StringIO.new
46
+ while sta = stas.shift
47
+ s << '(status ' << (sta['status'] || 'o') # o for open
48
+ s << ' pt:' << sta['point']
49
+ if f = sta['flavour']; s << ' fla:' << f; end
50
+ if f = sta['from']; s << ' fro:' << f; end
51
+ if m = sta['m']; s << ' m:' << m; end
52
+ s << ')'
53
+ s << "\n" if stas.any?
54
+ end
42
55
 
43
- s = StringIO.new
44
- while sta = stas.shift
45
- s << '(status ' << (sta['status'] || 'o') # o for open
46
- s << ' pt:' << sta['point']
47
- if f = sta['flavour']; s << ' fla:' << f; end
48
- if f = sta['from']; s << ' fro:' << f; end
49
- if m = sta['m']; s << ' m:' << m; end
50
- s << ')'
51
- s << "\n" if stas.any?
56
+ s.string
52
57
  end
53
58
 
54
- s.string
55
- end
59
+ def node_to_s(n) # there is already a .node_to_s in log.rb
56
60
 
57
- def self.node_to_s(n) # there is already a .node_to_s in log.rb
58
-
59
- n.inspect
61
+ n.inspect
62
+ end
60
63
  end
61
64
  end
62
65
 
@@ -8,74 +8,77 @@ module Flor::Tools; end
8
8
 
9
9
  module Flor::Tools::Env
10
10
 
11
- def self.make(path, envname=nil, opts={})
11
+ class << self
12
12
 
13
- if envname.is_a?(Hash)
14
- opts = envname
15
- envname = nil
16
- end
13
+ def make(path, envname=nil, opts={})
17
14
 
18
- opts[:env] = envname || 'production'
19
- opts[:sto_uri] ||= 'sqlite://tmp/dev.db'
20
- opts[:gitkeep] = true unless opts.has_key?(:gitkeep)
15
+ if envname.is_a?(Hash)
16
+ opts = envname
17
+ envname = nil
18
+ end
21
19
 
22
- mkdir(path, envname) if envname
20
+ opts[:env] = envname || 'production'
21
+ opts[:sto_uri] ||= 'sqlite://tmp/dev.db'
22
+ opts[:gitkeep] = true unless opts.has_key?(:gitkeep)
23
23
 
24
- mk_etc(path, envname, opts)
25
- mk_lib(path, envname, opts)
26
- mk_usr(path, envname, opts)
27
- mk_var(path, envname, opts)
28
- end
24
+ mkdir(path, envname) if envname
25
+
26
+ mk_etc(path, envname, opts)
27
+ mk_lib(path, envname, opts)
28
+ mk_usr(path, envname, opts)
29
+ mk_var(path, envname, opts)
30
+ end
29
31
 
30
- # protected, somehow
32
+ # protected, somehow
31
33
 
32
- def self.mk_etc(*ps, opts)
34
+ def mk_etc(*ps, opts)
33
35
 
34
- mkdir(*ps, 'etc')
35
- mkdir(*ps, 'etc', 'variables')
36
- touch(*ps, 'etc', 'variables', '.gitkeep') if opts[:gitkeep]
36
+ mkdir(*ps, 'etc')
37
+ mkdir(*ps, 'etc', 'variables')
38
+ touch(*ps, 'etc', 'variables', '.gitkeep') if opts[:gitkeep]
37
39
 
38
- write(*ps, 'etc', 'conf.json') do
39
- "env: #{opts[:env]}\n" +
40
- "sto_uri: #{opts[:sto_uri].inspect}"
40
+ write(*ps, 'etc', 'conf.json') do
41
+ "env: #{opts[:env]}\n" +
42
+ "sto_uri: #{opts[:sto_uri].inspect}"
43
+ end
41
44
  end
42
- end
43
45
 
44
- def self.mk_lib(*ps, opts)
46
+ def mk_lib(*ps, opts)
45
47
 
46
- mkdir(*ps, 'lib')
47
- mkdir(*ps, 'lib', 'flows')
48
- touch(*ps, 'lib', 'flows', '.gitkeep') if opts[:gitkeep]
49
- mkdir(*ps, 'lib', 'taskers')
50
- touch(*ps, 'lib', 'taskers', '.gitkeep') if opts[:gitkeep]
51
- end
48
+ mkdir(*ps, 'lib')
49
+ mkdir(*ps, 'lib', 'flows')
50
+ touch(*ps, 'lib', 'flows', '.gitkeep') if opts[:gitkeep]
51
+ mkdir(*ps, 'lib', 'taskers')
52
+ touch(*ps, 'lib', 'taskers', '.gitkeep') if opts[:gitkeep]
53
+ end
52
54
 
53
- def self.mk_usr(*ps, opts)
55
+ def mk_usr(*ps, opts)
54
56
 
55
- mkdir(*ps, 'usr')
57
+ mkdir(*ps, 'usr')
56
58
 
57
- if opts[:acme] == false
58
- touch(*ps, 'usr', '.gitkeep') if opts[:gitkeep]
59
- else
60
- mkdir(*ps, 'usr', 'com.acme')
61
- mk_etc(*ps, 'usr', 'com.acme', opts)
62
- mk_lib(*ps, 'usr', 'com.acme', opts)
59
+ if opts[:acme] == false
60
+ touch(*ps, 'usr', '.gitkeep') if opts[:gitkeep]
61
+ else
62
+ mkdir(*ps, 'usr', 'com.acme')
63
+ mk_etc(*ps, 'usr', 'com.acme', opts)
64
+ mk_lib(*ps, 'usr', 'com.acme', opts)
65
+ end
63
66
  end
64
- end
65
67
 
66
- def self.mk_var(*ps, opts)
68
+ def mk_var(*ps, opts)
67
69
 
68
- mkdir(*ps, 'var')
69
- mkdir(*ps, 'var', 'log')
70
- touch(*ps, 'var', 'log', '.gitkeep') if opts[:gitkeep]
71
- end
70
+ mkdir(*ps, 'var')
71
+ mkdir(*ps, 'var', 'log')
72
+ touch(*ps, 'var', 'log', '.gitkeep') if opts[:gitkeep]
73
+ end
72
74
 
73
- def self.mkdir(*ps); FileUtils.mkdir(File.join(*ps.compact)); end
74
- def self.touch(*ps); FileUtils.touch(File.join(*ps.compact)); end
75
+ def mkdir(*ps); FileUtils.mkdir(File.join(*ps.compact)); end
76
+ def touch(*ps); FileUtils.touch(File.join(*ps.compact)); end
75
77
 
76
- def self.write(*ps, &block)
78
+ def write(*ps, &block)
77
79
 
78
- File.open(File.join(*ps.compact), 'wb') { |f| f.write(block.call) }
80
+ File.open(File.join(*ps.compact), 'wb') { |f| f.write(block.call) }
81
+ end
79
82
  end
80
83
  end
81
84
 
@@ -208,18 +208,21 @@ module Flor::Tools
208
208
 
209
209
  ALIASES = {}
210
210
 
211
- def self.make_alias(a, b)
211
+ class << self
212
212
 
213
- define_method("hlp_#{a}") { "alias to #{b.inspect}" }
214
- alias_method "man_#{a}", "man_#{b}" rescue nil
215
- alias_method "cmd_#{a}", "cmd_#{b}"
213
+ def make_alias(a, b)
216
214
 
217
- (ALIASES[b] ||= []) << a
218
- end
215
+ define_method("hlp_#{a}") { "alias to #{b.inspect}" }
216
+ alias_method "man_#{a}", "man_#{b}" rescue nil
217
+ alias_method "cmd_#{a}", "cmd_#{b}"
218
+
219
+ (ALIASES[b] ||= []) << a
220
+ end
219
221
 
220
- def self.is_alias?(c)
222
+ def is_alias?(c)
221
223
 
222
- !! ALIASES.values.find { |a| a.include?(c) }
224
+ !! ALIASES.values.find { |a| a.include?(c) }
225
+ end
223
226
  end
224
227
 
225
228
  def args(line); line.split(/\s+/); end
@@ -642,7 +645,7 @@ module Flor::Tools
642
645
  puts Flor.to_d(
643
646
  { key: @unit.conf[key] }, colour: true, indent: 1, width: true)
644
647
  elsif key
645
- # alreay done
648
+ # already done
646
649
  else
647
650
  page(Flor.to_d(@unit.conf, colour: true, indent: 1, width: true))
648
651
  end
@@ -1,7 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+
3
4
  module Flor
4
5
 
6
+ # As seen in docs/hooks.md
7
+ #
8
+ # ```ruby
9
+ # flor.hooker.add('journal', Flor::Journal)
10
+ # ```
11
+ #
12
+ #
5
13
  # A Journal hook, receives only "consumed" messages and
6
14
  # keeps a copy of all of them.
7
15
  #
@@ -46,19 +46,22 @@ module Flor
46
46
  @spooler =
47
47
  (Flor::Conf.get_class(@conf, 'spooler') || Flor::Spooler).new(self)
48
48
 
49
- @heart_rate = @conf[:sch_heart_rate] || 0.3
50
- @reload_after = @conf[:sch_reload_after] || 60
49
+ @heart_rate = @conf['sch_heart_rate'] || 0.3
50
+ @reload_after = @conf['sch_reload_after'] || 60
51
51
  #
52
52
  @wake_up = true
53
53
  @next_time = nil
54
54
  @reloaded_at = Time.now
55
55
 
56
- @msg_max_res_time = @conf[:sch_msg_max_res_time] || 10 * 60
56
+ @msg_max_res_time = @conf['sch_msg_max_res_time'] || 10 * 60
57
57
 
58
58
  @idle_count = 0
59
59
 
60
- @max_executors = @conf[:sch_max_executors] || 1
61
- #
60
+ @max_executor_count =
61
+ @conf['sch_max_executor_count'] ||
62
+ @conf['sch_max_executors'] ||
63
+ 7
64
+
62
65
  @executors = []
63
66
 
64
67
  c = @conf['constant']
@@ -367,20 +370,22 @@ module Flor
367
370
  @wake_up = true
368
371
  end
369
372
 
370
- def notify(executor, o)
373
+ def notify(executor, message)
371
374
 
372
375
  if executor
373
- @hooker.notify(executor, o)
376
+ @hooker.notify(executor, message)
374
377
  else
375
- @hooker.wlist.notify(nil, o)
378
+ @hooker.wlist.notify(nil, message)
376
379
  end
377
380
 
378
- rescue => err
379
- puts '-sch' * 19
380
- puts "+ error in #{self.class}#notify"
381
- p err
382
- puts err.backtrace
383
- puts ('-sch' * 19) + ' .'
381
+ # NO, let it fail...
382
+ #
383
+ #rescue => err
384
+ # puts '-sch' * 19
385
+ # puts "+ error in #{self.class}#notify"
386
+ # p err
387
+ # puts err.backtrace
388
+ # puts ('-sch' * 19) + ' .'
384
389
  end
385
390
 
386
391
  def trap(node, tra)
@@ -710,18 +715,20 @@ module Flor
710
715
  @storage.trigger_timers
711
716
  end
712
717
 
718
+ def free_executor_count
719
+
720
+ [ 0, @max_executor_count - @executors.count ].max
721
+ end
722
+
713
723
  def trigger_executions
714
724
 
715
725
  @executors.select! { |e| e.alive? }
716
726
  # drop done executors
717
727
 
718
- free_executor_count = @max_executors - @executors.size
719
-
720
- return if free_executor_count < 1
721
-
722
- messages = @storage.load_messages(free_executor_count)
728
+ @storage.load_messages(free_executor_count).each do |exid, ms|
729
+ # remember that load_messages load count + 2 executions...
723
730
 
724
- messages.each do |exid, ms|
731
+ break if free_executor_count < 1
725
732
 
726
733
  next unless @storage.reserve_all_messages(ms)
727
734
 
@@ -821,7 +821,8 @@ module Flor
821
821
  @db[:flor_pointers]
822
822
  .import(
823
823
  POINTER_COLUMNS,
824
- pointers)
824
+ pointers,
825
+ skip_transaction: true)
825
826
 
826
827
  callback(:pointers, :update, exid)
827
828
  end
data/lib/flor.rb CHANGED
@@ -16,7 +16,7 @@ require 'dense'
16
16
 
17
17
  module Flor
18
18
 
19
- VERSION = '1.6.1'
19
+ VERSION = '1.6.3'
20
20
  end
21
21
 
22
22
  require 'flor/colours'
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flor
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.1
4
+ version: 1.6.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - John Mettraux
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2023-10-20 00:00:00.000000000 Z
10
+ date: 2025-05-29 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: munemo
@@ -44,14 +43,20 @@ dependencies:
44
43
  requirements:
45
44
  - - "~>"
46
45
  - !ruby/object:Gem::Version
47
- version: '1.2'
46
+ version: '1.10'
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: 1.11.1
48
50
  type: :runtime
49
51
  prerelease: false
50
52
  version_requirements: !ruby/object:Gem::Requirement
51
53
  requirements:
52
54
  - - "~>"
53
55
  - !ruby/object:Gem::Version
54
- version: '1.2'
56
+ version: '1.10'
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ version: 1.11.1
55
60
  - !ruby/object:Gem::Dependency
56
61
  name: dense
57
62
  requirement: !ruby/object:Gem::Requirement
@@ -290,7 +295,6 @@ metadata:
290
295
  mailing_list_uri: https://groups.google.com/forum/#!forum/floraison
291
296
  homepage_uri: https://github.com/floraison/flor
292
297
  source_code_uri: https://github.com/floraison/flor
293
- post_install_message:
294
298
  rdoc_options: []
295
299
  require_paths:
296
300
  - lib
@@ -305,8 +309,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
305
309
  - !ruby/object:Gem::Version
306
310
  version: '0'
307
311
  requirements: []
308
- rubygems_version: 3.2.33
309
- signing_key:
312
+ rubygems_version: 3.6.2
310
313
  specification_version: 4
311
314
  summary: A Ruby workflow engine
312
315
  test_files: []