flor 0.16.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (164) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +44 -1
  3. data/CREDITS.md +2 -0
  4. data/LICENSE.txt +1 -1
  5. data/Makefile +2 -2
  6. data/README.md +115 -7
  7. data/flor.gemspec +8 -9
  8. data/lib/flor.rb +2 -2
  9. data/lib/flor/colours.rb +5 -3
  10. data/lib/flor/conf.rb +20 -6
  11. data/lib/flor/core.rb +9 -8
  12. data/lib/flor/core/executor.rb +70 -38
  13. data/lib/flor/core/node.rb +5 -17
  14. data/lib/flor/core/procedure.rb +97 -12
  15. data/lib/flor/core/texecutor.rb +9 -2
  16. data/lib/flor/djan.rb +6 -2
  17. data/lib/flor/errors.rb +1 -0
  18. data/lib/flor/flor.rb +233 -21
  19. data/lib/flor/id.rb +20 -0
  20. data/lib/flor/log.rb +8 -1
  21. data/lib/flor/migrations/0001_tables.rb +7 -6
  22. data/lib/flor/migrations/0002_cunit_and_munit.rb +1 -0
  23. data/lib/flor/migrations/0003_timer_onid_bnid.rb +1 -0
  24. data/lib/flor/migrations/0004_trap_bnid.rb +1 -0
  25. data/lib/flor/migrations/0005_pointer_content.rb +21 -0
  26. data/lib/flor/parser.rb +19 -11
  27. data/lib/flor/pcore/_apply.rb +104 -57
  28. data/lib/flor/pcore/_arr.rb +1 -0
  29. data/lib/flor/pcore/_atom.rb +1 -0
  30. data/lib/flor/pcore/_att.rb +16 -1
  31. data/lib/flor/pcore/_coll.rb +1 -0
  32. data/lib/flor/pcore/_dmute.rb +5 -1
  33. data/lib/flor/pcore/_dol.rb +1 -0
  34. data/lib/flor/pcore/_dqs.rb +1 -0
  35. data/lib/flor/pcore/_dump.rb +1 -0
  36. data/lib/flor/pcore/_err.rb +1 -0
  37. data/lib/flor/pcore/_head.rb +1 -0
  38. data/lib/flor/pcore/_obj.rb +1 -0
  39. data/lib/flor/pcore/_pat_.rb +1 -0
  40. data/lib/flor/pcore/_pat_arr.rb +1 -0
  41. data/lib/flor/pcore/_pat_guard.rb +1 -0
  42. data/lib/flor/pcore/_pat_obj.rb +1 -0
  43. data/lib/flor/pcore/_pat_or.rb +1 -0
  44. data/lib/flor/pcore/_pat_regex.rb +1 -0
  45. data/lib/flor/pcore/_ref.rb +3 -1
  46. data/lib/flor/pcore/_rxs.rb +1 -0
  47. data/lib/flor/pcore/_skip.rb +1 -0
  48. data/lib/flor/pcore/_val.rb +1 -0
  49. data/lib/flor/pcore/all.rb +1 -0
  50. data/lib/flor/pcore/andor.rb +1 -0
  51. data/lib/flor/pcore/any.rb +1 -0
  52. data/lib/flor/pcore/apply.rb +1 -0
  53. data/lib/flor/pcore/arith.rb +47 -9
  54. data/lib/flor/pcore/array_qmark.rb +1 -0
  55. data/lib/flor/pcore/break.rb +14 -2
  56. data/lib/flor/pcore/case.rb +42 -0
  57. data/lib/flor/pcore/cmp.rb +1 -0
  58. data/lib/flor/pcore/collect.rb +2 -1
  59. data/lib/flor/pcore/cond.rb +1 -0
  60. data/lib/flor/pcore/cursor.rb +46 -4
  61. data/lib/flor/pcore/define.rb +33 -6
  62. data/lib/flor/pcore/detect.rb +1 -0
  63. data/lib/flor/pcore/do_return.rb +1 -0
  64. data/lib/flor/pcore/each.rb +1 -0
  65. data/lib/flor/pcore/echo.rb +1 -0
  66. data/lib/flor/pcore/empty.rb +1 -0
  67. data/lib/flor/pcore/fail.rb +1 -0
  68. data/lib/flor/pcore/filter.rb +1 -0
  69. data/lib/flor/pcore/find.rb +1 -0
  70. data/lib/flor/pcore/flatten.rb +1 -0
  71. data/lib/flor/pcore/for_each.rb +1 -0
  72. data/lib/flor/pcore/if.rb +1 -0
  73. data/lib/flor/pcore/includes.rb +1 -0
  74. data/lib/flor/pcore/inject.rb +1 -0
  75. data/lib/flor/pcore/iterator.rb +13 -0
  76. data/lib/flor/pcore/keys.rb +1 -0
  77. data/lib/flor/pcore/length.rb +1 -0
  78. data/lib/flor/pcore/loop.rb +1 -0
  79. data/lib/flor/pcore/map.rb +1 -0
  80. data/lib/flor/pcore/match.rb +1 -0
  81. data/lib/flor/pcore/matchr.rb +1 -0
  82. data/lib/flor/pcore/max.rb +1 -0
  83. data/lib/flor/pcore/merge.rb +1 -0
  84. data/lib/flor/pcore/move.rb +1 -0
  85. data/lib/flor/pcore/noeval.rb +1 -0
  86. data/lib/flor/pcore/noret.rb +1 -0
  87. data/lib/flor/pcore/not.rb +1 -0
  88. data/lib/flor/pcore/on.rb +34 -9
  89. data/lib/flor/pcore/on_cancel.rb +2 -1
  90. data/lib/flor/pcore/on_error.rb +1 -0
  91. data/lib/flor/pcore/push.rb +1 -0
  92. data/lib/flor/pcore/rand.rb +1 -0
  93. data/lib/flor/pcore/range.rb +1 -0
  94. data/lib/flor/pcore/reduce.rb +1 -0
  95. data/lib/flor/pcore/return.rb +1 -0
  96. data/lib/flor/pcore/reverse.rb +1 -0
  97. data/lib/flor/pcore/select.rb +1 -0
  98. data/lib/flor/pcore/sequence.rb +49 -0
  99. data/lib/flor/pcore/set.rb +15 -4
  100. data/lib/flor/pcore/shuffle.rb +1 -0
  101. data/lib/flor/pcore/slice.rb +1 -0
  102. data/lib/flor/pcore/sort.rb +1 -0
  103. data/lib/flor/pcore/sort_by.rb +1 -0
  104. data/lib/flor/pcore/split.rb +1 -0
  105. data/lib/flor/pcore/stall.rb +1 -0
  106. data/lib/flor/pcore/strings.rb +1 -0
  107. data/lib/flor/pcore/timestamp.rb +1 -0
  108. data/lib/flor/pcore/to_array.rb +1 -0
  109. data/lib/flor/pcore/twig.rb +1 -0
  110. data/lib/flor/pcore/type_of.rb +1 -0
  111. data/lib/flor/pcore/until.rb +1 -0
  112. data/lib/flor/punit/abort.rb +50 -0
  113. data/lib/flor/punit/{ccollect.rb → c_collect.rb} +3 -2
  114. data/lib/flor/punit/c_each.rb +30 -0
  115. data/lib/flor/punit/c_for_each.rb +42 -0
  116. data/lib/flor/punit/c_iterator.rb +161 -0
  117. data/lib/flor/punit/c_map.rb +44 -0
  118. data/lib/flor/punit/cancel.rb +10 -1
  119. data/lib/flor/punit/concurrence.rb +44 -200
  120. data/lib/flor/punit/cron.rb +1 -0
  121. data/lib/flor/punit/do_trap.rb +1 -0
  122. data/lib/flor/punit/every.rb +1 -0
  123. data/lib/flor/punit/graft.rb +4 -2
  124. data/lib/flor/punit/m_ram.rb +282 -0
  125. data/lib/flor/punit/on_timeout.rb +1 -0
  126. data/lib/flor/punit/part.rb +1 -0
  127. data/lib/flor/punit/schedule.rb +1 -0
  128. data/lib/flor/punit/signal.rb +30 -5
  129. data/lib/flor/punit/sleep.rb +1 -0
  130. data/lib/flor/punit/task.rb +9 -2
  131. data/lib/flor/punit/trace.rb +1 -0
  132. data/lib/flor/punit/trap.rb +41 -1
  133. data/lib/flor/to_string.rb +1 -0
  134. data/lib/flor/tools/env.rb +1 -0
  135. data/lib/flor/tools/shell.rb +1 -0
  136. data/lib/flor/tools/shell_out.rb +1 -0
  137. data/lib/flor/tt.rb +98 -0
  138. data/lib/flor/unit.rb +3 -0
  139. data/lib/flor/unit/caller.rb +158 -20
  140. data/lib/flor/unit/caller_jruby.rb +132 -0
  141. data/lib/flor/unit/dump.rb +36 -0
  142. data/lib/flor/unit/executor.rb +27 -9
  143. data/lib/flor/unit/ganger.rb +27 -7
  144. data/lib/flor/unit/hloader.rb +278 -0
  145. data/lib/flor/unit/hook.rb +76 -14
  146. data/lib/flor/unit/hooker.rb +40 -26
  147. data/lib/flor/unit/journal.rb +23 -0
  148. data/lib/flor/unit/loader.rb +141 -22
  149. data/lib/flor/unit/logger.rb +24 -5
  150. data/lib/flor/unit/models.rb +96 -22
  151. data/lib/flor/unit/models/execution.rb +66 -4
  152. data/lib/flor/unit/models/message.rb +1 -0
  153. data/lib/flor/unit/models/pointer.rb +12 -0
  154. data/lib/flor/unit/models/timer.rb +1 -0
  155. data/lib/flor/unit/models/trace.rb +1 -0
  156. data/lib/flor/unit/models/trap.rb +15 -6
  157. data/lib/flor/unit/scheduler.rb +284 -46
  158. data/lib/flor/unit/spooler.rb +6 -3
  159. data/lib/flor/unit/storage.rb +108 -42
  160. data/lib/flor/unit/taskers.rb +47 -1
  161. data/lib/flor/unit/waiter.rb +166 -26
  162. data/lib/flor/unit/wlist.rb +107 -12
  163. metadata +31 -46
  164. data/lib/flor/punit/cmap.rb +0 -112
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: b973449e80aa849aee7d3fade7ab13fe57a0b6a1
4
- data.tar.gz: a09d4a0b88d453b42742bb8814d5cee1f957c2c2
2
+ SHA256:
3
+ metadata.gz: 7a1f76874a9957b7c15c59baf997e0e60ff4872b9109a7cd741692735a94565c
4
+ data.tar.gz: 7fd1552910609023dd8bce0de0d5b5fc1ebc5ae1674fc27f0d0cb932c4bab2a7
5
5
  SHA512:
