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.
@@ -7,25 +7,6 @@ module OFlow
7
7
  # the next Flow until an error handler is found.
8
8
  module HasErrorHandler
9
9
 
10
- # Returns an error handler Task by checking for an @error_handler variable,
11
- # then looking for a Task with a base name of :error in itself or any of the
12
- # containing Flows.
13
- # @return [Task|nil] Task to handle errors
14
- def error_handler()
15
- return @error_handler if instance_variable_defined?(:@error_handler) && !@error_handler.nil?
16
- if instance_variable_defined?(:@flow)
17
- if @flow.respond_to?(:find_task)
18
- eh = @flow.find_task(:error)
19
- return eh unless eh.nil?
20
- end
21
- if @flow.respond_to?(:error_handler)
22
- eh = @flow.error_handler()
23
- return eh unless eh.nil?
24
- end
25
- end
26
- nil
27
- end
28
-
29
10
  # Sets avaliable for handling errors.
30
11
  # @param t [Task|nil] Task for handling error or nil to unset
31
12
  def error_handler=(t)
@@ -36,11 +17,11 @@ module OFlow
36
17
  # @param e [Exception] error to handle
37
18
  def handle_error(e)
38
19
  handler = error_handler()
39
- unless handler.nil?
40
- handler.receive(nil, Box.new([e, full_name()]))
41
- else
20
+ if handler.nil?
42
21
  puts "** [#{full_name()}] #{e.class}: #{e.message}"
43
22
  e.backtrace.each { |line| puts " #{line}" }
23
+ else
24
+ handler.receive(nil, Box.new([e, full_name()]))
44
25
  end
45
26
  end
46
27
 
@@ -4,26 +4,12 @@ module OFlow
4
4
  # Adds the ability to log by sending log requests to a log Task.
5
5
  module HasLog
6
6
 
7
- # Returns a log Task by looking for that Task in an attribute and then in
8
- # the contained Tasks or Tasks in outer Flows.
9
- # @return [Task] log Task.
10
- def log()
11
- return @log if instance_variable_defined?(:@log) && !@log.nil?
12
- # Log task take precedence over log variable.
13
- if respond_to?(:find_task)
14
- lg = find_task(:log)
15
- return lg unless lg.nil?
16
- end
17
- return @flow.log if instance_variable_defined?(:@flow) && @flow.respond_to?(:log)
18
- nil
19
- end
20
-
21
7
  # Sets the log attribute.
22
8
  # @param t [Task] log Task
23
9
  def log=(t)
24
10
  @log = t
25
11
  end
26
-
12
+
27
13
  # Lower level logging method. Generally only used when one of the primary
28
14
  # severity methods are called.
29
15
  # @param level [String] message severity or level
@@ -45,6 +31,26 @@ module OFlow
45
31
  end
46
32
  end
47
33
 
34
+ # @return true if errors would be logged.
35
+ def error?()
36
+ Env.log_level <= Logger::Severity::ERROR
37
+ end
38
+
39
+ # @return true if warn messages would be logged.
40
+ def warn?()
41
+ Env.log_level <= Logger::Severity::WARN
42
+ end
43
+
44
+ # @return true if info messages would be logged.
45
+ def info?()
46
+ Env.log_level <= Logger::Severity::INFO
47
+ end
48
+
49
+ # @return true if debug messages would be logged.
50
+ def debug?()
51
+ Env.log_level <= Logger::Severity::DEBUG
52
+ end
53
+
48
54
  # Logs the message if logging level is at least debug.
49
55
  # @param msg [String] message to log
50
56
  def debug(msg)
@@ -6,8 +6,9 @@ module OFlow
6
6
  class Inspector < ::OTerm::Executor
7
7
  attr_reader :running
8
8
 
9
- def initialize(port=6060)
9
+ def initialize(env, port=6060)
10
10
  super()
11
+ @env = env
11
12
  @running = true
12
13
 
13
14
  register('busy', self, :busy, 'returns the busy state of the system.', nil)
@@ -44,7 +45,7 @@ available for sorting on name, activity, or queue size.|)
44
45
  def shutdown(listener, args)
45
46
  super
46
47
  @running = false
47
- Env.shutdown()
48
+ @env.shutdown()
48
49
  end
