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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d36c10496898f7cb6df17b4be3ba103cb8098169
4
- data.tar.gz: 563283b71b4e11bd6b207a464b9204722d472f02
3
+ metadata.gz: 41a62977484dfde369899d13d67f34f41e88d643
4
+ data.tar.gz: 80f9dc4d7fb42859bfd4b292393b53857ac418b8
5
5
  SHA512:
6
- metadata.gz: 0d0572af84b50544594563dd86e625c1b86b444702d793ea1585cc870b31f07194903677e1a65a9c6b0d798b7e7a44031c233ca8e3764f36273c20fdb13ce9ff
7
- data.tar.gz: 6ca630368b0c25ee789e761043bbca9dcfb8e11fadf590ef8b19017b0d5ea1811c36135d9c9f2e0ab41b97e8f8776a7d969a88678c5c5a223eea4e2adf203c3c
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
- ::OFlow::Env.flow('hello_world') { |f|
100
- f.task(:repeater, ::OFlow::Actors::Timer, repeat: 3, period: period) { |t|
101
- t.link(nil, :hello, nil)
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
- ::OFlow::Env.flush()
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.
@@ -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'
@@ -42,6 +42,9 @@ module OFlow
42
42
  nil
43
43
  end
44
44
 
45
+ def set_option(key, value)
46
+ end
47
+
45
48
  # Return any options that should be displayed as part of a Task.describe().
46
49
  # @return [Hash] Hash of options with String keys.
47
50
  def options()
@@ -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
- req = read_req(session, @count)
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]
@@ -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]}, [#{now.strftime('%Y-%m-%dT%H:%M:%S.%6N')} #{tid}] #{ss} -- #{message}\n"
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
- @key_path = options.fetch(:key_path, 'key')
41
- @seq_path = options.fetch(:seq_path, 'seq')
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
- task.ship(dest, Box.new(result, box.tracker))
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?
@@ -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( options[:stop]) if options.has_key?(: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
- now = Time.now()
135
- if v.is_a?(Numeric)
136
- v = now + 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
- now = Time.now()
147
- if v.is_a?(Numeric)
148
- v = now + 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
- unless v.nil? || v.kind_of?(Numeric)
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
- @period = v
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
- unless v.nil? || v.kind_of?(Fixnum)
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
- @repeat = v
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)
@@ -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 in inside the Box. The Box is changed with the new
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 in inside the Box where the path is an array of
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
@@ -5,17 +5,11 @@ module OFlow
5
5
  # OFlow system.
6
6
  class Env
7
7
 
8
- extend HasTasks
9
- extend HasLog
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 self._clear()
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 self.describe(detail=0, indent=0)
247
+ def describe(detail=0, indent=0)
42
248
  i = ' ' * indent
43
- lines = ["#{i}#{self} {"]
44
- @tasks.each_value { |t|
45
- lines << t.describe(detail, indent + 2)
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")