6
- metadata.gz: 139a8cf68527edcdff12137a9ac831eee6fb9867a49ed9fe24d462efce7f3d53f9396de2302b449f963c1e74fe4e67b5590f04ad5605a6996bb35e6e0f7015ad
7
- data.tar.gz: 9de9e3bb4f12f2cb1b48ae0aa4d024ce4ce557b380d5b476c03dae7a7db63480ec041269702102a40165a3ed8ecb290d7b2c77299f999fdf605071976c2f99ea
6
+ metadata.gz: cbca621e72efc0bdf1df26a4554590032f7a42d76ca19b361305f519071f1f026678c55c2980e74661ebcd5ac018d9e3e892376ee69a07c91ebb1d2d809cbb75
7
+ data.tar.gz: b2c7cbe0a13c12f0e9b935c3d675f583d93a4f78f921982e75eb9050c84d97914c63cf9280667b5be977280cda9de27a57972f3f0d72b097710a6b3b1e45d67b
@@ -2,7 +2,50 @@
2
2
  # CHANGELOG.md
3
3
 
4
4
 
5
- ## flor 1.0.0 not yet released
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
@@ -3,6 +3,8 @@
3
3
 
4
4
  ## Contributors
5
5
 
6
+ * Olle Jonsson - https://github.com/olleolleolle
7
+ * Ryan Scott - https://github.com/Subtletree
6
8
  * Jeffrey Hicks - https://github.com/jrhicks