49
50
 
50
51
  def greeting()
@@ -56,7 +57,7 @@ available for sorting on name, activity, or queue size.|)
56
57
  end
57
58
 
58
59
  def busy(listener, args)
59
- if Env.busy?()
60
+ if @env.busy?()
60
61
  listener.out.pl("One or more Tasks is busy.")
61
62
  else
62
63
  listener.out.pl("All Tasks are idle.")
@@ -64,14 +65,14 @@ available for sorting on name, activity, or queue size.|)
64
65
  end
65
66
 
66
67
  def flows(listener, args)
67
- Env.each_task() do |t|
68
+ @env.each_task() do |t|
68
69
  listener.out.pl(t.full_name)
69
70
  end
70
71
  end
71
72
 
72
73
  def list(listener, args)
73
74
  if nil == args
74
- Env.each_task() do |task|
75
+ @env.each_task() do |task|
75
76
  listener.out.pl(task.full_name)
76
77
  end
77
78
  return
@@ -81,7 +82,7 @@ available for sorting on name, activity, or queue size.|)
81
82
  if id.nil?
82
83
  flow = Env
83
84
  else
84
- flow = Env.locate(id)
85
+ flow = @env.locate(id)
85
86
  end
86
87
  if flow.nil?
87
88
  listener.out.pl("--- No Flow or Task found for #{id}")
@@ -100,7 +101,7 @@ available for sorting on name, activity, or queue size.|)
100
101
  detail, id, ok = _parse_opt_id_args(args, 'v', listener)
101
102
  return unless ok
102
103
 
103
- task = Env.locate(id)
104
+ task = @env.locate(id)
104
105
  if task.nil?
105
106
  listener.out.pl("--- Failed to find '#{id}'")
106
107
  return
@@ -110,11 +111,11 @@ available for sorting on name, activity, or queue size.|)
110
111
 
111
112
  def start(listener, args)
112
113
  if nil == args || 0 == args.size()
113
- Env.start()
114
+ @env.start()
114
115
  listener.out.pl("All Tasks restarted")
115
116
  else
116
117
  args.strip!
117
- task = Env.locate(args)
118
+ task = @env.locate(args)
118
119
  if task.nil?
119
120
  listener.out.pl("--- Failed to find '#{args}'")
120
121
  else
@@ -125,7 +126,7 @@ available for sorting on name, activity, or queue size.|)
125
126
  end
126
127
 
127
128
  def step(listener, args)
128
- lg = Env.log()
129
+ lg = @env.log()
129
130
  stop_after = false
130
131
  if !lg.nil? && Task::STOPPED == lg.state
131
132
  lg.start()
@@ -136,7 +137,7 @@ available for sorting on name, activity, or queue size.|)
136
137
  task = Env
137
138
  else
138
139
  args.strip!
139
- task = Env.locate(args)
140
+ task = @env.locate(args)
140
141
  end
141
142
  if task.nil?
142
143
  listener.out.pl("--- Failed to find '#{args}'")
@@ -153,11 +154,11 @@ available for sorting on name, activity, or queue size.|)
153
154
 
154
155
  def stop(listener, args)
155
156
  if nil == args || 0 == args.size()
156
- Env.stop()
157
+ @env.stop()
157
158
  listener.out.pl("All Tasks stopped(paused)")
158
159
  else
159
160
  args.strip!
160
- task = Env.locate(args)
161
+ task = @env.locate(args)
161
162
  if task.nil?
162
163
  listener.out.pl("--- Failed to find '#{args}'")
163
164
  else
@@ -168,7 +169,7 @@ available for sorting on name, activity, or queue size.|)
168
169
  end
169
170
 
170
171
  def verbosity(listener, args)
171
- lg = Env.log
172
+ lg = @env.log
172
173
  if lg.nil?
173
174
  listener.out.pl("--- No logger")
174
175
  return
@@ -188,10 +189,10 @@ available for sorting on name, activity, or queue size.|)
188
189
  def watch(listener, args)
189
190
  tasks = []
190
191
  if args.nil? || 0 == args.size()
191
- Env.walk_tasks() { |t| tasks << t }
192
+ @env.walk_tasks() { |t| tasks << t }
192
193
  else
