flor 0.16.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +44 -1
- data/CREDITS.md +2 -0
- data/LICENSE.txt +1 -1
- data/Makefile +2 -2
- data/README.md +115 -7
- data/flor.gemspec +8 -9
- data/lib/flor.rb +2 -2
- data/lib/flor/colours.rb +5 -3
- data/lib/flor/conf.rb +20 -6
- data/lib/flor/core.rb +9 -8
- data/lib/flor/core/executor.rb +70 -38
- data/lib/flor/core/node.rb +5 -17
- data/lib/flor/core/procedure.rb +97 -12
- data/lib/flor/core/texecutor.rb +9 -2
- data/lib/flor/djan.rb +6 -2
- data/lib/flor/errors.rb +1 -0
- data/lib/flor/flor.rb +233 -21
- data/lib/flor/id.rb +20 -0
- data/lib/flor/log.rb +8 -1
- data/lib/flor/migrations/0001_tables.rb +7 -6
- data/lib/flor/migrations/0002_cunit_and_munit.rb +1 -0
- data/lib/flor/migrations/0003_timer_onid_bnid.rb +1 -0
- data/lib/flor/migrations/0004_trap_bnid.rb +1 -0
- data/lib/flor/migrations/0005_pointer_content.rb +21 -0
- data/lib/flor/parser.rb +19 -11
- data/lib/flor/pcore/_apply.rb +104 -57
- data/lib/flor/pcore/_arr.rb +1 -0
- data/lib/flor/pcore/_atom.rb +1 -0
- data/lib/flor/pcore/_att.rb +16 -1
- data/lib/flor/pcore/_coll.rb +1 -0
- data/lib/flor/pcore/_dmute.rb +5 -1
- data/lib/flor/pcore/_dol.rb +1 -0
- data/lib/flor/pcore/_dqs.rb +1 -0
- data/lib/flor/pcore/_dump.rb +1 -0
- data/lib/flor/pcore/_err.rb +1 -0
- data/lib/flor/pcore/_head.rb +1 -0
- data/lib/flor/pcore/_obj.rb +1 -0
- data/lib/flor/pcore/_pat_.rb +1 -0
- data/lib/flor/pcore/_pat_arr.rb +1 -0
- data/lib/flor/pcore/_pat_guard.rb +1 -0
- data/lib/flor/pcore/_pat_obj.rb +1 -0
- data/lib/flor/pcore/_pat_or.rb +1 -0
- data/lib/flor/pcore/_pat_regex.rb +1 -0
- data/lib/flor/pcore/_ref.rb +3 -1
- data/lib/flor/pcore/_rxs.rb +1 -0
- data/lib/flor/pcore/_skip.rb +1 -0
- data/lib/flor/pcore/_val.rb +1 -0
- data/lib/flor/pcore/all.rb +1 -0
- data/lib/flor/pcore/andor.rb +1 -0
- data/lib/flor/pcore/any.rb +1 -0
- data/lib/flor/pcore/apply.rb +1 -0
- data/lib/flor/pcore/arith.rb +47 -9
- data/lib/flor/pcore/array_qmark.rb +1 -0
- data/lib/flor/pcore/break.rb +14 -2
- data/lib/flor/pcore/case.rb +42 -0
- data/lib/flor/pcore/cmp.rb +1 -0
- data/lib/flor/pcore/collect.rb +2 -1
- data/lib/flor/pcore/cond.rb +1 -0
- data/lib/flor/pcore/cursor.rb +46 -4
- data/lib/flor/pcore/define.rb +33 -6
- data/lib/flor/pcore/detect.rb +1 -0
- data/lib/flor/pcore/do_return.rb +1 -0
- data/lib/flor/pcore/each.rb +1 -0
- data/lib/flor/pcore/echo.rb +1 -0
- data/lib/flor/pcore/empty.rb +1 -0
- data/lib/flor/pcore/fail.rb +1 -0
- data/lib/flor/pcore/filter.rb +1 -0
- data/lib/flor/pcore/find.rb +1 -0
- data/lib/flor/pcore/flatten.rb +1 -0
- data/lib/flor/pcore/for_each.rb +1 -0
- data/lib/flor/pcore/if.rb +1 -0
- data/lib/flor/pcore/includes.rb +1 -0
- data/lib/flor/pcore/inject.rb +1 -0
- data/lib/flor/pcore/iterator.rb +13 -0
- data/lib/flor/pcore/keys.rb +1 -0
- data/lib/flor/pcore/length.rb +1 -0
- data/lib/flor/pcore/loop.rb +1 -0
- data/lib/flor/pcore/map.rb +1 -0
- data/lib/flor/pcore/match.rb +1 -0
- data/lib/flor/pcore/matchr.rb +1 -0
- data/lib/flor/pcore/max.rb +1 -0
- data/lib/flor/pcore/merge.rb +1 -0
- data/lib/flor/pcore/move.rb +1 -0
- data/lib/flor/pcore/noeval.rb +1 -0
- data/lib/flor/pcore/noret.rb +1 -0
- data/lib/flor/pcore/not.rb +1 -0
- data/lib/flor/pcore/on.rb +34 -9
- data/lib/flor/pcore/on_cancel.rb +2 -1
- data/lib/flor/pcore/on_error.rb +1 -0
- data/lib/flor/pcore/push.rb +1 -0
- data/lib/flor/pcore/rand.rb +1 -0
- data/lib/flor/pcore/range.rb +1 -0
- data/lib/flor/pcore/reduce.rb +1 -0
- data/lib/flor/pcore/return.rb +1 -0
- data/lib/flor/pcore/reverse.rb +1 -0
- data/lib/flor/pcore/select.rb +1 -0
- data/lib/flor/pcore/sequence.rb +49 -0
- data/lib/flor/pcore/set.rb +15 -4
- data/lib/flor/pcore/shuffle.rb +1 -0
- data/lib/flor/pcore/slice.rb +1 -0
- data/lib/flor/pcore/sort.rb +1 -0
- data/lib/flor/pcore/sort_by.rb +1 -0
- data/lib/flor/pcore/split.rb +1 -0
- data/lib/flor/pcore/stall.rb +1 -0
- data/lib/flor/pcore/strings.rb +1 -0
- data/lib/flor/pcore/timestamp.rb +1 -0
- data/lib/flor/pcore/to_array.rb +1 -0
- data/lib/flor/pcore/twig.rb +1 -0
- data/lib/flor/pcore/type_of.rb +1 -0
- data/lib/flor/pcore/until.rb +1 -0
- data/lib/flor/punit/abort.rb +50 -0
- data/lib/flor/punit/{ccollect.rb → c_collect.rb} +3 -2
- data/lib/flor/punit/c_each.rb +30 -0
- data/lib/flor/punit/c_for_each.rb +42 -0
- data/lib/flor/punit/c_iterator.rb +161 -0
- data/lib/flor/punit/c_map.rb +44 -0
- data/lib/flor/punit/cancel.rb +10 -1
- data/lib/flor/punit/concurrence.rb +44 -200
- data/lib/flor/punit/cron.rb +1 -0
- data/lib/flor/punit/do_trap.rb +1 -0
- data/lib/flor/punit/every.rb +1 -0
- data/lib/flor/punit/graft.rb +4 -2
- data/lib/flor/punit/m_ram.rb +282 -0
- data/lib/flor/punit/on_timeout.rb +1 -0
- data/lib/flor/punit/part.rb +1 -0
- data/lib/flor/punit/schedule.rb +1 -0
- data/lib/flor/punit/signal.rb +30 -5
- data/lib/flor/punit/sleep.rb +1 -0
- data/lib/flor/punit/task.rb +9 -2
- data/lib/flor/punit/trace.rb +1 -0
- data/lib/flor/punit/trap.rb +41 -1
- data/lib/flor/to_string.rb +1 -0
- data/lib/flor/tools/env.rb +1 -0
- data/lib/flor/tools/shell.rb +1 -0
- data/lib/flor/tools/shell_out.rb +1 -0
- data/lib/flor/tt.rb +98 -0
- data/lib/flor/unit.rb +3 -0
- data/lib/flor/unit/caller.rb +158 -20
- data/lib/flor/unit/caller_jruby.rb +132 -0
- data/lib/flor/unit/dump.rb +36 -0
- data/lib/flor/unit/executor.rb +27 -9
- data/lib/flor/unit/ganger.rb +27 -7
- data/lib/flor/unit/hloader.rb +278 -0
- data/lib/flor/unit/hook.rb +76 -14
- data/lib/flor/unit/hooker.rb +40 -26
- data/lib/flor/unit/journal.rb +23 -0
- data/lib/flor/unit/loader.rb +141 -22
- data/lib/flor/unit/logger.rb +24 -5
- data/lib/flor/unit/models.rb +96 -22
- data/lib/flor/unit/models/execution.rb +66 -4
- data/lib/flor/unit/models/message.rb +1 -0
- data/lib/flor/unit/models/pointer.rb +12 -0
- data/lib/flor/unit/models/timer.rb +1 -0
- data/lib/flor/unit/models/trace.rb +1 -0
- data/lib/flor/unit/models/trap.rb +15 -6
- data/lib/flor/unit/scheduler.rb +284 -46
- data/lib/flor/unit/spooler.rb +6 -3
- data/lib/flor/unit/storage.rb +108 -42
- data/lib/flor/unit/taskers.rb +47 -1
- data/lib/flor/unit/waiter.rb +166 -26
- data/lib/flor/unit/wlist.rb +107 -12
- metadata +31 -46
- data/lib/flor/punit/cmap.rb +0 -112
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7a1f76874a9957b7c15c59baf997e0e60ff4872b9109a7cd741692735a94565c
|
4
|
+
data.tar.gz: 7fd1552910609023dd8bce0de0d5b5fc1ebc5ae1674fc27f0d0cb932c4bab2a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cbca621e72efc0bdf1df26a4554590032f7a42d76ca19b361305f519071f1f026678c55c2980e74661ebcd5ac018d9e3e892376ee69a07c91ebb1d2d809cbb75
|
7
|
+
data.tar.gz: b2c7cbe0a13c12f0e9b935c3d675f583d93a4f78f921982e75eb9050c84d97914c63cf9280667b5be977280cda9de27a57972f3f0d72b097710a6b3b1e45d67b
|
data/CHANGELOG.md
CHANGED
@@ -2,7 +2,50 @@
|
|
2
2
|
# CHANGELOG.md
|
3
3
|
|
4
4
|
|
5
|
-
## flor
|
5
|
+
## flor 0.19.0 not yet released
|
6
|
+
|
7
|
+
|
8
|
+
## flor 0.18.0 released 2019-05-05
|
9
|
+
|
10
|
+
* Refine BasicTasker#reply (more arg patterns)
|
11
|
+
* Fix "signal" vs exid: and payload:
|
12
|
+
* Make payload optional when cancelling
|
13
|
+
* Unlock `signal exid: other_execution_id "xxx"`
|
14
|
+
* Allow for `trap 'signal0' payload: { a: 'A' }`
|
15
|
+
* Allow for "on" in blocking mode (no block given)
|
16
|
+
* Turn "sequence" single string att results to tags
|
17
|
+
* gh-26, refine cancel / on_cancel and payload return
|
18
|
+
* Allow for custom :schema_info migration table
|
19
|
+
* Introduce a dedicated #refresh for all flor models
|
20
|
+
* Let scheduler sleep only 0.001s if @idle_count less than 1
|
21
|
+
* Implement Scheduler #dump and #load
|
22
|
+
* Default target #cancel and #kill to node '0'
|
23
|
+
* Expose taskname to tasker on detasking (@Subtletree)
|
24
|
+
* Refine BasicTasker#reply (more arg patterns)
|
25
|
+
* Allow for `trap 'signal0' payload: { a: 'A' }`
|
26
|
+
* Allow for "on" in blocking mode (no block given)
|
27
|
+
* Unlock `signal exid: other_execution_id "xxx"`
|
28
|
+
* Make payload optional when cancelling (default to payload as it was
|
29
|
+
upon reaching the cancelled node)
|
30
|
+
|
31
|
+
|
32
|
+
## flor 0.17.0 released 2019-04-08
|
33
|
+
|
34
|
+
- Switch to 0.17.x
|
35
|
+
|
36
|
+
|
37
|
+
## flor 0.16.2 released 2019-04-08
|
38
|
+
|
39
|
+
- Many improvements
|
40
|
+
- Allow for `[ 'he' 'll' 'o' ] | + join: '.'` (yields "he.ll.o")
|
41
|
+
- Allow for `[ 1 2 3 ] | + _` (yields `6`)
|
42
|
+
- Make "child_on_error:"/"children_on_error:" a common attribute
|
43
|
+
- Ensure "on_cancel" sets only one handler
|
44
|
+
|
45
|
+
|
46
|
+
## flor 0.16.1 released 2019-02-05
|
47
|
+
|
48
|
+
- Depend on Sequel 5 (Sequel 4 and 5 seem OK)
|
6
49
|
|
7
50
|
|
8
51
|
## flor 0.16.0 released 2019-02-04
|
data/CREDITS.md
CHANGED
data/LICENSE.txt
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
|
2
|
-
Copyright (c) 2015-
|
2
|
+
Copyright (c) 2015-2020, John Mettraux, jmettraux+flor@gmail.com
|
3
3
|
|
4
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
5
5
|
of this software and associated documentation files (the "Software"), to deal
|
data/Makefile
CHANGED
@@ -32,7 +32,7 @@ build: gemspec_validate
|
|
32
32
|
mv $(NAME)-$(VERSION).gem pkg/
|
33
33
|
|
34
34
|
push: build
|
35
|
-
gem push pkg/$(NAME)-$(VERSION).gem
|
35
|
+
gem push --otp "$(OTP)" pkg/$(NAME)-$(VERSION).gem
|
36
36
|
|
37
37
|
|
38
38
|
## flor tasks ##
|
@@ -71,7 +71,7 @@ mk:
|
|
71
71
|
$(RUBY) -Ilib -e "require 'flor/tools/env'; Flor::Tools::Env.make('tmp', '$(FLOR_ENV)', gitkeep: true)"
|
72
72
|
|
73
73
|
doc:
|
74
|
-
$(RUBY) -Imak -r '
|
74
|
+
$(RUBY) -Imak -r 'doc_procedures' -e "make_doc_procedures()"
|
75
75
|
doct:
|
76
76
|
@$(RUBY) mak/ptree.rb
|
77
77
|
|
data/README.md
CHANGED
@@ -10,10 +10,22 @@ Flor is a "Ruby workflow engine", if that makes any sense.
|
|
10
10
|
* [floraison mailing list](https://groups.google.com/forum/#!forum/floraison)
|
11
11
|
* [twitter.com/@flor_workflow](https://twitter.com/flor_workflow)
|
12
12
|
|
13
|
-
|
13
|
+
|
14
|
+
## use
|
15
|
+
|
16
|
+
As a workflow engine, flor takes as input process definitions and executes them. Those executions may in turn call pieces of Ruby code or external scripts that perform the actual work. Those pieces of code and scripts are called "taskers".
|
17
|
+
|
18
|
+
The classical way to use a language interpreter is to instantiate it as needed and let it die as the host execution ends. A workflow engine is more like a server, it may host multiple executions. And if the workflow engine stops, it may be restarted and pick the work back, when it was when it stopped.
|
19
|
+
|
20
|
+
Flor process definitions are written in the flor language, a programming language mostly inspired by Scheme and Ruby. As always with programming languages, readability is hoped for, for a workflow engine this is especially necessary since those business processes are the bread and butter of business users.
|
21
|
+
|
22
|
+
Using flor in your Ruby project requires you to clearly separate business process definitions from taskers. Since a flor instance may host multiple process executions based on one or more process definitions, many of the taskers may be reused from one definition to the other. For instance, if a "send-invoice-to-customer" tasker is created it might get used in the "process-retail-order" and the "process-big-distribution-order" processes.
|
23
|
+
|
24
|
+
|
25
|
+
## design
|
14
26
|
|
15
27
|
* Strives to propose a scheming interpreter for long running executions
|
16
|
-
* Is written in Ruby a rather straightforward language with at least two
|
28
|
+
* Is written in Ruby, a rather straightforward language with at least two
|
17
29
|
wonderful implementations (MRI and JRuby, which is enterprise-friendly)
|
18
30
|
* Stores everything as JSON (if it breaks it's still readable)
|
19
31
|
* Stores in any database supported by [Sequel](http://sequel.jeremyevans.net/)
|
@@ -22,24 +34,120 @@ Flor is a "Ruby workflow engine", if that makes any sense.
|
|
22
34
|
* All in all should be easy to maintain (engine itself and executions running
|
23
35
|
on top of it)
|
24
36
|
|
25
|
-
## Quickstart
|
26
37
|
|
27
|
-
|
38
|
+
## quickstart
|
39
|
+
|
40
|
+
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
|
+
|
42
|
+
```ruby
|
43
|
+
require 'flor/unit'
|
44
|
+
|
45
|
+
#ENV['FLOR_DEBUG'] = 'dbg,sto,stdout' # full sql + flor debug output
|
46
|
+
#ENV['FLOR_DEBUG'] = 'dbg,stdout' # flor debug output
|
47
|
+
# uncomment to see the flor activity
|
48
|
+
|
49
|
+
sto_uri = 'sqlite://flor_qs.db'
|
50
|
+
sto_uri = 'jdbc:sqlite://flor_qs.db' if RUBY_PLATFORM.match(/java/)
|
51
|
+
|
52
|
+
flor = Flor::Unit.new(loader: Flor::HashLoader, sto_uri: sto_uri)
|
53
|
+
# instantiate flor unit
|
54
|
+
|
55
|
+
flor.storage.delete_tables
|
56
|
+
flor.storage.migrate
|
57
|
+
# blank slate database
|
58
|
+
|
59
|
+
class DemoTasker < Flor::BasicTasker
|
60
|
+
def task(message)
|
61
|
+
(attd['times'] || 1).times do
|
62
|
+
message['payload']['log'] << "#{tasker}: #{task_name}"
|
63
|
+
end
|
64
|
+
reply
|
65
|
+
end
|
66
|
+
end
|
67
|
+
flor.add_tasker(:alice, DemoTasker)
|
68
|
+
flor.add_tasker(:bob, DemoTasker)
|
69
|
+
# a simple logging tasker implementation bound under
|
70
|
+
# two different tasker names
|
71
|
+
|
72
|
+
flor.start
|
73
|
+
# start the flor unit, so that it can process executions
|
74
|
+
|
75
|
+
exid = flor.launch(
|
76
|
+
%q{
|
77
|
+
sequence
|
78
|
+
alice 'hello' times: 2
|
79
|
+
bob 'world'
|
80
|
+
},
|
81
|
+
payload: { log: [ "started at #{Time.now}" ] })
|
82
|
+
# launch a new execution, one that chains alice and bob work
|
83
|
+
|
84
|
+
#r = flor.wait(exid, 'terminated')
|
85
|
+
r = flor.wait(exid)
|
86
|
+
# wait for the execution to terminate or to fail
|
28
87
|
|
29
|
-
|
88
|
+
p r['point']
|
89
|
+
# "terminated" hopefully
|
90
|
+
p r['payload']['log']
|
91
|
+
# [ "started at 2019-03-31 10:20:18 +0900",
|
92
|
+
# "alice: hello", "alice: hello",
|
93
|
+
# "bob: world" ]
|
94
|
+
```
|
95
|
+
|
96
|
+
This quickstart is at [doc/quickstart0/](doc/quickstart0/), it's a minimal, one-file Ruby quickstart.
|
97
|
+
|
98
|
+
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
|
+
|
100
|
+
|
101
|
+
## documentation
|
30
102
|
|
31
103
|
See [doc/](doc/).
|
32
104
|
|
105
|
+
* [doc/procedures/](doc/procedures/#procedures) - the basic building blocks of the flor language
|
106
|
+
* [doc/glossary](doc/glossary.md) - words and their meaning in the flor context
|
107
|
+
* [doc/patterns](doc/patterns.md) - workflow patterns and their flor (tentative) implementations
|
108
|
+
|
109
|
+
|
110
|
+
## related projects
|
111
|
+
|
112
|
+
* [mantor/floristry](https://github.com/mantor/floristry) - visualize and interact with flor through Rails facilities
|
113
|
+
* [floraison/pollen](https://github.com/floraison/pollen) - a set of flor hooks that emit over the http
|
114
|
+
* [floraison/florist](https://github.com/floraison/florist) - a flor worklist implementation
|
115
|
+
* [floraison/flack](https://github.com/floraison/flack) - a flor wrapping [Rack](https://github.com/rack/rack) app
|
116
|
+
* [floraison/fugit](https://github.com/floraison/fugit) - a time library for flor and [rufus-scheduler](https://github.com/jmettraux/rufus-scheduler)
|
117
|
+
* [floraison/raabro](https://github.com/floraison/raabro) - the PEG library flor uses for its parsing needs
|
118
|
+
|
119
|
+
|
33
120
|
## blog posts and presentations
|
34
121
|
|
35
|
-
* [
|
122
|
+
* [flor workflow engine](http://jmettraux.skepti.ch/20190407.html?t=flor_workflow_engine&f=readme) - on flor itself
|
123
|
+
* [the flor language](http://jmettraux.skepti.ch/20180927.html?t=the_flor_language&f=readme) - on the flor workflow definition language itself
|
124
|
+
* [reddit answer on workflow engines](http://jmettraux.skepti.ch/20190416.html?t=reddit_answer_on_workflow_engines&f=readme) - an answer to a Reddit question on workflow engines, archived as a post
|
36
125
|
* [Flor, hubristic interpreter](http://rubykaigi.org/2017/presentations/jmettraux.html) - RubyKaigi 2017, Hiroshima - presentation
|
37
126
|
* [flor design 0](http://jmettraux.skepti.ch/20171021.html?t=flor_design_0&f=readme) - running a simple execution, what happens - blog post
|
38
127
|
* [flor, branch to branch](https://speakerdeck.com/jmettraux/flor-branch-to-branch) - q1 2017 - very dry deck
|
39
128
|
* [flor 2017](https://speakerdeck.com/jmettraux/flor-2017) - q1 2017 - very dry deck
|
40
129
|
|
41
130
|
|
42
|
-
##
|
131
|
+
## other Ruby projects about workflows
|
132
|
+
|
133
|
+
There are various other Ruby and Ruby on Rails projects about workflows and business processes, each with its own take on them.
|
134
|
+
|
135
|
+
* [Dynflow](http://dynflow.github.io/) - "Dynflow (DYNamic workFLOW) is a workflow engine written in Ruby"
|
136
|
+
* [rails_workflow](https://github.com/madzhuga/rails_workflow) - "Rails Workflow Engine allows you to organize your application business logic by joining user- and auto- operations in processes"
|
137
|
+
* [rails_engine/flow_core](https://github.com/rails-engine/flow_core) - "A multi purpose, extendable, Workflow-net-based workflow engine for Rails applications"
|
138
|
+
* [Trailblazer](http://trailblazer.to/) - "The Advanced Business Logic Framework"
|
139
|
+
* [Petri Flow](https://github.com/hooopo/petri_flow) - "Petri Net Workflow Engine for Ruby" (Rails)
|
140
|
+
* [Pallets](https://github.com/linkyndy/pallets) - "Simple and reliable workflow engine, written in Ruby"
|
141
|
+
* [Gush](https://github.com/chaps-io/gush) - "Fast and distributed workflow runner using ActiveJob and Redis"
|
142
|
+
|
143
|
+
There is a [workflow engine](https://ruby.libhunt.com/categories/5786-workflow-engine) category on [Awesome Ruby](https://ruby.libhunt.com/).
|
144
|
+
|
145
|
+
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.
|
146
|
+
|
147
|
+
It's not limited to Ruby, but there is a wider list at [meirwah/awesome-workflow-engines](https://github.com/meirwah/awesome-workflow-engines).
|
148
|
+
|
149
|
+
|
150
|
+
## license
|
43
151
|
|
44
152
|
MIT, see [LICENSE.txt](LICENSE.txt)
|
45
153
|
|
data/flor.gemspec
CHANGED
@@ -10,8 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.platform = Gem::Platform::RUBY
|
11
11
|
s.authors = [ 'John Mettraux' ]
|
12
12
|
s.email = [ 'jmettraux+flor@gmail.com' ]
|
13
|
-
s.homepage = '
|
14
|
-
#s.rubyforge_project = 'flor'
|
13
|
+
s.homepage = 'https://github.com/floraison'
|
15
14
|
s.license = 'MIT'
|
16
15
|
s.summary = 'A Ruby workflow engine'
|
17
16
|
|
@@ -38,16 +37,16 @@ A Ruby workflow engine (ruote next generation)
|
|
38
37
|
"#{s.name}.gemspec",
|
39
38
|
]
|
40
39
|
|
41
|
-
s.add_runtime_dependency 'munemo', '~> 1.0', '>= 1.0.1'
|
42
|
-
s.add_runtime_dependency 'raabro', '~> 1.1', '>= 1.1.5'
|
43
40
|
#s.add_runtime_dependency 'rufus-lru', '~> 1.1'
|
44
|
-
s.add_runtime_dependency '
|
45
|
-
s.add_runtime_dependency '
|
41
|
+
s.add_runtime_dependency 'munemo', '~> 1.0' # >= 1.0 and < 2
|
42
|
+
s.add_runtime_dependency 'raabro', '~> 1.1' # >= 1.1 and < 2
|
43
|
+
s.add_runtime_dependency 'fugit', '~> 1.2' # >= 1.2 and < 2
|
44
|
+
s.add_runtime_dependency 'dense', '~> 1.1' # >= 1.1 and < 2
|
46
45
|
|
47
|
-
s.add_runtime_dependency 'sequel', '~>
|
46
|
+
s.add_runtime_dependency 'sequel', '~> 5.0' # >= 5.0 and < 6
|
48
47
|
|
49
|
-
s.add_development_dependency 'rspec', '~> 3.
|
50
|
-
s.add_development_dependency 'terminal-table'
|
48
|
+
s.add_development_dependency 'rspec', '~> 3.8' # >= 3.8 and < 4
|
49
|
+
s.add_development_dependency 'terminal-table', '~> 1.8' # >= 1.8 and < 2
|
51
50
|
|
52
51
|
s.require_path = 'lib'
|
53
52
|
end
|
data/lib/flor.rb
CHANGED
data/lib/flor/colours.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Flor
|
3
4
|
|
4
5
|
COLS = Hash[*%w[
|
5
6
|
|
6
7
|
reset 0;0
|
7
|
-
bright 1 dim 2 underlined 4 blink 5 reverse 7 hidden 8 default 39
|
8
|
+
bright 1 dim 2 underlined 4 blink 5 reverse 7 hidden 8 strike 9 default 39
|
8
9
|
black 30 red 31 green 32 yellow 33 blue 34 magenta 35 cyan 36
|
9
10
|
light_gray 37 dark_gray 90 light_red 91 light_green 92
|
10
11
|
light_yellow 93 light_blue 94 light_magenta 95 light_cyan 96 white 97
|
@@ -17,7 +18,8 @@ module Flor
|
|
17
18
|
brown yellow purple magenta dark_grey dark_gray light_grey light_gray
|
18
19
|
|
19
20
|
rd red bl blue bu blue ba black bk black gn green gr green dg dark_gray
|
20
|
-
gy light_gray lg light_gray yl yellow y yellow ma magenta
|
21
|
+
gy light_gray lg light_gray yl yellow y yellow ma magenta wt white
|
22
|
+
rs reset
|
21
23
|
br bright bri bright un underlined rv reverse bn blink blg bg_light_gray
|
22
24
|
und underlined rev reverse
|
23
25
|
]]
|
@@ -28,7 +30,7 @@ module Flor
|
|
28
30
|
if v.match(/\A\d/)
|
29
31
|
class_eval(%{
|
30
32
|
def #{k}(s=nil)
|
31
|
-
s ? "[#{v}m" + s + "[0;
|
33
|
+
s ? "[#{v}m" + s + "[0;0m" : "[#{v}m"
|
32
34
|
end })
|
33
35
|
else
|
34
36
|
class_eval(
|
data/lib/flor/conf.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Flor
|
3
4
|
|
@@ -48,6 +49,15 @@ module Flor
|
|
48
49
|
# "reserved" state, messages are put back in the "created" state
|
49
50
|
# (by a running unit (scheduler) if any).
|
50
51
|
#
|
52
|
+
# * :db_migration / :sto_migration / :db_migration_dir / :sto_migration_dir
|
53
|
+
# (Flor::Storage#migrate option)
|
54
|
+
# Points the migrator to its Ruby Sequel migration directory.
|
55
|
+
#
|
56
|
+
# * :db_sparse_migrations / :sto_sparse_migrations
|
57
|
+
# (Flor::Storage#migrate option)
|
58
|
+
# Setting this to true is equivalent to calling
|
59
|
+
# `unit.storage.migration(allow_missing_migration_files: true)`
|
60
|
+
#
|
51
61
|
# And finally:
|
52
62
|
#
|
53
63
|
# * :flor_debug or :debug
|
@@ -71,8 +81,12 @@ module Flor
|
|
71
81
|
|
72
82
|
def prepare(conf, over_conf)
|
73
83
|
|
74
|
-
c =
|
75
|
-
|
84
|
+
c =
|
85
|
+
case conf
|
86
|
+
when String then Flor::ConfExecutor.interpret_path_or_source(conf)
|
87
|
+
when Hash then Flor.to_string_keyed_hash(conf)
|
88
|
+
else conf
|
89
|
+
end
|
76
90
|
|
77
91
|
fail ArgumentError.new(
|
78
92
|
"cannot extract conf out of #{c.inspect} (#{conf.class})"
|
@@ -92,10 +106,10 @@ module Flor
|
|
92
106
|
|
93
107
|
def get_class(conf, key)
|
94
108
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
109
|
+
case v = conf[key]
|
110
|
+
when Class then v
|
111
|
+
when String then Flor.const_lookup(v)
|
112
|
+
else nil
|
99
113
|
end
|
100
114
|
end
|
101
115
|
|
data/lib/flor/core.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Flor
|
3
4
|
|
@@ -28,7 +29,7 @@ module Flor
|
|
28
29
|
|
29
30
|
t =
|
30
31
|
tree.is_a?(String) ?
|
31
|
-
Flor.parse(tree, opts[:fname], opts) :
|
32
|
+
Flor.parse(tree, opts[:fname] || opts[:path], opts) :
|
32
33
|
tree
|
33
34
|
|
34
35
|
unless t
|
@@ -51,13 +52,13 @@ module Flor
|
|
51
52
|
"given launch variables should come in a Hash, but it's a #{vs.class}"
|
52
53
|
) unless vs.is_a?(Hash)
|
53
54
|
|
54
|
-
msg =
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
55
|
+
msg = {
|
56
|
+
'point' => 'execute',
|
57
|
+
'exid' => exid,
|
58
|
+
'nid' => '0',
|
59
|
+
'tree' => t,
|
60
|
+
'payload' => pl,
|
61
|
+
'vars' => vs }
|
61
62
|
|
62
63
|
msg['vdomain'] = opts[:vdomain] \
|
63
64
|
if opts.has_key?(:vdomain)
|
data/lib/flor/core/executor.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module Flor
|
3
4
|
|
@@ -69,18 +70,31 @@ module Flor
|
|
69
70
|
|
70
71
|
def trigger_hook(hook, message)
|
71
72
|
|
72
|
-
|
73
|
+
m =
|
74
|
+
case
|
75
|
+
when hook.respond_to?(:notify) then :notify
|
76
|
+
when hook.respond_to?(:on_message) then :on_message
|
77
|
+
else :on
|
78
|
+
end
|
79
|
+
as =
|
80
|
+
case hook.method(m).arity
|
81
|
+
when 3 then [ @unit, self, message ]
|
82
|
+
when 2 then [ self, message ]
|
83
|
+
else [ message ]
|
84
|
+
end
|
85
|
+
|
86
|
+
r = hook.send(m, *as)
|
87
|
+
|
88
|
+
Flor.is_array_of_messages?(r) ? r : []
|
73
89
|
end
|
74
90
|
|
75
91
|
def trigger_block(block, opts, message)
|
76
92
|
|
77
93
|
r =
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
else
|
83
|
-
block.call(self, message, opts)
|
94
|
+
case block.arity
|
95
|
+
when 1 then block.call(message)
|
96
|
+
when 2 then block.call(message, opts)
|
97
|
+
else block.call(self, message, opts)
|
84
98
|
end
|
85
99
|
|
86
100
|
r.is_a?(Array) && r.all? { |e| e.is_a?(Hash) } ? r : []
|
@@ -146,9 +160,10 @@ module Flor
|
|
146
160
|
# cnid: closure nid
|
147
161
|
# dbg: used to debug messages (useful @node['dbg'] when 'receive')
|
148
162
|
|
149
|
-
|
150
|
-
|
151
|
-
|
163
|
+
%w[ error cancel timeout ]
|
164
|
+
.each { |k|
|
165
|
+
h = message["on_#{k}_handler"]
|
166
|
+
node["on_#{k}"] = [ h ] if h }
|
152
167
|
|
153
168
|
@execution['nodes'][nid] = node
|
154
169
|
end
|
@@ -197,7 +212,8 @@ module Flor
|
|
197
212
|
#
|
198
213
|
# tag: et al
|
199
214
|
|
200
|
-
node['tree'] = message['tree'] = t =
|
215
|
+
node['tree'] = message['tree'] = t =
|
216
|
+
[ '_sqs', tree[0], tree[2] ]
|
201
217
|
|
202
218
|
node['heat0'] = t[0]
|
203
219
|
node['heat'] = h = n.deref(t[0])
|
@@ -207,14 +223,16 @@ module Flor
|
|
207
223
|
#
|
208
224
|
# rewrite `alpha` into `task alpha`
|
209
225
|
|
210
|
-
|
226
|
+
mt = message['tree']
|
227
|
+
l = mt[2]
|
211
228
|
|
212
|
-
message['otree'] =
|
229
|
+
message['otree'] = mt
|
213
230
|
|
214
|
-
|
215
|
-
'task'
|
216
|
-
|
217
|
-
|
231
|
+
node['tree'] = message['tree'] =
|
232
|
+
[ 'task', [
|
233
|
+
[ '_att', [ [ '_sqs', heat[1]['tasker'], l ] ], l ],
|
234
|
+
*mt[1]
|
235
|
+
], l ]
|
218
236
|
end
|
219
237
|
end
|
220
238
|
|
@@ -228,6 +246,19 @@ module Flor
|
|
228
246
|
heap = node['heap']
|
229
247
|
|
230
248
|
heac = Flor::Procedure[heap]
|
249
|
+
unless heac
|
250
|
+
puts "v" * 80
|
251
|
+
puts "===node:"
|
252
|
+
p node['nid']
|
253
|
+
p heap
|
254
|
+
puts "."
|
255
|
+
pp node
|
256
|
+
puts "===message:"
|
257
|
+
p message['point']
|
258
|
+
puts "."
|
259
|
+
pp message
|
260
|
+
puts "." * 80
|
261
|
+
end
|
231
262
|
fail NameError.new("unknown procedure #{heap.inspect}") unless heac
|
232
263
|
|
233
264
|
head = heac.new(self, node, message)
|
@@ -260,9 +291,8 @@ module Flor
|
|
260
291
|
|
261
292
|
def receive(message)
|
262
293
|
|
263
|
-
messages = leave_node(message)
|
264
|
-
|
265
294
|
nid = message['nid']
|
295
|
+
messages = leave_node(message)
|
266
296
|
|
267
297
|
return messages + toc_messages(message) unless nid
|
268
298
|
# 'terminated' or 'ceased'
|
@@ -292,7 +322,9 @@ module Flor
|
|
292
322
|
cls = node['closures']
|
293
323
|
|
294
324
|
pro = Flor::Procedure.make(self, node, message)
|
325
|
+
|
295
326
|
pro.end
|
327
|
+
# do end the node
|
296
328
|
|
297
329
|
cancels = pro.send(:wrap_cancel_children, 'cancel_trailing' => true)
|
298
330
|
|
@@ -353,12 +385,7 @@ module Flor
|
|
353
385
|
|
354
386
|
def error_reply(node, message, err)
|
355
387
|
|
356
|
-
m = message
|
357
|
-
.select { |k, v| %w[ sm exid nid from payload tree ].include?(k) }
|
358
|
-
|
359
|
-
m['point'] = 'failed'
|
360
|
-
m['fpoint'] = message['point']
|
361
|
-
m['error'] = Flor.to_error(err)
|
388
|
+
m = Flor.to_error_message(message, err)
|
362
389
|
|
363
390
|
@unit.logger.log_err(self, m, flag: true)
|
364
391
|
|
@@ -392,15 +419,14 @@ module Flor
|
|
392
419
|
|
393
420
|
last = (message['cause'] ||= [])[0]
|
394
421
|
|
395
|
-
c = {
|
396
|
-
|
397
|
-
'm' => message['m'],
|
398
|
-
'nid' => message['nid'],
|
399
|
-
'type' => message['type'],
|
400
|
-
'at' => last && last['at'] }
|
422
|
+
c = { 'cause' => cause, 'at' => last && last['at'] }
|
423
|
+
%w[ m sm nid type ].each { |k| c[k] = message[k] }
|
401
424
|
|
402
425
|
return if c == last
|
403
426
|
|
427
|
+
# argh, the causes in the messages go most recent first
|
428
|
+
# while the statuses in the nodes go most recent last
|
429
|
+
|
404
430
|
message['cause'] =
|
405
431
|
[ c.tap { |h| h['at'] = Flor.tstamp } ] +
|
406
432
|
message['cause']
|
@@ -421,12 +447,16 @@ module Flor
|
|
421
447
|
stack_cause(message) \
|
422
448
|
if %w[ trigger cancel ].include?(message['point'])
|
423
449
|
|
424
|
-
|
450
|
+
begin
|
451
|
+
determine_heat(message)
|
452
|
+
rescue => e
|
453
|
+
raise e unless message['point'] == 'failed'
|
454
|
+
end
|
425
455
|
|
426
456
|
ms = []
|
427
457
|
ms += @unit.notify(self, message) # pre
|
428
458
|
|
429
|
-
ms +=
|
459
|
+
ms += send(message['point'], message)
|
430
460
|
|
431
461
|
message['payload'] = message.delete('pld') if message.has_key?('pld')
|
432
462
|
message['consumed'] = Flor.tstamp
|
@@ -456,11 +486,6 @@ module Flor
|
|
456
486
|
[]
|
457
487
|
end
|
458
488
|
|
459
|
-
def entered(message); []; end
|
460
|
-
def left(message); []; end
|
461
|
-
|
462
|
-
def ceased(message); []; end
|
463
|
-
|
464
489
|
def terminated(message)
|
465
490
|
|
466
491
|
message['vars'] = @execution['nodes']['0']['vars']
|
@@ -496,6 +521,13 @@ module Flor
|
|
496
521
|
end
|
497
522
|
|
498
523
|
def signal(message); []; end
|
524
|
+
def entered(message); []; end
|
525
|
+
def left(message); []; end
|
526
|
+
def ceased(message); []; end
|
527
|
+
#
|
528
|
+
# Return an empty array of new messages. No direct effect.
|
529
|
+
#
|
530
|
+
# Some trap, hook, and/or waiter might lie in wait though.
|
499
531
|
|
500
532
|
def lookup_on_error_parent(message)
|
501
533
|
|