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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 41a62977484dfde369899d13d67f34f41e88d643
|
4
|
+
data.tar.gz: 80f9dc4d7fb42859bfd4b292393b53857ac418b8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a32921aa1cfae8f6053f571d5b9fad0bf473f2365f46b36f12ff5866ea913e8836d6f5463b46f7fe05cdc839dd93455f985aabaccc2ae1ecf85455f84353beb0
|
7
|
+
data.tar.gz: 04ad4e9db6a86bb6ea02ab1f31f405f8abcb34c7af516e3598c0549414a5fbb704224eb91935a635884daad0837e1e3a8eb99805c0f074a54946fc31b818052e
|
data/README.md
CHANGED
@@ -25,6 +25,18 @@ Follow [@peterohler on Twitter](http://twitter.com/#!/peterohler) for announceme
|
|
25
25
|
|
26
26
|
## Release Notes
|
27
27
|
|
28
|
+
### Next Release 0.8
|
29
|
+
|
30
|
+
- A somewhat non trivial example. GemChart collects statistics on my gems and
|
31
|
+
stores those statistics. It also provides a web interface to view the
|
32
|
+
statistics as a graph.
|
33
|
+
|
34
|
+
### Current Release 0.7
|
35
|
+
|
36
|
+
- Simplified the APIs and structure.
|
37
|
+
|
38
|
+
- Added OmniGraffle support. Diagrams can now be executed.
|
39
|
+
|
28
40
|
### Release 0.6
|
29
41
|
|
30
42
|
- Added HTTP Server Actor that acts as a simple HTTP server.
|
@@ -95,19 +107,23 @@ end
|
|
95
107
|
Next build the flow using Ruby code.
|
96
108
|
|
97
109
|
```ruby
|
110
|
+
env = ::OFlow::Env.new('')
|
111
|
+
|
98
112
|
def hello_flow(period)
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
}
|
103
|
-
f.task(:hello, HelloWorld)
|
113
|
+
env.flow('hello_world') { |f|
|
114
|
+
f.task(:repeater, ::OFlow::Actors::Timer, repeat: 3, period: period) { |t|
|
115
|
+
t.link(nil, :hello, nil)
|
104
116
|
}
|
117
|
+
f.task(:hello, HelloWorld)
|
118
|
+
}
|
119
|
+
env.prepare()
|
120
|
+
env.start()
|
105
121
|
end
|
106
122
|
|
107
123
|
hello_flow(1.0)
|
108
124
|
|
109
125
|
if $0 == __FILE__
|
110
|
-
|
126
|
+
env.flush()
|
111
127
|
end
|
112
128
|
```
|
113
129
|
|
@@ -123,10 +139,6 @@ Hello World!
|
|
123
139
|
|
124
140
|
## Future Features
|
125
141
|
|
126
|
-
- HTTP Server Actor
|
127
|
-
|
128
|
-
- OmniGraffle file input for configuration.
|
129
|
-
|
130
142
|
- .svg file input for configuration.
|
131
143
|
|
132
144
|
- Visio file input for configuration.
|
@@ -141,7 +153,7 @@ Hello World!
|
|
141
153
|
around 10M operations per second where an operation is one task execution per
|
142
154
|
thread.
|
143
155
|
|
144
|
-
- HTTP based inpector.
|
156
|
+
- HTTP/Websockets based inpector.
|
145
157
|
|
146
158
|
# Links
|
147
159
|
|
@@ -162,33 +174,3 @@ Hello World!
|
|
162
174
|
[Oj Object Mode Performance](http://www.ohler.com/dev/oj_misc/performance_object.html) compares Oj object mode parser performance to other marshallers.
|
163
175
|
|
164
176
|
[Oj Callback Performance](http://www.ohler.com/dev/oj_misc/performance_callback.html) compares Oj callback parser performance to other JSON parsers.
|
165
|
-
|
166
|
-
### License:
|
167
|
-
|
168
|
-
Copyright (c) 2014, Peter Ohler
|
169
|
-
All rights reserved.
|
170
|
-
|
171
|
-
Redistribution and use in source and binary forms, with or without
|
172
|
-
modification, are permitted provided that the following conditions are met:
|
173
|
-
|
174
|
-
- Redistributions of source code must retain the above copyright notice, this
|
175
|
-
list of conditions and the following disclaimer.
|
176
|
-
|
177
|
-
- Redistributions in binary form must reproduce the above copyright notice,
|
178
|
-
this list of conditions and the following disclaimer in the documentation
|
179
|
-
and/or other materials provided with the distribution.
|
180
|
-
|
181
|
-
- Neither the name of Peter Ohler nor the names of its contributors may be
|
182
|
-
used to endorse or promote products derived from this software without
|
183
|
-
specific prior written permission.
|
184
|
-
|
185
|
-
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
186
|
-
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
187
|
-
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
188
|
-
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
189
|
-
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
190
|
-
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
191
|
-
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
192
|
-
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
193
|
-
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
194
|
-
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
data/lib/oflow.rb
CHANGED
@@ -4,9 +4,6 @@ end
|
|
4
4
|
|
5
5
|
require 'oflow/errors'
|
6
6
|
require 'oflow/stamp'
|
7
|
-
require 'oflow/hasname'
|
8
|
-
require 'oflow/hastasks'
|
9
|
-
require 'oflow/haslinks'
|
10
7
|
require 'oflow/haserrorhandler'
|
11
8
|
require 'oflow/haslog'
|
12
9
|
require 'oflow/pattern'
|
@@ -19,5 +16,7 @@ require 'oflow/flow'
|
|
19
16
|
|
20
17
|
require 'oflow/actors'
|
21
18
|
|
19
|
+
require 'oflow/graffle'
|
20
|
+
|
22
21
|
require 'oflow/env'
|
23
22
|
require 'oflow/inspector'
|
data/lib/oflow/actor.rb
CHANGED
@@ -28,7 +28,8 @@ module OFlow
|
|
28
28
|
session = @server.accept_nonblock()
|
29
29
|
session.fcntl(Fcntl::F_SETFL, session.fcntl(Fcntl::F_GETFL, 0) | Fcntl::O_NONBLOCK)
|
30
30
|
@count += 1
|
31
|
-
|
31
|
+
# if nil is returned the request was empty
|
32
|
+
next if (req = read_req(session, @count)).nil?
|
32
33
|
@sessions[@count] = session
|
33
34
|
resp = {
|
34
35
|
status: 200,
|
@@ -106,6 +107,7 @@ module OFlow
|
|
106
107
|
id: id,
|
107
108
|
}
|
108
109
|
line = session.gets()
|
110
|
+
return if line.nil?
|
109
111
|
parts = line.split(' ')
|
110
112
|
req[:method] = parts[0]
|
111
113
|
req[:protocol] = parts[2]
|
data/lib/oflow/actors/log.rb
CHANGED
@@ -78,6 +78,7 @@ module OFlow
|
|
78
78
|
# @option options [String|Fixnum] :severity initial setting for severity
|
79
79
|
# @option options [Proc] :formatter initial setting for the formatter procedure
|
80
80
|
def set_options(options)
|
81
|
+
@formatter = options.fetch(:formatter, nil)
|
81
82
|
if !(filename = options[:filename]).nil?
|
82
83
|
max_file_size = options.fetch(:max_file_size, options.fetch(:shift_size, 1048576))
|
83
84
|
max_file_count = options.fetch(:max_file_count, options.fetch(:shift_age, 7))
|
@@ -86,9 +87,9 @@ module OFlow
|
|
86
87
|
@logger = Logger.new(stream)
|
87
88
|
else
|
88
89
|
@logger = Logger.new(STDOUT)
|
90
|
+
@formatter = proc { |s,t,p,m| "#{s[0]} #{p}> #{m}\n" } if @formatter.nil?
|
89
91
|
end
|
90
92
|
@logger.level = options.fetch(:severity, Env.log_level)
|
91
|
-
@formatter = options.fetch(:formatter, nil)
|
92
93
|
@logger.formatter = proc { |s,t,p,m| m }
|
93
94
|
@name = 'Logger' if @name.nil?
|
94
95
|
end
|
@@ -103,7 +104,7 @@ module OFlow
|
|
103
104
|
ss = ['DEBUG', 'INFO', 'WARN', 'ERROR', 'FATAL'][level]
|
104
105
|
ss = '' if ss.nil?
|
105
106
|
if @formatter.nil?
|
106
|
-
msg = "#{ss[0]}
|
107
|
+
msg = "#{ss[0]} #{now.strftime('%Y-%m-%dT%H:%M:%S.%6N')} #{tid}> #{message}\n"
|
107
108
|
else
|
108
109
|
msg = @formatter.call(ss, now, tid, message)
|
109
110
|
end
|
@@ -29,6 +29,8 @@ module OFlow
|
|
29
29
|
# - :key_data [String] path to record data (default: nil (all))
|
30
30
|
# - :key_path [String] path to key for the record (default: 'key')
|
31
31
|
# - :seq_path [String] path to sequence for the record (default: 'seq')
|
32
|
+
# - :results_path [String] path to where the results should be placed in
|
33
|
+
# the request (default: nil or ship only results)
|
32
34
|
# - :cache [Boolean] if true, cache records in memory
|
33
35
|
# - :historic [Boolean] if true, do not delete previous versions
|
34
36
|
def initialize(task, options)
|
@@ -37,21 +39,25 @@ module OFlow
|
|
37
39
|
if @dir.nil?
|
38
40
|
@dir = File.join('db', task.full_name.gsub(':', '/'))
|
39
41
|
end
|
40
|
-
@
|
41
|
-
|
42
|
+
@dir = File.expand_path(@dir.strip)
|
43
|
+
|
44
|
+
@key_path = options.fetch(:key_path, 'key').strip
|
45
|
+
@seq_path = options.fetch(:seq_path, 'seq').strip
|
42
46
|
@data_path = options.fetch(:data_path, nil) # nil means all contents
|
47
|
+
@data_path.strip! unless @data_path.nil?
|
48
|
+
@results_path = options[:results_path]
|
49
|
+
@results_path.strip! unless @results_path.nil?
|
43
50
|
if options.fetch(:cache, true)
|
44
51
|
# key is record key, value is [seq, rec]
|
45
52
|
@cache = {}
|
46
53
|
else
|
47
54
|
@cache = nil
|
48
55
|
end
|
49
|
-
@historic = options.fetch(:historic, false)
|
56
|
+
@historic = ('true' == options.fetch(:historic, 'false').to_s)
|
50
57
|
|
51
58
|
if Dir.exist?(@dir)
|
52
59
|
unless @cache.nil?
|
53
|
-
Dir.glob(File.join('**', '*.json')).each do |path|
|
54
|
-
path = File.join(@dir, path)
|
60
|
+
Dir.glob(File.join(@dir, '**', '*.json')).each do |path|
|
55
61
|
if File.symlink?(path)
|
56
62
|
rec = load(path)
|
57
63
|
unless @cache.nil?
|
@@ -76,6 +82,8 @@ module OFlow
|
|
76
82
|
result = read(box)
|
77
83
|
when :update
|
78
84
|
result = update(box)
|
85
|
+
when :insert_update
|
86
|
+
result = insert_update(box)
|
79
87
|
when :delete, :remove
|
80
88
|
result = delete(box)
|
81
89
|
when :query
|
@@ -85,7 +93,14 @@ module OFlow
|
|
85
93
|
else
|
86
94
|
raise OpError.new(task.full_name, op)
|
87
95
|
end
|
88
|
-
|
96
|
+
unless dest.nil?
|
97
|
+
if @results_path.nil?
|
98
|
+
box = Box.new(result, box.tracker)
|
99
|
+
else
|
100
|
+
box = box.set(@results_path, result)
|
101
|
+
end
|
102
|
+
task.ship(dest, box)
|
103
|
+
end
|
89
104
|
end
|
90
105
|
|
91
106
|
def insert(box)
|
@@ -151,6 +166,14 @@ module OFlow
|
|
151
166
|
rec
|
152
167
|
end
|
153
168
|
|
169
|
+
def insert_update(box)
|
170
|
+
begin
|
171
|
+
insert(box)
|
172
|
+
rescue ExistsError
|
173
|
+
update(box)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
154
177
|
def delete(box)
|
155
178
|
key = box.get(@key_path)
|
156
179
|
@cache.delete(key) unless @cache.nil?
|
data/lib/oflow/actors/timer.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
|
2
|
+
require 'date'
|
3
|
+
|
2
4
|
require 'logger'
|
3
5
|
|
4
6
|
module OFlow
|
@@ -70,7 +72,6 @@ module OFlow
|
|
70
72
|
end
|
71
73
|
while true
|
72
74
|
now = Time.now()
|
73
|
-
|
74
75
|
# If past stop time then it is done. A future change in options can
|
75
76
|
# restart the timer.
|
76
77
|
return if !@stop.nil? && @stop < now
|
@@ -123,7 +124,7 @@ module OFlow
|
|
123
124
|
|
124
125
|
def set_options(options)
|
125
126
|
set_start(options[:start]) # if nil let start get set to now
|
126
|
-
set_stop(
|
127
|
+
set_stop(options[:stop]) if options.has_key?(:stop)
|
127
128
|
set_period(options[:period]) if options.has_key?(:period)
|
128
129
|
set_repeat(options[:repeat]) if options.has_key?(:repeat)
|
129
130
|
set_with_tracker(options[:with_tracker])
|
@@ -131,9 +132,11 @@ module OFlow
|
|
131
132
|
end
|
132
133
|
|
133
134
|
def set_start(v)
|
134
|
-
|
135
|
-
|
136
|
-
v =
|
135
|
+
if v.is_a?(String)
|
136
|
+
v = DateTime.parse(v).to_time
|
137
|
+
v = v - v.gmtoff
|
138
|
+
elsif v.is_a?(Numeric)
|
139
|
+
v = Time.now() + v
|
137
140
|
elsif v.nil?
|
138
141
|
v = Time.now()
|
139
142
|
elsif !v.kind_of?(Time) && !v.kind_of?(Date)
|
@@ -143,9 +146,11 @@ module OFlow
|
|
143
146
|
end
|
144
147
|
|
145
148
|
def set_stop(v)
|
146
|
-
|
147
|
-
|
148
|
-
v =
|
149
|
+
if v.is_a?(String)
|
150
|
+
v = DateTime.parse(v).to_time
|
151
|
+
v = v - v.gmtoff
|
152
|
+
elsif v.is_a?(Numeric)
|
153
|
+
v = Time.now() + v
|
149
154
|
elsif !v.nil? && !v.kind_of?(Time) && !v.kind_of?(Date)
|
150
155
|
raise ConfigError.new("Expected stop to be a Time or Numeric, not a #{v.class}.")
|
151
156
|
end
|
@@ -153,17 +158,29 @@ module OFlow
|
|
153
158
|
end
|
154
159
|
|
155
160
|
def set_period(v)
|
156
|
-
|
161
|
+
p = 0.0
|
162
|
+
if v.kind_of?(Numeric)
|
163
|
+
p = v
|
164
|
+
elsif v.is_a?(String)
|
165
|
+
p = v.strip().to_f
|
166
|
+
else
|
157
167
|
raise ConfigError.new("Expected period to be a Numeric, not a #{v.class}.")
|
158
168
|
end
|
159
|
-
|
169
|
+
raise ConfigError.new("period must be greater than 0.0.") if 0.0 >= p
|
170
|
+
@period = p
|
160
171
|
end
|
161
172
|
|
162
173
|
def set_repeat(v)
|
163
|
-
|
174
|
+
r = nil
|
175
|
+
if v.kind_of?(Fixnum)
|
176
|
+
r = v
|
177
|
+
elsif v.is_a?(String)
|
178
|
+
r = v.strip().to_i
|
179
|
+
elsif !v.nil?
|
164
180
|
raise ConfigError.new("Expected repeat to be a Fixnum, not a #{v.class}.")
|
165
181
|
end
|
166
|
-
|
182
|
+
raise ConfigError.new("repeat must be greater than or equal 0.0 or nil") if !r.nil? && 0.0 >= r
|
183
|
+
@repeat = r
|
167
184
|
end
|
168
185
|
|
169
186
|
def set_label(v)
|
data/lib/oflow/box.rb
CHANGED
@@ -35,7 +35,7 @@ module OFlow
|
|
35
35
|
Box.new(@contents, @tracker.receive(location, op))
|
36
36
|
end
|
37
37
|
|
38
|
-
# Sets or adds a value
|
38
|
+
# Sets or adds a value inside the Box. The Box is changed with the new
|
39
39
|
# contents being thawed where necessary. A path is a set of element names in
|
40
40
|
# the case of a Hash or index numbers in the case of an Array joined with
|
41
41
|
# the ':' character as a separator.
|
@@ -46,7 +46,7 @@ module OFlow
|
|
46
46
|
aset(path.split(':'), value)
|
47
47
|
end
|
48
48
|
|
49
|
-
# Sets or adds a value
|
49
|
+
# Sets or adds a value inside the Box where the path is an array of
|
50
50
|
# element names or indices. Indices can be Fixnum or Strings.
|
51
51
|
# @param path [Array] location of element to change or add.
|
52
52
|
# @param value value for the addition or change
|
data/lib/oflow/env.rb
CHANGED
@@ -5,17 +5,11 @@ module OFlow
|
|
5
5
|
# OFlow system.
|
6
6
|
class Env
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
extend HasName
|
11
|
-
extend HasErrorHandler
|
8
|
+
include HasLog
|
9
|
+
include HasErrorHandler
|
12
10
|
|
13
|
-
# The default logging level.
|
14
11
|
@@log_level = Logger::WARN
|
15
12
|
|
16
|
-
init_name(nil, '')
|
17
|
-
init_tasks()
|
18
|
-
|
19
13
|
# Returns the default log level.
|
20
14
|
# @return [Fixnum] the default log level which is one of the Logger::Severity values.
|
21
15
|
def self.log_level()
|
@@ -26,23 +20,235 @@ module OFlow
|
|
26
20
|
# @param level [Fixnum] Logger::Severity to set the default log level to
|
27
21
|
def self.log_level=(level)
|
28
22
|
@@log_level = level unless level < Logger::Severity::DEBUG || Logger::Severity::FATAL < level
|
23
|
+
#@log.receive(:severity, Box.new(@log_level)) unless @log.nil?
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(name='')
|
27
|
+
# The default logging level.
|
28
|
+
@flows = {}
|
29
|
+
@prepared = false
|
30
|
+
@name = name
|
31
|
+
@log = nil
|
32
|
+
_clear()
|
33
|
+
end
|
34
|
+
|
35
|
+
def full_name()
|
36
|
+
@name
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns a log Task if one is set on the instance.
|
40
|
+
# @return [Task] log Task.
|
41
|
+
def log()
|
42
|
+
@log
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns a error_handler Task if one is set on the instance.
|
46
|
+
# @return [Task] error_handler Task.
|
47
|
+
def error_handler()
|
48
|
+
@error_handler
|
49
|
+
end
|
50
|
+
|
51
|
+
# Creates a Flow and yield to a block with the newly create Flow. Used to
|
52
|
+
# contruct Flows.
|
53
|
+
# @param name [Symbol|String] base name for the Flow
|
54
|
+
# @param options [Hash] optional parameters
|
55
|
+
# @param block [Proc] block to yield to with the new Flow instance
|
56
|
+
# @return [Flow] new Flow
|
57
|
+
def flow(name, &block)
|
58
|
+
f = Flow.new(self, name)
|
59
|
+
@flows[f.name] = f
|
60
|
+
yield(f) if block_given?
|
61
|
+
f
|
62
|
+
end
|
63
|
+
|
64
|
+
def prepare()
|
65
|
+
@flows.each_value { |f|
|
66
|
+
f.resolve_all_links()
|
67
|
+
}
|
68
|
+
validate()
|
69
|
+
@prepared = true
|
70
|
+
end
|
71
|
+
|
72
|
+
# Validates the container by verifying all links on a task have been set to
|
73
|
+
# a valid destination and that destination has been resolved.
|
74
|
+
# @raise [ValidateError] if there is an error in validation
|
75
|
+
def validate()
|
76
|
+
# collects errors and raises all errors at once if there are any
|
77
|
+
errors = _validation_errors()
|
78
|
+
raise ValidateError.new(errors) unless errors.empty?
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns an Array of validation errors.
|
82
|
+
def _validation_errors()
|
83
|
+
errors = []
|
84
|
+
@flows.each_value { |f| errors += f._validation_errors() }
|
85
|
+
errors
|
86
|
+
end
|
87
|
+
|
88
|
+
# Resolves all the Links on all the Flows being managed.
|
89
|
+
def resolve_all_links()
|
90
|
+
@flows.each_value { |f|
|
91
|
+
f.resolve_all_links()
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
# Iterates over each Flow and yields to the provided block with each Flow.
|
96
|
+
# @param blk [Proc] Proc to call on each iteration
|
97
|
+
def each_flow(&blk)
|
98
|
+
@flows.each { |name,flow| blk.yield(flow) }
|
99
|
+
end
|
100
|
+
|
101
|
+
# Performs a recursive walk over all Flows and yields to the provided block
|
102
|
+
# for each.
|
103
|
+
# @param blk [Proc] Proc to call on each iteration
|
104
|
+
def walk_flows(&blk)
|
105
|
+
@flows.each_value do |f|
|
106
|
+
blk.yield(t)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Performs a recursive walk over all Tasks in all Flows and yields to the
|
111
|
+
# provided block for each.
|
112
|
+
# @param blk [Proc] Proc to call on each iteration
|
113
|
+
def walk_tasks(&blk)
|
114
|
+
@flows.each_value do |f|
|
115
|
+
f.walk_tasks(&blk)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Locates and return a Flow with the specified name.
|
120
|
+
# @param name [String] name of the Flow
|
121
|
+
# @return [Flow|nil] the Flow with the name specified or nil
|
122
|
+
def find_flow(name)
|
123
|
+
name = name.to_sym unless name.nil?
|
124
|
+
@flows[name]
|
125
|
+
end
|
126
|
+
|
127
|
+
# Locates and return a Task with the specified full name.
|
128
|
+
# @param name [String] full name of the Task
|
129
|
+
# @return [Flow|Task|nil] the Flow or Task with the name specified or nil
|
130
|
+
def locate(name)
|
131
|
+
name = name[1..-1] if name.start_with?(':')
|
132
|
+
name = name[0..-2] if name.end_with?(':')
|
133
|
+
path = name.split(':')
|
134
|
+
_locate(path)
|
135
|
+
end
|
136
|
+
|
137
|
+
def _locate(path)
|
138
|
+
f = @flows[path[0].to_sym]
|
139
|
+
return f if f.nil? || 1 == path.size
|
140
|
+
f._locate(path[1..-1])
|
141
|
+
end
|
142
|
+
|
143
|
+
# Returns the number of active Tasks.
|
144
|
+
def flow_count()
|
145
|
+
@flows.size
|
146
|
+
end
|
147
|
+
|
148
|
+
# Returns the sum of all the requests in all the Flow's Task's queues.
|
149
|
+
# @return [Fixnum] total number of items waiting to be processed
|
150
|
+
def queue_count()
|
151
|
+
cnt = 0
|
152
|
+
@flows.each_value { |f| cnt += f.queue_count() }
|
153
|
+
cnt
|
154
|
+
end
|
155
|
+
|
156
|
+
# Returns true of one or more Tasks is either processing a request or has a
|
157
|
+
# request waiting to be processed on it's input queue.
|
158
|
+
# @return [true|false] the busy state across all Tasks
|
159
|
+
def busy?
|
160
|
+
@flows.each_value { |f| return true if f.busy? }
|
161
|
+
return true if !@log.nil? && @log.busy?
|
162
|
+
return true if !@error_handler.nil? && @error_handler.busy?
|
163
|
+
false
|
164
|
+
end
|
165
|
+
|
166
|
+
# Calls the stop() method on all Tasks.
|
167
|
+
def stop()
|
168
|
+
@flows.each_value { |f| f.stop() }
|
169
|
+
end
|
170
|
+
|
171
|
+
# Calls the step() method one Task that is stopped and has an item in the
|
172
|
+
# queue. The Tasks with the highest backed_up() value is selected.
|
173
|
+
def step()
|
174
|
+
max = 0.0
|
175
|
+
best = nil
|
176
|
+
walk_tasks() do |t|
|
177
|
+
if Task::STOPPED == t.state
|
178
|
+
bu = t.backed_up()
|
179
|
+
if max < bu
|
180
|
+
best = t
|
181
|
+
max = bu
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
best.step() unless best.nil?
|
186
|
+
best
|
187
|
+
end
|
188
|
+
|
189
|
+
# Calls the start() method on all Tasks.
|
190
|
+
def start()
|
191
|
+
prepare() unless @prepared
|
192
|
+
@flows.each_value { |f| f.start() }
|
193
|
+
end
|
194
|
+
|
195
|
+
# Wakes up all the Tasks in the Flow.
|
196
|
+
def wakeup()
|
197
|
+
@flows.each_value { |f| f.wakeup() }
|
198
|
+
end
|
199
|
+
|
200
|
+
# Wakes up all the Tasks in the Flow and waits for the system to become idle
|
201
|
+
# before returning.
|
202
|
+
def flush()
|
203
|
+
wakeup()
|
204
|
+
@flows.each_value { |f| f.flush() }
|
205
|
+
while busy?
|
206
|
+
sleep(0.2)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
# Sets the state of all Tasks recursively. This should not be called
|
211
|
+
# directly.
|
212
|
+
def state=(s)
|
213
|
+
@flows.each_value do |f|
|
214
|
+
f.state = s
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
# Shuts down all Tasks.
|
219
|
+
# @param flush_first [true|false] flag indicating shutdown should occur after the system becomes idle
|
220
|
+
def shutdown(flush_first=false)
|
221
|
+
# block all tasks first so threads can empty queues
|
222
|
+
@flows.each_value do |f|
|
223
|
+
f.state = Task::BLOCKED
|
224
|
+
end
|
225
|
+
# shutdown and wait for queues to empty if necessary
|
226
|
+
@flows.each_value do |f|
|
227
|
+
f.shutdown(flush_first)
|
228
|
+
end
|
229
|
+
@flows = {}
|
230
|
+
end
|
231
|
+
|
232
|
+
# Clears out all Tasks and Flows and resets the object back to a empty state.
|
233
|
+
def clear()
|
234
|
+
shutdown()
|
235
|
+
@flows = {}
|
236
|
+
_clear()
|
29
237
|
end
|
30
238
|
|
31
239
|
# Resets the error handler and log. Usually called on init and by the
|
32
240
|
# clear() method.
|
33
|
-
def
|
241
|
+
def _clear()
|
34
242
|
@error_handler = Task.new(self, :error, Actors::ErrorHandler)
|
35
243
|
@log = Task.new(self, :log, Actors::Log)
|
36
244
|
end
|
37
245
|
|
38
|
-
_clear()
|
39
|
-
|
40
246
|
# Describes all the Flows and Tasks in the system.
|
41
|
-
def
|
247
|
+
def describe(detail=0, indent=0)
|
42
248
|
i = ' ' * indent
|
43
|
-
lines = ["#{i}#{self} {"]
|
44
|
-
@
|
45
|
-
lines <<
|
249
|
+
lines = ["#{i}#{@name} (#{self.class.name}) {"]
|
250
|
+
@flows.each_value { |f|
|
251
|
+
lines << f.describe(detail, indent + 2)
|
46
252
|
}
|
47
253
|
lines << i + "}"
|
48
254
|
lines.join("\n")
|