oflow 0.3.0

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.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +182 -0
  4. data/lib/oflow/actor.rb +76 -0
  5. data/lib/oflow/actors/errorhandler.rb +32 -0
  6. data/lib/oflow/actors/ignore.rb +22 -0
  7. data/lib/oflow/actors/log.rb +175 -0
  8. data/lib/oflow/actors/relay.rb +23 -0
  9. data/lib/oflow/actors/timer.rb +126 -0
  10. data/lib/oflow/actors.rb +11 -0
  11. data/lib/oflow/box.rb +195 -0
  12. data/lib/oflow/env.rb +52 -0
  13. data/lib/oflow/errors.rb +74 -0
  14. data/lib/oflow/flow.rb +75 -0
  15. data/lib/oflow/haserrorhandler.rb +48 -0
  16. data/lib/oflow/haslinks.rb +64 -0
  17. data/lib/oflow/haslog.rb +72 -0
  18. data/lib/oflow/hasname.rb +31 -0
  19. data/lib/oflow/hastasks.rb +209 -0
  20. data/lib/oflow/inspector.rb +501 -0
  21. data/lib/oflow/link.rb +43 -0
  22. data/lib/oflow/pattern.rb +8 -0
  23. data/lib/oflow/stamp.rb +39 -0
  24. data/lib/oflow/task.rb +415 -0
  25. data/lib/oflow/test/action.rb +21 -0
  26. data/lib/oflow/test/actorwrap.rb +62 -0
  27. data/lib/oflow/test.rb +8 -0
  28. data/lib/oflow/tracker.rb +109 -0
  29. data/lib/oflow/version.rb +5 -0
  30. data/lib/oflow.rb +23 -0
  31. data/test/actors/log_test.rb +57 -0
  32. data/test/actors/timer_test.rb +56 -0
  33. data/test/actorwrap_test.rb +48 -0
  34. data/test/all_tests.rb +27 -0
  35. data/test/box_test.rb +127 -0
  36. data/test/collector.rb +23 -0
  37. data/test/flow_basic_test.rb +93 -0
  38. data/test/flow_cfg_error_test.rb +94 -0
  39. data/test/flow_log_test.rb +87 -0
  40. data/test/flow_nest_test.rb +215 -0
  41. data/test/flow_rescue_test.rb +133 -0
  42. data/test/flow_tracker_test.rb +82 -0
  43. data/test/stutter.rb +21 -0
  44. data/test/task_test.rb +98 -0
  45. data/test/tracker_test.rb +59 -0
  46. metadata +93 -0