193
194
  args.strip!
194
- task = Env.locate(args)
195
+ task = @env.locate(args)
195
196
  if task.nil?
196
197
  listener.out.pl("--- Failed to find '#{args}'")
197
198
  return
@@ -388,7 +389,7 @@ available for sorting on name, activity, or queue size.|)
388
389
  names = ['fatal', 'error', 'warn', 'info', 'debug'].select { |s| s.start_with?(last.downcase()) }
389
390
  else # expect id or options
390
391
  with_colon = ':' == last[0]
391
- Env.walk_tasks(false) do |t|
392
+ @env.walk_tasks(false) do |t|
392
393
  fn = t.full_name
393
394
  fn = fn[1..-1] unless with_colon
394
395
  names << fn if fn.start_with?(last)
@@ -7,24 +7,25 @@ module OFlow
7
7
 
8
8
  # Name of the target.
9
9
  attr_reader :target_name
10
+ # Name of the target's parent flow.
11
+ attr_reader :flow_name
10
12
  # Operation to provide the target.
11
13
  attr_reader :op
12
- # The actual target Task or Flow.
14
+ # The actual target Task.
13
15
  attr_reader :target
14
- # Flag indicating the Link is from a Flow to a Task contained in the Flow.
15
- attr_reader :ingress
16
16
 
17
17
  # Creates a new Link. This is called from link() and route() methods on
18
18
  # Tasks and Flows.
19
+ # @param flow_name [Symbol|String] parent flow name to find the target task in or nil for this parent
19
20
  # @param target_name [Symbol] target Task base name
20
21
  # @param op [Symbol] operation to use on the target
21
22
  # @param ingress [true|false] indicates the Link is internal
22
23
  # @return [Link] new Link
23
- def initialize(target_name, op, ingress=false)
24
+ def initialize(flow_name, target_name, op)
24
25
  @target_name = target_name
26
+ @flow_name = flow_name
25
27
  @op = op
26
28
  @target = nil
27
- @ingress = ingress
28
29
  end
29
30
 
30
31
  # Delivers a package (Box) to the target.
@@ -35,7 +36,11 @@ module OFlow
35
36
 
36
37
  # Returns a string representation of the Link.
37
38
  def to_s()
38
- "Link{ingress: #{@ingress}, target_name: #{@target_name}, op: #{op}, target: #{@target}}"
39
+ if @flow_name.nil?
40
+ "Link{target_name: #{@target_name}, op: #{op}, target: #{@target}}"
41
+ else
42
+ "Link{target_name: #{@flow_name}:#{@target_name}, op: #{op}, target: #{@target}}"
43
+ end
39
44
  end
40
45
  alias inspect to_s
41
46
 
@@ -6,11 +6,17 @@ module OFlow
6
6
  # data by the receive() method. The request is put on a queue and popped off
7
7
  # one at a time and handed to an Actor associatesd with the Task.
8
8
  class Task
9
- include HasName
10
- include HasLinks
9
+
11
10
  include HasErrorHandler
12
11
  include HasLog
13
12
 
13
+ # The name.
14
+ attr_reader :name
15
+
16
+ attr_accessor :bounds
17
+ attr_accessor :shape
18
+ attr_accessor :color
19
+
14
20
  # value of @state that indicates the Task is being created.
15
21
  STARTING = 0
16
22
  # value of @state that indicates the Task is not currently processing requests
@@ -36,7 +42,12 @@ module OFlow
36
42
  # @param actor_class [Class] _class Class of the Actor to create
37
43
  # @param options [Hash] additional options for the Task or Actor
38
44
  def initialize(flow, name, actor_class, options={})
45
+ @flow = flow
46
+ @name = name.to_sym
39
47
  @state = STARTING
48
+ @bounds = nil # [x, y, w, h] as fixnums
49
+ @color = nil # [r, g, b] as floats
50
+ @shape = nil # Rectangle is the default
40
51
  @queue = []
41
52
  @req_mutex = Mutex.new()
42
53
  @req_thread = nil
@@ -47,9 +58,10 @@ module OFlow
47
58
  @current_req = nil
48
59
  @proc_cnt = 0
49
60
  @loop = nil
