oflow 0.6.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|