oflow 0.6.0 → 0.8.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.
- checksums.yaml +4 -4
- data/README.md +23 -41
- data/lib/oflow.rb +2 -3
- data/lib/oflow/actor.rb +3 -0
- data/lib/oflow/actors/httpserver.rb +3 -1
- data/lib/oflow/actors/log.rb +3 -2
- data/lib/oflow/actors/persister.rb +29 -6
- data/lib/oflow/actors/timer.rb +29 -12
- data/lib/oflow/box.rb +2 -2
- data/lib/oflow/env.rb +221 -15
- data/lib/oflow/flow.rb +217 -37
- data/lib/oflow/graffle.rb +293 -0
- data/lib/oflow/haserrorhandler.rb +3 -22
- data/lib/oflow/haslog.rb +21 -15
- data/lib/oflow/inspector.rb +18 -17
- data/lib/oflow/link.rb +11 -6
- data/lib/oflow/task.rb +134 -22
- data/lib/oflow/test/actorwrap.rb +1 -1
- data/lib/oflow/version.rb +1 -1
- data/test/actors/balancer_test.rb +17 -12
- data/test/actors/httpserver_test.rb +11 -10
- data/test/actors/log_test.rb +3 -6
- data/test/actors/merger_test.rb +23 -18
- data/test/actors/persister_test.rb +6 -8
- data/test/actors/timer_test.rb +63 -35
- data/test/actorwrap_test.rb +2 -6
- data/test/all_tests.rb +3 -7
- data/test/box_test.rb +4 -10
- data/test/flow_basic_test.rb +24 -22
- data/test/flow_cfg_error_test.rb +17 -13
- data/test/flow_linked_test.rb +146 -0
- data/test/flow_log_test.rb +43 -29
- data/test/flow_rescue_test.rb +41 -27
- data/test/flow_tracker_test.rb +26 -30
- data/test/helper.rb +15 -0
- data/test/task_test.rb +3 -7
- data/test/tracker_test.rb +3 -11
- metadata +5 -7
- data/lib/oflow/haslinks.rb +0 -68
- data/lib/oflow/hasname.rb +0 -31
- data/lib/oflow/hastasks.rb +0 -214
- data/test/flow_nest_test.rb +0 -215
data/lib/oflow/hastasks.rb
DELETED
@@ -1,214 +0,0 @@
|
|
1
|
-
|
2
|
-
module OFlow
|
3
|
-
|
4
|
-
# Provides the ability to have Tasks and Flows.
|
5
|
-
module HasTasks
|
6
|
-
|
7
|
-
# Initializes the tasks attribute.
|
8
|
-
def init_tasks()
|
9
|
-
@tasks = {}
|
10
|
-
end
|
11
|
-
|
12
|
-
# Creates a Flow and yield to a block with the newly create Flow. Used to
|
13
|
-
# contruct Flows.
|
14
|
-
# @param name [Symbol|String] base name for the Flow
|
15
|
-
# @param options [Hash] optional parameters
|
16
|
-
# @param block [Proc] block to yield to with the new Flow instance
|
17
|
-
# @return [Flow] new Flow
|
18
|
-
def flow(name, options={}, &block)
|
19
|
-
f = Flow.new(self, name, options)
|
20
|
-
@tasks[f.name] = f
|
21
|
-
yield(f) if block_given?
|
22
|
-
f.resolve_all_links()
|
23
|
-
# Wait to validate until at the top so up-links don't fail validation.
|
24
|
-
if Env == self
|
25
|
-
f.validate()
|
26
|
-
f.start()
|
27
|
-
end
|
28
|
-
f
|
29
|
-
end
|
30
|
-
|
31
|
-
# Creates a Task and yield to a block with the newly create Task. Used to
|
32
|
-
# configure Tasks.
|
33
|
-
# @param name [Symbol|String] base name for the Task
|
34
|
-
# @param actor_class [Class] Class to create an Actor instance of
|
35
|
-
# @param options [Hash] optional parameters
|
36
|
-
# @param block [Proc] block to yield to with the new Task instance
|
37
|
-
# @return [Task] new Task
|
38
|
-
def task(name, actor_class, options={}, &block)
|
39
|
-
has_state = options.has_key?(:state)
|
40
|
-
options[:state] = Task::STOPPED unless has_state
|
41
|
-
t = Task.new(self, name, actor_class, options)
|
42
|
-
@tasks[t.name] = t
|
43
|
-
yield(t) if block_given?
|
44
|
-
t
|
45
|
-
end
|
46
|
-
|
47
|
-
# Validates the container by verifying all links on a task have been set to
|
48
|
-
# a valid destination and that destination has been resolved.
|
49
|
-
# @raise [ValidateError] if there is an error in validation
|
50
|
-
def validate()
|
51
|
-
# collects errors and raises all errors at once if there are any
|
52
|
-
errors = _validation_errors()
|
53
|
-
raise ValidateError.new(errors) unless errors.empty?
|
54
|
-
end
|
55
|
-
|
56
|
-
# Returns an Array of validation errors.
|
57
|
-
def _validation_errors()
|
58
|
-
errors = []
|
59
|
-
@tasks.each_value { |t| errors += t._validation_errors() }
|
60
|
-
errors
|
61
|
-
end
|
62
|
-
|
63
|
-
# Resolves all the Links on all the Tasks and Flows being managed as well as
|
64
|
-
# any Links in the instance itself.
|
65
|
-
def resolve_all_links()
|
66
|
-
@links.each_value { |lnk|
|
67
|
-
set_link_target(lnk) if lnk.target.nil?
|
68
|
-
}
|
69
|
-
@tasks.each_value { |t|
|
70
|
-
t.resolve_all_links()
|
71
|
-
}
|
72
|
-
end
|
73
|
-
|
74
|
-
# Iterates over each Task and yields to the provided block with each Task.
|
75
|
-
# @param blk [Proc] Proc to call on each iteration
|
76
|
-
def each_task(&blk)
|
77
|
-
@tasks.each { |name,task| blk.yield(task) }
|
78
|
-
end
|
79
|
-
|
80
|
-
# Performs a recursive walk over all Tasks and yields to the provided block
|
81
|
-
# for each. Flows are followed recusively.
|
82
|
-
# @param tasks_only [true|false] indicates on Tasks and not Flows are yielded to
|
83
|
-
# @param blk [Proc] Proc to call on each iteration
|
84
|
-
def walk_tasks(tasks_only=true, &blk)
|
85
|
-
@tasks.each_value do |t|
|
86
|
-
if t.is_a?(Task)
|
87
|
-
blk.yield(t)
|
88
|
-
else
|
89
|
-
blk.yield(t) unless tasks_only
|
90
|
-
t.walk_tasks(tasks_only, &blk)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
# Locates and return a Task with the specified name.
|
96
|
-
# @param name [String] name of the Task
|
97
|
-
# @return [Task|nil] the Task with the name specified or nil
|
98
|
-
def find_task(name)
|
99
|
-
name = name.to_sym unless name.nil?
|
100
|
-
return self if :flow == name
|
101
|
-
@tasks[name]
|
102
|
-
end
|
103
|
-
|
104
|
-
# Locates and return a Task with the specified full name.
|
105
|
-
# @param name [String] full name of the Task
|
106
|
-
# @return [Task|nil] the Task with the name specified or nil
|
107
|
-
def locate(name)
|
108
|
-
name = name[1..-1] if name.start_with?(':')
|
109
|
-
name = name[0..-2] if name.end_with?(':')
|
110
|
-
path = name.split(':')
|
111
|
-
_locate(path)
|
112
|
-
end
|
113
|
-
|
114
|
-
def _locate(path)
|
115
|
-
t = @tasks[path[0].to_sym]
|
116
|
-
return t if t.nil? || 1 == path.size
|
117
|
-
t._locate(path[1..-1])
|
118
|
-
end
|
119
|
-
|
120
|
-
# Returns the number of active Tasks.
|
121
|
-
def task_count()
|
122
|
-
@tasks.size
|
123
|
-
end
|
124
|
-
|
125
|
-
# Returns the sum of all the requests in all the Tasks's queues.
|
126
|
-
# @return [Fixnum] total number of items waiting to be processed
|
127
|
-
def queue_count()
|
128
|
-
cnt = 0
|
129
|
-
@tasks.each_value { |task| cnt += task.queue_count() }
|
130
|
-
cnt
|
131
|
-
end
|
132
|
-
|
133
|
-
# Returns true of one or more Tasks is either processing a request or has a
|
134
|
-
# request waiting to be processed on it's input queue.
|
135
|
-
# @return [true|false] the busy state across all Tasks
|
136
|
-
def busy?
|
137
|
-
@tasks.each_value { |task| return true if task.busy? }
|
138
|
-
false
|
139
|
-
end
|
140
|
-
|
141
|
-
# Calls the stop() method on all Tasks.
|
142
|
-
def stop()
|
143
|
-
@tasks.each_value { |task| task.stop() }
|
144
|
-
end
|
145
|
-
|
146
|
-
# Calls the step() method one Task that is stopped and has an item in the
|
147
|
-
# queue. The Tasks with the highest backed_up() value is selected.
|
148
|
-
def step()
|
149
|
-
max = 0.0
|
150
|
-
best = nil
|
151
|
-
walk_tasks() do |t|
|
152
|
-
if Task::STOPPED == t.state
|
153
|
-
bu = t.backed_up()
|
154
|
-
if max < bu
|
155
|
-
best = t
|
156
|
-
max = bu
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
160
|
-
best.step() unless best.nil?
|
161
|
-
best
|
162
|
-
end
|
163
|
-
|
164
|
-
# Calls the start() method on all Tasks.
|
165
|
-
def start()
|
166
|
-
@tasks.each_value { |task| task.start() }
|
167
|
-
end
|
168
|
-
|
169
|
-
# Wakes up all the Tasks in the Flow.
|
170
|
-
def wakeup()
|
171
|
-
@tasks.each_value { |t| t.wakeup() }
|
172
|
-
end
|
173
|
-
|
174
|
-
# Wakes up all the Tasks in the Flow and waits for the system to become idle
|
175
|
-
# before returning.
|
176
|
-
def flush()
|
177
|
-
wakeup()
|
178
|
-
@tasks.each_value { |t| t.flush() }
|
179
|
-
while busy?
|
180
|
-
sleep(0.2)
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
# Sets the state of all Tasks recursively. This should not be called
|
185
|
-
# directly.
|
186
|
-
def state=(s)
|
187
|
-
@tasks.each_value do |task|
|
188
|
-
task.state = s
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
# Shuts down all Tasks.
|
193
|
-
# @param flush_first [true|false] flag indicating shutdown should occur after the system becomes idle
|
194
|
-
def shutdown(flush_first=false)
|
195
|
-
# block all tasks first so threads can empty queues
|
196
|
-
@tasks.each_value do |task|
|
197
|
-
task.state = Task::BLOCKED
|
198
|
-
end
|
199
|
-
# shutdown and wait for queues to empty if necessary
|
200
|
-
@tasks.each_value do |task|
|
201
|
-
task.shutdown(flush_first)
|
202
|
-
end
|
203
|
-
@tasks = {}
|
204
|
-
end
|
205
|
-
|
206
|
-
# Clears out all Tasks and Flows and resets the object back to a empty state.
|
207
|
-
def clear()
|
208
|
-
shutdown()
|
209
|
-
@tasks = {}
|
210
|
-
_clear()
|
211
|
-
end
|
212
|
-
|
213
|
-
end # HasTasks
|
214
|
-
end # OFlow
|
data/test/flow_nest_test.rb
DELETED
@@ -1,215 +0,0 @@
|
|
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.warn("#{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
|