oflow 0.6.0 → 0.8.0

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