7
9
  * David Verrier - https://github.com/dverrier
8
10
  * Tsunehisa Doi - https://github.com/dmicky0419
@@ -1,5 +1,5 @@
1
1
 
2
- Copyright (c) 2015-2019, John Mettraux, jmettraux+flor@gmail.com
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 'doc' -e "make_procedures_doc()"
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
- ## Design
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
- See [quickstart/](quickstart/).
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
- ## Documentation
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
- * [the flor language](http://jmettraux.skepti.ch/20180927.html?t=the_flor_language) - on the flor workflow definition language itself
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
- ## LICENSE
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
 
@@ -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 = 'http://github.com/floraison'
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 'fugit', '~> 1.1', '>= 1.1.8'
45
- s.add_runtime_dependency 'dense', '~> 1.1', '>= 1.1.6'
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', '~> 4'
46
+ s.add_runtime_dependency 'sequel', '~> 5.0' # >= 5.0 and < 6
48
47
 
49
- s.add_development_dependency 'rspec', '~> 3.7'
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
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  require 'pp'
3
4
  require 'json'
@@ -14,8 +15,7 @@ require 'dense'
14
15
 
15
16
  module Flor
16
17
 
17
- VERSION = '0.16.0'
18
- #VERSION = '1.0.0'
18
+ VERSION = '1.0.0'
19
19
  end
20
20
 
21
21
  require 'flor/colours'
@@ -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 rs reset
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 + "" : "[#{v}m"
33
+ s ? "[#{v}m" + s + "" : "[#{v}m"
32
34
  end })
33
35
  else
34
36
  class_eval(
@@ -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 = conf
75
- c = Flor::ConfExecutor.interpret_path_or_source(c) if c.is_a?(String)
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
- if v = conf[key]
96
- Flor.const_lookup(v)
97
- else
98
- nil
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
 
@@ -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
- { 'point' => 'execute',
56
- 'exid' => exid,
57
- 'nid' => '0',
58
- 'tree' => t,
59
- 'payload' => pl,
60
- 'vars' => vs }
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)
@@ -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
- hook.notify(self, message)
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
- if block.arity == 1
79
- block.call(message)
80
- elsif block.arity == 2
81
- block.call(message, opts)
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
- if oeh = message['on_error_handler']
150
- node['on_error'] = [ [ [ '*' ], oeh ] ]
151
- end
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 = [ '_sqs', tree[0], tree[2] ]
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
- l = message['tree'][2]
226
+ mt = message['tree']
227
+ l = mt[2]
211
228
 
212
- message['otree'] = Flor.dup(message['tree'])
229
+ message['otree'] = mt
213
230
 
214
- message['tree'][0] =
215
- 'task'
216
- message['tree'][1].unshift(
217
- [ '_att', [ [ '_sqs', heat[1]['tasker'], l ] ], l ])
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
- 'cause' => cause,
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
- determine_heat(message)
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 += self.send(message['point'], message)
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