61
+ @links = {}
62
+ @log = nil
63
+ @error_handler = nil
50
64
 
51
- init_name(flow, name)
52
- init_links()
53
65
  set_options(options)
54
66
 
55
67
  info("Creating actor #{actor_class} with options #{options}.")
@@ -57,6 +69,7 @@ module OFlow
57
69
  raise Exception.new("#{actor} does not respond to the perform() method.") unless @actor.respond_to?(:perform)
58
70
 
59
71
  @state = options.fetch(:state, RUNNING)
72
+
60
73
  return unless @actor.with_own_thread()
61
74
 
62
75
  @loop = Thread.start(self) do |me|
@@ -77,7 +90,11 @@ module OFlow
77
90
 
78
91
  @current_req = req
79
92
  begin
80
- info("perform(#{req.op}, #{req.box})")
93
+ if debug?
94
+ debug("perform(#{req.op}, #{req.box.nil? ? '<nil>' : req.box})")
95
+ else
96
+ info("perform(#{req.op})")
97
+ end
81
98
  @actor.perform(req.op, req.box) unless req.nil?
82
99
  rescue Exception => e
83
100
  handle_error(e)
@@ -100,17 +117,63 @@ module OFlow
100
117
  end
101
118
  end
102
119
 
120
+ # Creates a Link identified by the label that has a target Task and
121
+ # operation.
122
+
123
+ # @param label [Symbol|String] identifer of the Link
124
+ # @param target [Symbol|String] identifer of the target Task
125
+ # @param op [Symbol|String] operation to perform on the target Task
126
+ # @param flow_name [Symbol|String] parent flow name to find the target task in or nil for this parent
127
+ def link(label, target, op, flow_name=nil)
128
+ label = label.to_sym unless label.nil?
129
+ op = op.to_sym unless op.nil?
130
+ flow_name = flow_name.to_sym unless flow_name.nil?
131
+ raise ConfigError.new("Link #{label} already exists.") unless @links[label].nil?
132
+ @links[label] = Link.new(flow_name, target.to_sym, op)
133
+ end
134
+
135
+ # Similar to a full file path. The full_name described the containment of
136
+ # the named item.
137
+ # @return [String] full name of item
138
+ def full_name()
139
+ if @flow.nil?
140
+ ':' + @name.to_s
141
+ else
142
+ @flow.full_name() + ':' + @name.to_s
143
+ end
144
+ end
145
+
146
+ # Returns a log Task by looking for that Task as an attribute and then in
147
+ # the parent Flow.
148
+ # @return [Task] log Task.
149
+ def log()
150
+ return @log unless @log.nil?
151
+ @flow.log()
152
+ end
153
+
154
+ # Returns an error handler Task by looking for that Task as an attribute and then in
155
+ # the parent Flow.
156
+ # @return [Task] error handler Task.
157
+ def error_handler()
158
+ return @error_handler unless @error_handler.nil?
159
+ @flow.error_handler()
160
+ end
161
+
103
162
  # Returns the state of the Task as a String.
104
163
  # @return [String] String representation of the state
105
164
  def state_string()
106
165
  ss = 'UNKNOWN'
107
166
  case @state
167
+ when STARTING
168
+ ss = 'STARTING'
108
169
  when STOPPED
109
170
  ss = 'STOPPED'
110
171
  when RUNNING
111
172
  ss = 'RUNNING'
112
173
  when CLOSING
113
174
  ss = 'CLOSING'
175
+ when BLOCKED
176
+ ss = 'BLOCKED'
114
177
  when STEP
115
178
  ss = 'STEP'
116
179
  end
@@ -124,7 +187,11 @@ module OFlow
124
187
  i = ' ' * indent
125
188
  lines = ["#{i}#{name} (#{actor.class}) {"]
126
189
  @links.each { |local,link|
127
- lines << " #{i}#{local} => #{link.target_name}:#{link.op}"
190
+ if link.flow_name.nil?
191
+ lines << " #{i}#{local} => :#{link.target_name}:#{link.op}"
192
+ else
193
+ lines << " #{i}#{local} => #{link.flow_name}:#{link.target_name}:#{link.op}"
194
+ end
128
195
  }
129
196
  if 1 <= detail
