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 +4 -4
- data/CHANGELOG.md +13 -0
- data/CREDITS.md +1 -0
- data/LICENSE.txt +1 -1
- data/README.md +10 -3
- data/flor.gemspec +1 -1
- data/lib/flor/colours.rb +52 -52
- data/lib/flor/conf.rb +3 -3
- data/lib/flor/core/executor.rb +1 -1
- data/lib/flor/core/procedure.rb +3 -3
- data/lib/flor/core/texecutor.rb +4 -4
- data/lib/flor/core.rb +65 -52
- data/lib/flor/djan.rb +1 -1
- data/lib/flor/parser.rb +27 -24
- data/lib/flor/pcore/andor.rb +1 -1
- data/lib/flor/pcore/apply.rb +1 -1
- data/lib/flor/pcore/case.rb +1 -1
- data/lib/flor/pcore/define.rb +1 -1
- data/lib/flor/pcore/fail.rb +1 -1
- data/lib/flor/pcore/if.rb +21 -0
- data/lib/flor/pcore/iterator.rb +2 -3
- data/lib/flor/pcore/match.rb +1 -1
- data/lib/flor/pcore/reverse.rb +1 -1
- data/lib/flor/pcore/set.rb +1 -1
- data/lib/flor/pcore/until.rb +1 -1
- data/lib/flor/punit/c_iterator.rb +1 -0
- data/lib/flor/punit/signal.rb +1 -1
- data/lib/flor/punit/trap.rb +1 -1
- data/lib/flor/to_string.rb +39 -36
- data/lib/flor/tools/env.rb +51 -48
- data/lib/flor/tools/shell.rb +12 -9
- data/lib/flor/unit/journal.rb +8 -0
- data/lib/flor/unit/scheduler.rb +27 -20
- data/lib/flor/unit/storage.rb +2 -1
- data/lib/flor.rb +1 -1
- metadata +11 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 047da4d33003f19f54b5d646fa488e59a63878f25e5a4b39006375ce9a9b22c4
|
4
|
+
data.tar.gz: e4d955a4984e3eb781ad10eaa657cc3b9432ef67495402f9286a98a479215685
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
data/LICENSE.txt
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
Copyright (c) 2015-
|
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
|
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
|
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 [
|
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.
|
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
|
-
|
56
|
+
class << self
|
57
57
|
|
58
|
-
|
59
|
-
end
|
58
|
+
def no_colours
|
60
59
|
|
61
|
-
|
60
|
+
@no_colours
|
61
|
+
end
|
62
62
|
|
63
|
-
|
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
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
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
|
-
|
76
|
-
|
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
|
-
|
77
|
+
return @colours if c == true
|
78
|
+
return @no_colours if c == false
|
79
79
|
|
80
|
-
|
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
|
-
|
86
|
-
|
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
|
-
|
87
|
+
@no_colours
|
88
|
+
end
|
89
89
|
|
90
|
-
s
|
91
|
-
end
|
90
|
+
def decolour(s)
|
92
91
|
|
93
|
-
|
92
|
+
s.gsub(/\x1b\[\d+(;\d+)?m/, '')
|
93
|
+
end
|
94
94
|
|
95
|
-
|
96
|
-
end
|
95
|
+
def no_colour_length(s)
|
97
96
|
|
98
|
-
|
97
|
+
decolour(s).length
|
98
|
+
end
|
99
99
|
|
100
|
-
|
101
|
-
r = StringIO.new
|
102
|
-
l = 0
|
100
|
+
def truncate_string(s, maxlen, post='...')
|
103
101
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
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
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
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
|
-
|
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
|
-
|
125
|
-
|
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
|
-
|
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
|
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)
|
data/lib/flor/core/executor.rb
CHANGED
@@ -156,7 +156,7 @@ module Flor
|
|
156
156
|
end
|
157
157
|
#
|
158
158
|
# vars: variables
|
159
|
-
# vdomain: variable domain (used in
|
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
|
|
data/lib/flor/core/procedure.rb
CHANGED
@@ -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
|
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
|
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
|
944
|
+
# The core cancel work, is overridden by some procedure implementations.
|
945
945
|
#
|
946
946
|
def cancel
|
947
947
|
|
data/lib/flor/core/texecutor.rb
CHANGED
@@ -20,12 +20,12 @@ module Flor
|
|
20
20
|
@archive = nil
|
21
21
|
end
|
22
22
|
|
23
|
-
def notify(executor,
|
23
|
+
def notify(executor, message)
|
24
24
|
|
25
|
-
return [] if
|
25
|
+
return [] if message['consumed']
|
26
26
|
|
27
|
-
@logger.notify(executor,
|
28
|
-
@journal <<
|
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
|
-
|
5
|
+
class << self
|
6
6
|
|
7
|
-
|
8
|
-
@exid_mutex ||= Mutex.new
|
7
|
+
def generate_exid(domain, unit)
|
9
8
|
|
10
|
-
|
9
|
+
@exid_counter ||= 0
|
10
|
+
@exid_mutex ||= Mutex.new
|
11
11
|
|
12
|
-
|
13
|
-
@exid_mutex.synchronize do
|
12
|
+
t = Time.now.utc
|
14
13
|
|
15
|
-
|
14
|
+
sus =
|
15
|
+
@exid_mutex.synchronize do
|
16
16
|
|
17
|
-
|
18
|
-
@exid_counter = 0 if @exid_counter > 99
|
17
|
+
sus = t.sec * 100000000 + t.usec * 100 + @exid_counter
|
19
18
|
|
20
|
-
|
21
|
-
|
19
|
+
@exid_counter = @exid_counter + 1
|
20
|
+
@exid_counter = 0 if @exid_counter > 99
|
22
21
|
|
23
|
-
|
22
|
+
Munemo.to_s(sus)
|
23
|
+
end
|
24
24
|
|
25
|
-
|
26
|
-
end
|
25
|
+
t = t.strftime('%Y%m%d.%H%M')
|
27
26
|
|
28
|
-
|
27
|
+
"#{domain}-#{unit}-#{t}.#{sus}"
|
28
|
+
end
|
29
29
|
|
30
|
-
|
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
|
-
|
32
|
+
t =
|
33
|
+
tree.is_a?(String) ?
|
34
|
+
Flor.parse(tree, opts[:fname] || opts[:path], opts) :
|
35
|
+
tree
|
36
36
|
|
37
|
-
|
38
|
-
|
39
|
-
#
|
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
|
-
"
|
43
|
-
|
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
|
-
|
46
|
-
|
57
|
+
msg = {
|
58
|
+
'point' => 'execute',
|
59
|
+
'exid' => exid,
|
60
|
+
'nid' => '0',
|
61
|
+
'tree' => t,
|
62
|
+
'payload' => pl,
|
63
|
+
'vars' => vs }
|
47
64
|
|
48
|
-
|
49
|
-
|
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
|
-
|
56
|
-
|
57
|
-
'exid' => exid,
|
58
|
-
'nid' => '0',
|
59
|
-
'tree' => t,
|
60
|
-
'payload' => pl,
|
61
|
-
'vars' => vs }
|
68
|
+
msg
|
69
|
+
end
|
62
70
|
|
63
|
-
|
64
|
-
if opts.has_key?(:vdomain)
|
71
|
+
def load_procedures(dir)
|
65
72
|
|
66
|
-
|
67
|
-
|
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
|
-
|
80
|
+
Dir[dirpath].sort.each { |path| require(path) }
|
81
|
+
end
|
70
82
|
|
71
|
-
|
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
|
-
|
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
|
-
|
700
|
+
class << self
|
701
701
|
|
702
|
-
|
702
|
+
def unescape_u(cs)
|
703
703
|
|
704
|
-
|
705
|
-
|
704
|
+
s = ''; 4.times { s << cs.next }
|
705
|
+
|
706
|
+
[ s.to_i(16) ].pack('U*')
|
707
|
+
end
|
706
708
|
|
707
|
-
|
709
|
+
def unescape(s)
|
708
710
|
|
709
|
-
|
711
|
+
sio = StringIO.new
|
710
712
|
|
711
|
-
|
713
|
+
cs = s.each_char
|
712
714
|
|
713
|
-
|
715
|
+
loop do
|
714
716
|
|
715
|
-
|
717
|
+
c = cs.next
|
716
718
|
|
717
|
-
|
719
|
+
break unless c
|
718
720
|
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
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
|
-
|
737
|
+
sio.string
|
738
|
+
end
|
736
739
|
end
|
737
740
|
end
|
738
741
|
|
data/lib/flor/pcore/andor.rb
CHANGED
data/lib/flor/pcore/apply.rb
CHANGED
data/lib/flor/pcore/case.rb
CHANGED
@@ -88,7 +88,7 @@ class Flor::Pro::Case < Flor::Procedure
|
|
88
88
|
#
|
89
89
|
# ### defaulting to f.ret
|
90
90
|
#
|
91
|
-
# When nothing is
|
91
|
+
# When nothing is explicitly provided for consideration by "case", the
|
92
92
|
# incoming `f.ret` is used.
|
93
93
|
#
|
94
94
|
# ```
|
data/lib/flor/pcore/define.rb
CHANGED
@@ -103,7 +103,7 @@ class Flor::Pro::Define < Flor::Procedure
|
|
103
103
|
|
104
104
|
return [ tre, sig ] unless wrapped?(sig)
|
105
105
|
|
106
|
-
# There
|
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)
|
data/lib/flor/pcore/fail.rb
CHANGED
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
|
#
|
data/lib/flor/pcore/iterator.rb
CHANGED
@@ -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
|
-
|
152
|
+
|
153
|
+
att_children.each { |ac| th[1] << Flor.dup(ac) }
|
155
154
|
|
156
155
|
if non_att_children.any?
|
157
156
|
|
data/lib/flor/pcore/match.rb
CHANGED
@@ -156,7 +156,7 @@ class Flor::Pro::Match < Flor::Pro::Case
|
|
156
156
|
#
|
157
157
|
# ## "bind"
|
158
158
|
#
|
159
|
-
# "bind" binds
|
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)"
|
data/lib/flor/pcore/reverse.rb
CHANGED
data/lib/flor/pcore/set.rb
CHANGED
@@ -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
|
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 ]
|
data/lib/flor/pcore/until.rb
CHANGED
data/lib/flor/punit/signal.rb
CHANGED
data/lib/flor/punit/trap.rb
CHANGED
@@ -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
|
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:
|
data/lib/flor/to_string.rb
CHANGED
@@ -5,58 +5,61 @@ module Flor
|
|
5
5
|
# used in procedure, cancel, until, cursor specs
|
6
6
|
# when "walking" and "stepping"
|
7
7
|
|
8
|
-
|
8
|
+
class << self
|
9
9
|
|
10
|
-
|
11
|
-
# should it emerge somewhere...
|
10
|
+
def to_s(o=nil, k=nil)
|
12
11
|
|
13
|
-
|
12
|
+
return 'Flor' if o == nil && k == nil
|
13
|
+
# should it emerge somewhere...
|
14
14
|
|
15
|
-
|
15
|
+
return o.collect { |e| Flor.to_s(e, k) }.join("\n") if o.is_a?(Array)
|
16
16
|
|
17
|
-
|
18
|
-
return message_to_s(o) if o['point'].is_a?(String)
|
17
|
+
if o.is_a?(Hash)
|
19
18
|
|
20
|
-
|
21
|
-
|
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
|
-
|
25
|
-
o.inspect
|
26
|
-
end
|
30
|
+
def message_to_s(m)
|
27
31
|
|
28
|
-
|
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
|
-
|
31
|
-
|
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
|
-
|
37
|
-
end
|
41
|
+
def node_status_to_s(n)
|
38
42
|
|
39
|
-
|
43
|
+
stas = n['status'].reverse
|
40
44
|
|
41
|
-
|
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
|
-
|
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
|
-
|
55
|
-
end
|
59
|
+
def node_to_s(n) # there is already a .node_to_s in log.rb
|
56
60
|
|
57
|
-
|
58
|
-
|
59
|
-
n.inspect
|
61
|
+
n.inspect
|
62
|
+
end
|
60
63
|
end
|
61
64
|
end
|
62
65
|
|
data/lib/flor/tools/env.rb
CHANGED
@@ -8,74 +8,77 @@ module Flor::Tools; end
|
|
8
8
|
|
9
9
|
module Flor::Tools::Env
|
10
10
|
|
11
|
-
|
11
|
+
class << self
|
12
12
|
|
13
|
-
|
14
|
-
opts = envname
|
15
|
-
envname = nil
|
16
|
-
end
|
13
|
+
def make(path, envname=nil, opts={})
|
17
14
|
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
if envname.is_a?(Hash)
|
16
|
+
opts = envname
|
17
|
+
envname = nil
|
18
|
+
end
|
21
19
|
|
22
|
-
|
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
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
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
|
-
|
32
|
+
# protected, somehow
|
31
33
|
|
32
|
-
|
34
|
+
def mk_etc(*ps, opts)
|
33
35
|
|
34
|
-
|
35
|
-
|
36
|
-
|
36
|
+
mkdir(*ps, 'etc')
|
37
|
+
mkdir(*ps, 'etc', 'variables')
|
38
|
+
touch(*ps, 'etc', 'variables', '.gitkeep') if opts[:gitkeep]
|
37
39
|
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
46
|
+
def mk_lib(*ps, opts)
|
45
47
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
55
|
+
def mk_usr(*ps, opts)
|
54
56
|
|
55
|
-
|
57
|
+
mkdir(*ps, 'usr')
|
56
58
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
68
|
+
def mk_var(*ps, opts)
|
67
69
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
70
|
+
mkdir(*ps, 'var')
|
71
|
+
mkdir(*ps, 'var', 'log')
|
72
|
+
touch(*ps, 'var', 'log', '.gitkeep') if opts[:gitkeep]
|
73
|
+
end
|
72
74
|
|
73
|
-
|
74
|
-
|
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
|
-
|
78
|
+
def write(*ps, &block)
|
77
79
|
|
78
|
-
|
80
|
+
File.open(File.join(*ps.compact), 'wb') { |f| f.write(block.call) }
|
81
|
+
end
|
79
82
|
end
|
80
83
|
end
|
81
84
|
|
data/lib/flor/tools/shell.rb
CHANGED
@@ -208,18 +208,21 @@ module Flor::Tools
|
|
208
208
|
|
209
209
|
ALIASES = {}
|
210
210
|
|
211
|
-
|
211
|
+
class << self
|
212
212
|
|
213
|
-
|
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
|
-
|
218
|
-
|
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
|
-
|
222
|
+
def is_alias?(c)
|
221
223
|
|
222
|
-
|
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
|
-
#
|
648
|
+
# already done
|
646
649
|
else
|
647
650
|
page(Flor.to_d(@unit.conf, colour: true, indent: 1, width: true))
|
648
651
|
end
|
data/lib/flor/unit/journal.rb
CHANGED
@@ -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
|
#
|
data/lib/flor/unit/scheduler.rb
CHANGED
@@ -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[
|
50
|
-
@reload_after = @conf[
|
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[
|
56
|
+
@msg_max_res_time = @conf['sch_msg_max_res_time'] || 10 * 60
|
57
57
|
|
58
58
|
@idle_count = 0
|
59
59
|
|
60
|
-
@
|
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,
|
373
|
+
def notify(executor, message)
|
371
374
|
|
372
375
|
if executor
|
373
|
-
@hooker.notify(executor,
|
376
|
+
@hooker.notify(executor, message)
|
374
377
|
else
|
375
|
-
@hooker.wlist.notify(nil,
|
378
|
+
@hooker.wlist.notify(nil, message)
|
376
379
|
end
|
377
380
|
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
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
|
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
|
-
|
731
|
+
break if free_executor_count < 1
|
725
732
|
|
726
733
|
next unless @storage.reserve_all_messages(ms)
|
727
734
|
|
data/lib/flor/unit/storage.rb
CHANGED
data/lib/flor.rb
CHANGED
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.
|
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:
|
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.
|
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.
|
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
|
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: []
|