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 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")