130
197
  lines << " #{i}queued: #{queue_count()} (#{busy? ? 'busy' : 'idle'})"
@@ -281,8 +348,11 @@ module OFlow
281
348
  # @param op [Symbol] operation to perform
282
349
  # @param box [Box] contents or data for the request
283
350
  def receive(op, box)
284
- info("receive(#{op}, #{box}) #{state_string}")
285
-
351
+ if debug?
352
+ debug("receive(#{op}, #{box.nil? ? '<nil>' : box}) #{state_string}")
353
+ else
354
+ info("receive(#{op})")
355
+ end
286
356
  return if CLOSING == @state
287
357
 
288
358
  raise BlockedError.new() if BLOCKED == @state
@@ -317,7 +387,11 @@ module OFlow
317
387
  box.freeze() unless box.nil?
318
388
  link = resolve_link(dest)
319
389
  raise LinkError.new(dest) if link.nil? || link.target.nil?
320
- info("shipping #{box} to #{link.target_name}:#{link.op}")
390
+ if debug?
391
+ debug("shipping #{box} to #{link.target_name}:#{link.op}")
392
+ else
393
+ info("shipping to #{link.target_name}:#{link.op}")
394
+ end
321
395
  link.target.receive(link.op, box)
322
396
  link
323
397
  end
@@ -333,6 +407,17 @@ module OFlow
333
407
  @req_timeout = options.fetch(:request_timeout, @req_timeout).to_f
334
408
  end
335
409
 
410
+ def set_option(key, value)
411
+ case key.to_sym()
412
+ when :max_queue_count
413
+ @max_queue_count = value.to_i
414
+ when :request_timeout
415
+ @req_timeout = value.to_f
416
+ else
417
+ @actor.set_option(key, value)
418
+ end
419
+ end
420
+
336
421
  def _validation_errors()
337
422
  errors = []
338
423
  @links.each_value { |lnk| _check_link(lnk, errors) }
@@ -377,6 +462,46 @@ module OFlow
377
462
  }
378
463
  end
379
464
 
465
+ # Attempts to find and resolve the Link identified by the label. Resolving a
466
+ # Link uses the target identifier to find the target Task and save that in
467
+ # the Link.
468
+ # @param label [Symbol|String] identifer of the Link
469
+ # @return [Link] returns the Link for the label
470
+ def resolve_link(label)
471
+ label = label.to_sym unless label.nil?
472
+ lnk = @links[label] || @links[nil]
473
+ return nil if lnk.nil?
474
+ set_link_target(lnk) if lnk.target.nil?
475
+ lnk
476
+ end
477
+
478
+ # Sets the target Task for a Link.
479
+ # @param lnk [Link] Link to find the target Task for.
480
+ def set_link_target(lnk)
481
+ f = @flow
482
+ f = @flow.env.find_flow(lnk.flow_name) unless lnk.flow_name.nil?
483
+ raise ConfigError.new("Flow '#{lnk.flow_name}' not found.") if f.nil?
484
+ lnk.instance_variable_set(:@target, f.find_task(lnk.target_name))
485
+ end
486
+
487
+ # Attempts to find the Link identified by the label.
488
+ # @param label [Symbol|String] identifer of the Link
489
+ # @return [Link] returns the Link for the label
490
+ def find_link(label)
491
+ label = label.to_sym unless label.nil?
492
+ @links[label] || @links[nil]
493
+ end
494
+
495
+ # Returns the Links.
496
+ # @return [Hash] Hash of Links with the keys as Symbols that are the labels of the Links.
497
+ def links()
498
+ @links
499
+ end
500
+
501
+ def has_links?()
502
+ !@links.nil? && !@links.empty?
503
+ end
504
+
380
505
  private
381
506
 
382
507
  # Internal class used to store information about asynchronous method
@@ -404,18 +529,5 @@ module OFlow
404
529
 
405
530
  end # Request
406
531
 
407
- class Link
408
- attr_reader :name
409
- attr_reader :task
410
- attr_reader :op
411
-
412
- def initialize(name, task, op)
413
- @name = name
414
- @task = task
415
- @op = op
416
- end
417
-
418
- end # Link
419
-
420
532
  end # Task
421
533
  end # OFlow