@@ -0,0 +1,215 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ [ File.dirname(__FILE__),
5
+ File.join(File.dirname(__FILE__), "../lib")
6
+ ].each { |path| $: << path unless $:.include?(path) }
7
+
8
+ require 'test/unit'
9
+ require 'oflow'
10
+
11
+ require 'collector'
12
+
13
+ class Hop < ::OFlow::Actor
14
+
15
+ def initialize(task, options)
16
+ super
17
+ end
18
+
19
+ def perform(op, box)
20
+ task.info("#{op} #{box.contents}")
21
+ task.ship(op, box)
22
+ end
23
+
24
+ end # Hop
25
+
26
+ class FlowNestTest < ::Test::Unit::TestCase
27
+
28
+ def test_flow_nest
29
+ trigger = nil
30
+ collector = nil
31
+ ::OFlow::Env.flow(:nest, :opt1 => 1) { |f|
32
+ # use collector as the log
33
+ f.task(:log, Collector) { |t|
34
+ collector = t.actor
35
+ }
36
+
37
+ # starts off the process
38
+ trigger = f.task(:trigger, Hop) { |t|
39
+ t.link(nil, :deep, nil)
40
+ }
41
+ # a nested flow
42
+ f.flow(:deep) { |f2|
43
+ f2.route(nil, :one, nil)
44
+ f2.task(:one, Hop) { |t|
45
+ t.link(nil, :two, nil)
46
+ }
47
+ f2.task(:two, Hop) { |t|
48
+ t.link(nil, :flow, :bye)
49
+ }
50
+ f2.link(:bye, :out, nil)
51
+ }
52
+ f.task(:out, Hop) { |t|
53
+ t.link(nil, :done, nil)
54
+ }
55
+ f.task(:done, ::OFlow::Actors::Ignore)
56
+ }
57
+
58
+ # see if the flow was constructed correctly
59
+ assert_equal(%|OFlow::Env {
60
+ nest (OFlow::Flow) {
61
+ log (Collector) {
62
+ }
63
+ trigger (Hop) {
64
+ => deep:
65
+ }
66
+ deep (OFlow::Flow) {
67
+ one (Hop) {
68
+ => two:
69
+ }
70
+ two (Hop) {
71
+ => flow:bye
72
+ }
73
+ * one:
74
+ bye => out:
75
+ }
76
+ out (Hop) {
77
+ => done:
78
+ }
79
+ done (OFlow::Actors::Ignore) {
80
+ }
81
+ }
82
+ }|, ::OFlow::Env.describe())
83
+
84
+ # run it and check the output
85
+ trigger.receive(:go, ::OFlow::Box.new(7))
86
+ ::OFlow::Env.flush()
87
+ assert_equal([['go 7', ':nest:trigger'],
88
+ [' 7', ':nest:deep:one'],
89
+ [' 7', ':nest:deep:two'],
90
+ [' 7', ':nest:out']
91
+ ], collector.collection)
92
+
93
+ ::OFlow::Env.clear()
94
+ end
95
+
96
+ def test_flow_nest_deep
97
+ trigger = nil
98
+ collector = nil
99
+ ::OFlow::Env.flow(:nest_deep, :opt1 => 1) { |f|
100
+ # use collector as the log
101
+ f.task(:log, Collector) { |t|
102
+ collector = t.actor
103
+ }
104
+
105
+ # starts off the process
106
+ trigger = f.task(:trigger, Hop) { |t|
107
+ t.link(nil, :deep, nil)
108
+ }
109
+ # a nested flow
110
+ f.flow(:deep) { |f2|
111
+ f2.route(nil, :deeper, nil)
112
+ f2.flow(:deeper) { |f3|
113
+ f3.route(nil, :one, nil)
114
+ f3.task(:one, Hop) { |t|
115
+ t.link(nil, :two, nil)
116
+ }
117
+ f3.task(:two, Hop) { |t|
118
+ t.link(nil, :flow, :bye)
119
+ }
120
+ f3.link(:bye, :flow, :bye)
121
+ }
122
+ f2.link(:bye, :out, nil)
123
+ }
124
+ f.task(:out, Hop) { |t|
125
+ t.link(nil, :done, nil)
126
+ }
127
+ f.task(:done, ::OFlow::Actors::Ignore)
128
+ }
129
+
130
+ # see if the flow was constructed correctly
131
+ assert_equal(%|OFlow::Env {
132
+ nest_deep (OFlow::Flow) {
133
+ log (Collector) {
134
+ }
135
+ trigger (Hop) {
136
+ => deep:
137
+ }
138
+ deep (OFlow::Flow) {
139
+ deeper (OFlow::Flow) {
140
+ one (Hop) {
141
+ => two:
142
+ }
143
+ two (Hop) {
144
+ => flow:bye
145
+ }
146
+ * one:
147
+ bye => flow:bye
148
+ }
149
+ * deeper:
150
+ bye => out:
151
+ }
152
+ out (Hop) {
153
+ => done:
154
+ }
155
+ done (OFlow::Actors::Ignore) {
156
+ }
157
+ }
158
+ }|, ::OFlow::Env.describe())
159
+
160
+ # run it and check the output
161
+ trigger.receive(:go, ::OFlow::Box.new(7))
162
+ ::OFlow::Env.flush()
163
+ assert_equal([['go 7', ':nest_deep:trigger'],
164
+ [' 7', ':nest_deep:deep:deeper:one'],
165
+ [' 7', ':nest_deep:deep:deeper:two'],
166
+ [' 7', ':nest_deep:out']
167
+ ], collector.collection)
168
+
169
+ ::OFlow::Env.clear()
170
+ end
171
+
172
+ def test_flow_nest_label
173
+ trigger = nil
174
+ collector = nil
175
+ ::OFlow::Env.flow(:nest) { |f|
176
+ # use collector as the log
177
+ f.task(:log, Collector) { |t|
178
+ collector = t.actor
179
+ }
180
+
181
+ # starts off the process
182
+ trigger = f.task(:trigger, Hop) { |t|
183
+ t.link(:go, :deep, :first)
184
+ }
185
+ # a nested flow
186
+ f.flow(:deep) { |f2|
187
+ f2.route(:first, :one, :hip)
188
+ f2.task(:one, Hop) { |t|
189
+ t.link(:hip, :two, :hop)
190
+ }
191
+ f2.task(:two, Hop) { |t|
192
+ t.link(:hop, :flow, :get_out)
193
+ }
194
+ f2.link(:get_out, :out, :finish)
195
+ }
196
+ f.task(:out, Hop) { |t|
197
+ t.link(:finish, :done, nil)
198
+ }
199
+ f.task(:done, ::OFlow::Actors::Ignore)
200
+ }
201
+
202
+ # run it and check the output
203
+ trigger.receive(:go, ::OFlow::Box.new(7))
204
+ ::OFlow::Env.flush()
205
+ assert_equal([['go 7', ':nest:trigger'],
206
+ ['hip 7', ':nest:deep:one'],
207
+ ['hop 7', ':nest:deep:two'],
208
+ ['finish 7', ':nest:out']
209
+ ], collector.collection)
210
+
211
+ ::OFlow::Env.clear()
212
+ end
213
+
214
+
215
+ end # FlowNestTest
@@ -0,0 +1,133 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ [ File.dirname(__FILE__),
5
+ File.join(File.dirname(__FILE__), "../lib")
6
+ ].each { |path| $: << path unless $:.include?(path) }
7
+
8
+ require 'test/unit'
9
+ require 'oflow'
10
+
11
+ require 'collector'
12
+
13
+ class Crash < ::OFlow::Actor
14
+ def initialize(task, options)
15
+ super
16
+ end
17
+
18
+ def perform(op, box)
19
+ nil.crash()
20
+ end
21
+
22
+ end # Crash
23
+
24
+ class FlowRescueTest < ::Test::Unit::TestCase
25
+
26
+ # Make sure the error handler works and forwards to the 'error' task if it
27
+ # exists.
28
+ def test_flow_rescue_task
29
+ trigger = nil
30
+ collector = nil
31
+ ::OFlow::Env.flow('rescue') { |f|
32
+ trigger = f.task('crash', Crash)
33
+ f.task(:collector, Collector) { |t|
34
+ collector = t.actor
35
+ }
36
+ f.task(:error, ::OFlow::Actors::Relay) { |t|
37
+ t.link(nil, 'collector', 'error')
38
+ }
39
+ f.task(:log, ::OFlow::Actors::Relay) { |t|
40
+ t.link(nil, 'collector', 'log')
41
+ }
42
+ }
43
+ trigger.receive(:knock, ::OFlow::Box.new(7))
44
+ ::OFlow::Env.flush()
45
+
46
+ assert_equal(collector.collection.size, 1)
47
+ assert_equal(collector.collection[0][0].class, NoMethodError)
48
+ assert_equal(collector.collection[0][1], ':rescue:crash')
49
+
50
+ ::OFlow::Env.clear()
51
+ end
52
+
53
+ # Make sure the error handler on the flow is used to handle errors.
54
+ def test_flow_rescue_var
55
+ trigger = nil
56
+ collector = nil
57
+ ::OFlow::Env.flow('rescue') { |f|
58
+ trigger = f.task('crash', Crash)
59
+ f.error_handler = f.task(:collector, Collector) { |t|
60
+ collector = t.actor
61
+ }
62
+ f.task(:log, ::OFlow::Actors::Relay) { |t|
63
+ t.link(nil, 'collector', 'log')
64
+ }
65
+ }
66
+ trigger.receive(:knock, ::OFlow::Box.new(7))
67
+ ::OFlow::Env.flush()
68
+
69
+ assert_equal(collector.collection.size, 1)
70
+ assert_equal(collector.collection[0][0].class, NoMethodError)
71
+ assert_equal(collector.collection[0][1], ':rescue:crash')
72
+
73
+ ::OFlow::Env.clear()
74
+ end
75
+
76
+ # Make sure the error handler on the flow is used to handle errors.
77
+ def test_flow_rescue_env
78
+ trigger = nil
79
+ collector = nil
80
+ ::OFlow::Env.flow('rescue') { |f|
81
+ trigger = f.task('crash', Crash)
82
+ ::OFlow::Env.error_handler = f.task(:collector, Collector) { |t|
83
+ collector = t.actor
84
+ }
85
+ f.task(:log, ::OFlow::Actors::Relay) { |t|
86
+ t.link(nil, 'collector', 'log')
87
+ }
88
+ }
89
+ trigger.receive(:knock, ::OFlow::Box.new(7))
90
+ ::OFlow::Env.flush()
91
+
92
+ assert_equal(1, collector.collection.size)
93
+ assert_equal(NoMethodError, collector.collection[0][0].class)
94
+ assert_equal(':rescue:crash', collector.collection[0][1])
95
+
96
+ ::OFlow::Env.clear()
97
+ end
98
+
99
+ # Make sure the default error handler on the flow passes a message to the log.
100
+ def test_flow_rescue_env_log
101
+ trigger = nil
102
+ collector = nil
103
+ ::OFlow::Env.flow('rescue') { |f|
104
+ trigger = f.task('crash', Crash)
105
+ ::OFlow::Env.log = f.task(:collector, Collector) { |t|
106
+ collector = t.actor
107
+ }
108
+ }
109
+ trigger.receive(:knock, ::OFlow::Box.new(7))
110
+ ::OFlow::Env.flush()
111
+
112
+ assert_equal(1, collector.collection.size)
113
+ assert_equal(["NoMethodError: undefined method `crash' for nil:NilClass",
114
+ "/flow_rescue_test.rb:0:in `perform'",
115
+ "/task.rb:0:in `block in initialize'"],
116
+ simplify(collector.collection[0][0]))
117
+
118
+ assert_equal(':rescue:crash', collector.collection[0][1])
119
+
120
+ ::OFlow::Env.clear()
121
+ end
122
+
123
+ def simplify(bt)
124
+ bt.split("\n").map do |line|
125
+ i = line.index(/\/\w+\.rb\:\d+/)
126
+ unless i.nil?
127
+ line = line[i..-1]
128
+ end
129
+ line.gsub(/\d+/, '0')
130
+ end
131
+ end
132
+
133
+ end # FlowRescueTest
@@ -0,0 +1,82 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ [ File.dirname(__FILE__),
5
+ File.join(File.dirname(__FILE__), "../lib")
6
+ ].each { |path| $: << path unless $:.include?(path) }
7
+
8
+ require 'test/unit'
9
+ require 'oflow'
10
+
11
+ require 'collector'
12
+
13
+ class Throw < ::OFlow::Actor
14
+
15
+ def initialize(task, options)
16
+ super
17
+ end
18
+
19
+ def perform(op, box)
20
+ task.ship(op, box)
21
+ end
22
+
23
+ end # Throw
24
+
25
+ class Catch < ::OFlow::Actor
26
+ attr_reader :ball
27
+
28
+ def initialize(task, options)
29
+ super
30
+ @ball = nil
31
+ end
32
+
33
+ def perform(op, box)
34
+ @ball = box
35
+ end
36
+
37
+ end # Catch
38
+
39
+ class FlowTrackerTest < ::Test::Unit::TestCase
40
+
41
+ def test_flow_tracker
42
+ trigger = nil
43
+ catcher = nil
44
+ ::OFlow::Env.flow(:nest, :opt1 => 1) { |f|
45
+
46
+ # starts off the process
47
+ trigger = f.task(:trigger, Throw) { |t|
48
+ t.link(nil, :deep, nil)
49
+ }
50
+ # a nested flow
51
+ f.flow(:deep) { |f2|
52
+ f2.route(nil, :one, nil)
53
+ f2.task(:one, Throw) { |t|
54
+ t.link(nil, :two, nil)
55
+ }
56
+ f2.task(:two, Throw) { |t|
57
+ t.link(nil, :flow, :bye)
58
+ }
59
+ f2.link(:bye, :out, nil)
60
+ }
61
+ f.task(:out, Throw) { |t|
62
+ t.link(nil, :done, nil)
63
+ }
64
+ catcher = f.task(:done, Catch)
65
+ }
66
+
67
+ # run it and check the output
68
+ trigger.receive(:go, ::OFlow::Box.new(7, ::OFlow::Tracker.create('test')))
69
+ ::OFlow::Env.flush()
70
+ assert_equal(["test-",
71
+ ":nest:trigger-go",
72
+ ":nest:deep-",
73
+ ":nest:deep:one-",
74
+ ":nest:deep:two-",
75
+ ":nest:deep-bye",
76
+ ":nest:out-",
77
+ ":nest:done-"], catcher.actor.ball.tracker.track.map {|s| s.where })
78
+
79
+ ::OFlow::Env.clear()
80
+ end
81
+
82
+ end # FlowTrackerTest
data/test/stutter.rb ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ [ File.dirname(__FILE__),
5
+ File.join(File.dirname(__FILE__), "../lib")
6
+ ].each { |path| $: << path unless $:.include?(path) }
7
+
8
+ require 'oflow'
9
+
10
+ class Stutter < ::OFlow::Actor
11
+
12
+ def initialize(task, options)
13
+ super
14
+ end
15
+
16
+ def perform(op, box)
17
+ task.ship(:collector, ::OFlow::Box.new([task.full_name, op, box.contents]))
18
+ task.ship(op, box)
19
+ end
20
+
21
+ end # Stutter
data/test/task_test.rb ADDED
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ [ File.dirname(__FILE__),
5
+ File.join(File.dirname(__FILE__), "../lib")
6
+ ].each { |path| $: << path unless $:.include?(path) }
7
+
8
+ require 'test/unit'
9
+ require 'oflow'
10
+
11
+ class Gather < ::OFlow::Actor
12
+ attr_accessor :requests
13
+
14
+ def initialize(task, options)
15
+ super
16
+ @requests = {}
17
+ end
18
+
19
+ def perform(op, box)
20
+ if @requests[op].nil?
21
+ @requests[op] = [box]
22
+ else
23
+ @requests[op] << box
24
+ end
25
+ end
26
+
27
+ end # Gather
28
+
29
+
30
+ class TaskTest < ::Test::Unit::TestCase
31
+
32
+ def test_task_queue_count
33
+ task = ::OFlow::Task.new(nil, 'test', Gather)
34
+ assert_equal(0, task.queue_count())
35
+ task.stop()
36
+ task.receive(:dance, ::OFlow::Box.new('two step'))
37
+ assert_equal(1, task.queue_count())
38
+ task.receive(:dance, ::OFlow::Box.new('twist'))
39
+ assert_equal(2, task.queue_count())
40
+ task.shutdown()
41
+ end
42
+
43
+ def test_task_perform
44
+ task = ::OFlow::Task.new(nil, 'test', Gather)
45
+ task.receive(:dance, ::OFlow::Box.new('two step'))
46
+ task.flush()
47
+
48
+ requests = task.actor.requests
49
+ assert_equal(1, requests.size)
50
+ boxes = requests[:dance]
51
+ assert_equal(1, boxes.size)
52
+ box = boxes[0]
53
+ assert_equal(false, box.nil?)
54
+ assert_equal('two step', box.contents)
55
+ task.shutdown()
56
+ end
57
+
58
+ def test_task_perform_shutdown
59
+ task = ::OFlow::Task.new(nil, 'test', Gather)
60
+ task.receive(:dance, ::OFlow::Box.new('two step'))
61
+ task.shutdown(true)
62
+
63
+ requests = task.actor.requests
64
+ assert_equal(1, requests.size)
65
+ boxes = requests[:dance]
66
+ assert_equal(1, boxes.size)
67
+ box = boxes[0]
68
+ assert_equal(false, box.nil?)
69
+ assert_equal('two step', box.contents)
70
+ end
71
+
72
+ def test_task_raise_after_close
73
+ task = ::OFlow::Task.new(nil, 'test', Gather)
74
+ task.shutdown()
75
+ assert_raise(ThreadError) { task.start() }
76
+ end
77
+
78
+ def test_task_max_queue_count
79
+ task = ::OFlow::Task.new(nil, 'test', Gather, :max_queue_count => 4, :req_timeout => 0.1)
80
+ task.stop()
81
+ 6.times do |i|
82
+ begin
83
+ task.receive(:dance, ::OFlow::Box.new(i))
84
+ rescue ::OFlow::BusyError
85
+ # expected for all over first 4
86
+ end
87
+ end
88
+ task.start()
89
+ task.shutdown(true)
90
+
91
+ requests = task.actor.requests
92
+ boxes = requests[:dance]
93
+ assert_equal(4, boxes.size)
94
+ nums = boxes.map { |box| box.contents }
95
+ assert_equal([0, 1, 2, 3], nums)
96
+ end
97
+
98
+ end # TaskTest
@@ -0,0 +1,59 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: UTF-8
3
+
4
+ # Ubuntu does not accept arguments to ruby when called using env. To get warnings to show up the -w options is
5
+ # required. That can be set in the RUBYOPT environment variable.
6
+ # export RUBYOPT=-w
7
+
8
+ $VERBOSE = true
9
+
10
+ $: << File.join(File.dirname(__FILE__), "../lib")
11
+
12
+ require 'test/unit'
13
+ require 'oflow'
14
+
15
+ class TrackerTest < ::Test::Unit::TestCase
16
+
17
+ def test_tracker_new
18
+ t = ::OFlow::Tracker.create('here')
19
+ t2 = ::OFlow::Tracker.create('here')
20
+
21
+ assert_not_equal(t.id, t2.id, 'id must be unique')
22
+ assert_equal('here', t.track[0].location)
23
+ end
24
+
25
+ def test_tracker_track
26
+ t = ::OFlow::Tracker.create('here')
27
+ t2 = t.receive('there', 'op1')
28
+
29
+ assert_equal('here', t.track[0].location)
30
+ assert_equal('here', t2.track[0].location)
31
+ assert_equal('there', t2.track[1].location)
32
+ end
33
+
34
+ def test_tracker_merge
35
+ t = ::OFlow::Tracker.create('here')
36
+ # 2 different paths
37
+ t2 = t.receive('there', 'op1')
38
+ t3 = t.receive('everywhere', 'op2')
39
+ # should not happen but should handle merging when not back to a common place
40
+ t4 = t2.merge(t3)
41
+ assert_equal('here', t4.track[0].location)
42
+ assert_equal(true, t4.track[1].is_a?(Array))
43
+ assert_equal(2, t4.track[1].size)
44
+ assert_equal('there', t4.track[1][0][0].location)
45
+ assert_equal('everywhere', t4.track[1][1][0].location)
46
+
47
+ # back to a common location
48
+ t2 = t2.receive('home', 'op1')
49
+ t3 = t3.receive('home', 'op1')
50
+ t4 = t2.merge(t3)
51
+ assert_equal('here', t4.track[0].location)
52
+ assert_equal(true, t4.track[1].is_a?(Array))
53
+ assert_equal(2, t4.track[1].size)
54
+ assert_equal('there', t4.track[1][0][0].location)
55
+ assert_equal('everywhere', t4.track[1][1][0].location)
56
+ assert_equal('home', t4.track[2].location)
57
+ end
58
+
59
+ end # TrackerTest
metadata ADDED
@@ -0,0 +1,93 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oflow
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Peter Ohler
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-04 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Operations Workflow in Ruby. This implements a workflow/process flow
14
+ using multiple task nodes that each have their own queues and execution thread.
15
+ email: peter@ohler.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files:
19
+ - README.md
20
+ files:
21
+ - LICENSE
22
+ - README.md
23
+ - lib/oflow.rb
24
+ - lib/oflow/actor.rb
25
+ - lib/oflow/actors.rb
26
+ - lib/oflow/actors/errorhandler.rb
27
+ - lib/oflow/actors/ignore.rb
28
+ - lib/oflow/actors/log.rb
29
+ - lib/oflow/actors/relay.rb
30
+ - lib/oflow/actors/timer.rb
31
+ - lib/oflow/box.rb
32
+ - lib/oflow/env.rb
33
+ - lib/oflow/errors.rb
34
+ - lib/oflow/flow.rb
35
+ - lib/oflow/haserrorhandler.rb
36
+ - lib/oflow/haslinks.rb
37
+ - lib/oflow/haslog.rb
38
+ - lib/oflow/hasname.rb
39
+ - lib/oflow/hastasks.rb
40
+ - lib/oflow/inspector.rb
41
+ - lib/oflow/link.rb
42
+ - lib/oflow/pattern.rb
43
+ - lib/oflow/stamp.rb
44
+ - lib/oflow/task.rb
45
+ - lib/oflow/test.rb
46
+ - lib/oflow/test/action.rb
47
+ - lib/oflow/test/actorwrap.rb
48
+ - lib/oflow/tracker.rb
49
+ - lib/oflow/version.rb
50
+ - test/actors/log_test.rb
51
+ - test/actors/timer_test.rb
52
+ - test/actorwrap_test.rb
53
+ - test/all_tests.rb
54
+ - test/box_test.rb
55
+ - test/collector.rb
56
+ - test/flow_basic_test.rb
57
+ - test/flow_cfg_error_test.rb
58
+ - test/flow_log_test.rb
59
+ - test/flow_nest_test.rb
60
+ - test/flow_rescue_test.rb
61
+ - test/flow_tracker_test.rb
62
+ - test/stutter.rb
63
+ - test/task_test.rb
64
+ - test/tracker_test.rb
65
+ homepage: http://www.ohler.com/oflow
66
+ licenses:
67
+ - MIT
68
+ - GPL-3.0
69
+ metadata: {}
70
+ post_install_message:
71
+ rdoc_options:
72
+ - "--main"
73
+ - README.md
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: '0'
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ requirements: []
87
+ rubyforge_project: oflow
88
+ rubygems_version: 2.2.0
89
+ signing_key:
90
+ specification_version: 4
91
+ summary: Operations Workflow in Ruby
92
+ test_files: []
93
+ has_rdoc: true