oflow 0.3.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 +7 -0
- data/LICENSE +20 -0
- data/README.md +182 -0
- data/lib/oflow/actor.rb +76 -0
- data/lib/oflow/actors/errorhandler.rb +32 -0
- data/lib/oflow/actors/ignore.rb +22 -0
- data/lib/oflow/actors/log.rb +175 -0
- data/lib/oflow/actors/relay.rb +23 -0
- data/lib/oflow/actors/timer.rb +126 -0
- data/lib/oflow/actors.rb +11 -0
- data/lib/oflow/box.rb +195 -0
- data/lib/oflow/env.rb +52 -0
- data/lib/oflow/errors.rb +74 -0
- data/lib/oflow/flow.rb +75 -0
- data/lib/oflow/haserrorhandler.rb +48 -0
- data/lib/oflow/haslinks.rb +64 -0
- data/lib/oflow/haslog.rb +72 -0
- data/lib/oflow/hasname.rb +31 -0
- data/lib/oflow/hastasks.rb +209 -0
- data/lib/oflow/inspector.rb +501 -0
- data/lib/oflow/link.rb +43 -0
- data/lib/oflow/pattern.rb +8 -0
- data/lib/oflow/stamp.rb +39 -0
- data/lib/oflow/task.rb +415 -0
- data/lib/oflow/test/action.rb +21 -0
- data/lib/oflow/test/actorwrap.rb +62 -0
- data/lib/oflow/test.rb +8 -0
- data/lib/oflow/tracker.rb +109 -0
- data/lib/oflow/version.rb +5 -0
- data/lib/oflow.rb +23 -0
- data/test/actors/log_test.rb +57 -0
- data/test/actors/timer_test.rb +56 -0
- data/test/actorwrap_test.rb +48 -0
- data/test/all_tests.rb +27 -0
- data/test/box_test.rb +127 -0
- data/test/collector.rb +23 -0
- data/test/flow_basic_test.rb +93 -0
- data/test/flow_cfg_error_test.rb +94 -0
- data/test/flow_log_test.rb +87 -0
- data/test/flow_nest_test.rb +215 -0
- data/test/flow_rescue_test.rb +133 -0
- data/test/flow_tracker_test.rb +82 -0
- data/test/stutter.rb +21 -0
- data/test/task_test.rb +98 -0
- data/test/tracker_test.rb +59 -0
- metadata +93 -0
data/lib/oflow/box.rb
ADDED
@@ -0,0 +1,195 @@
|
|
1
|
+
|
2
|
+
module OFlow
|
3
|
+
|
4
|
+
# A Box encapsulates data in the system. It provides a wrapper around the data
|
5
|
+
# which becomes immutable as it is frozen in transit between Tasks. The Box
|
6
|
+
# allows the contents to be modified by replacing the contents with thawed
|
7
|
+
# copies of the original data.
|
8
|
+
#
|
9
|
+
# Boxes are shipped between Tasks. A Tracker can also be attached to a Box to
|
10
|
+
# follow it and gather a history of it's movements.
|
11
|
+
class Box
|
12
|
+
|
13
|
+
# Tracker for the box if there is one.
|
14
|
+
attr_reader :tracker
|
15
|
+
|
16
|
+
# The contents of the Box.
|
17
|
+
attr_reader :contents
|
18
|
+
|
19
|
+
# Create a new Box withe the content provided. The value provided will be
|
20
|
+
# frozen to inhibit changes to the value after the Box is created.
|
21
|
+
# @param value contents of the Box
|
22
|
+
# @param tracker [Tracker] used to track the progress of the Box
|
23
|
+
def initialize(value, tracker=nil)
|
24
|
+
@tracker = tracker
|
25
|
+
@contents = value
|
26
|
+
end
|
27
|
+
|
28
|
+
# Receives a Box by creating a new Box whose contents is the same as the
|
29
|
+
# existing but with an updated tracker.
|
30
|
+
# @param location [String] where the Box was received, full name of Task
|
31
|
+
# @param op [Symbol] operation that the Box was received under
|
32
|
+
# @return [Box] new Box.
|
33
|
+
def receive(location, op)
|
34
|
+
return self if @tracker.nil?
|
35
|
+
Box.new(@contents, @tracker.receive(location, op))
|
36
|
+
end
|
37
|
+
|
38
|
+
# Sets or adds a value in inside the Box. The Box is changed with the new
|
39
|
+
# contents being thawed where necessary. A path is a set of element names in
|
40
|
+
# the case of a Hash or index numbers in the case of an Array joined with
|
41
|
+
# the ':' character as a separator.
|
42
|
+
# @param path [String] location of element to change or add.
|
43
|
+
# @param value value for the addition or change
|
44
|
+
def set(path, value)
|
45
|
+
return aset(nil, value) if path.nil?
|
46
|
+
aset(path.split(':'), value)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Sets or adds a value in inside the Box where the path is an array of
|
50
|
+
# element names or indices. Indices can be Fixnum or Strings.
|
51
|
+
# @param path [Array] location of element to change or add.
|
52
|
+
# @param value value for the addition or change
|
53
|
+
def aset(path, value)
|
54
|
+
Box.new(_aset(path, @contents, value), @tracker)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns the data element described by the path. A path is a set of element
|
58
|
+
# names in the case of a Hash or index numbers in the case of an Array
|
59
|
+
# joined with the ':' character as a separator.
|
60
|
+
# @param path [String] location of element to return
|
61
|
+
# @return the data element.
|
62
|
+
def get(path)
|
63
|
+
return @contents if path.nil?
|
64
|
+
aget(path.split(':'))
|
65
|
+
end
|
66
|
+
|
67
|
+
# Returns the data element described by the path which is an array of
|
68
|
+
# element names or indices. Indices can be Fixnum or Strings.
|
69
|
+
# @param path [Array] location of element to return
|
70
|
+
# @return the data element.
|
71
|
+
def aget(path)
|
72
|
+
_aget(path, @contents)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns a string representation of the Box and contents.
|
76
|
+
def to_s()
|
77
|
+
"Box{#{@contents}, tracker: #{@tracker}}"
|
78
|
+
end
|
79
|
+
alias inspect to_s
|
80
|
+
|
81
|
+
# Called when passing to another Task. It freezes the contents recursively.
|
82
|
+
def freeze()
|
83
|
+
deep_freeze(@contents)
|
84
|
+
super
|
85
|
+
end
|
86
|
+
|
87
|
+
# Makes a copy of the frozen contents and the Box to allow modifications.
|
88
|
+
# @return [Box] new Box.
|
89
|
+
def thaw()
|
90
|
+
# Don't freeze the contents.
|
91
|
+
Box.new(thaw_value(@contents, true), @tracker)
|
92
|
+
end
|
93
|
+
|
94
|
+
# TBD make these module methods on a Freezer module
|
95
|
+
|
96
|
+
def deep_freeze(value)
|
97
|
+
case value
|
98
|
+
when Array
|
99
|
+
value.each { |v| deep_freeze(v) }
|
100
|
+
when Hash
|
101
|
+
# hash keys are frozen already
|
102
|
+
value.each { |k, v| deep_freeze(v) }
|
103
|
+
end
|
104
|
+
# Don't freeze other Objects. This leaves an out for special purpose
|
105
|
+
# functionality.
|
106
|
+
value.freeze
|
107
|
+
end
|
108
|
+
|
109
|
+
# Make a copy of the value, unfrozen.
|
110
|
+
def thaw_value(value, recurse)
|
111
|
+
return value unless value.frozen? || recurse
|
112
|
+
case value
|
113
|
+
when Array
|
114
|
+
# thaws the array itself but not the elements
|
115
|
+
value = Array.new(value)
|
116
|
+
value.map! { |v| thaw_value(v, true) } if recurse
|
117
|
+
when Hash
|
118
|
+
# thaws the hash itself but not the elements
|
119
|
+
orig = value
|
120
|
+
value = {}
|
121
|
+
if recurse
|
122
|
+
orig.each { |k, v| value[k] = thaw_value(v, true) }
|
123
|
+
else
|
124
|
+
orig.each { |k, v| value[k] = v }
|
125
|
+
end
|
126
|
+
when String
|
127
|
+
value = String.new(value)
|
128
|
+
end
|
129
|
+
value
|
130
|
+
end
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def _aset(path, value, rv)
|
135
|
+
return rv if path.nil? || path.empty?
|
136
|
+
p = path[0]
|
137
|
+
case value
|
138
|
+
when Array
|
139
|
+
value = Array.new(value) if value.frozen?
|
140
|
+
i = p.to_i
|
141
|
+
value[p.to_i] = _aset(path[1..-1], value[i], rv)
|
142
|
+
when Hash
|
143
|
+
if value.frozen?
|
144
|
+
orig = value
|
145
|
+
value = {}
|
146
|
+
orig.each { |k, v| value[k] = v }
|
147
|
+
end
|
148
|
+
if value.has_key?(p)
|
149
|
+
value[p] = _aset(path[1..-1], value[p], rv)
|
150
|
+
else
|
151
|
+
ps = p.to_sym
|
152
|
+
value[ps] = _aset(path[1..-1], value[ps], rv)
|
153
|
+
end
|
154
|
+
when NilClass
|
155
|
+
begin
|
156
|
+
i = p.to_i
|
157
|
+
value = []
|
158
|
+
value[i] = _aset(path[1..-1], nil, rv)
|
159
|
+
rescue
|
160
|
+
ps = p.to_sym
|
161
|
+
value = {}
|
162
|
+
value[ps] = _aset(path[1..-1], nil, rv)
|
163
|
+
end
|
164
|
+
else
|
165
|
+
raise FrozenError.new(p, value)
|
166
|
+
end
|
167
|
+
value
|
168
|
+
end
|
169
|
+
|
170
|
+
def _aget(path, value)
|
171
|
+
return value if path.nil? || path.empty? || value.nil?
|
172
|
+
p = path[0]
|
173
|
+
case value
|
174
|
+
when Array
|
175
|
+
begin
|
176
|
+
_aget(path[1..-1], value[p.to_i])
|
177
|
+
rescue
|
178
|
+
nil
|
179
|
+
end
|
180
|
+
when Hash
|
181
|
+
v = value[p] || value[p.to_sym]
|
182
|
+
_aget(path[1..-1], v)
|
183
|
+
else
|
184
|
+
if value.respond_to?(p.to_sym)
|
185
|
+
_aget(path[1..-1], value.send(p))
|
186
|
+
else
|
187
|
+
nil
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
end # Box
|
193
|
+
|
194
|
+
end # OFlow
|
195
|
+
|
data/lib/oflow/env.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
|
2
|
+
module OFlow
|
3
|
+
|
4
|
+
# The platform that Flows are created in. It is the outer most element of the
|
5
|
+
# OFlow system.
|
6
|
+
class Env
|
7
|
+
|
8
|
+
extend HasTasks
|
9
|
+
extend HasLog
|
10
|
+
extend HasName
|
11
|
+
extend HasErrorHandler
|
12
|
+
|
13
|
+
# The default logging level.
|
14
|
+
@@log_level = Logger::WARN
|
15
|
+
|
16
|
+
init_name(nil, '')
|
17
|
+
init_tasks()
|
18
|
+
|
19
|
+
# Returns the default log level.
|
20
|
+
# @return [Fixnum] the default log level which is one of the Logger::Severity values.
|
21
|
+
def self.log_level()
|
22
|
+
@@log_level
|
23
|
+
end
|
24
|
+
|
25
|
+
# Sets the default log level.
|
26
|
+
# @param level [Fixnum] Logger::Severity to set the default log level to
|
27
|
+
def self.log_level=(level)
|
28
|
+
@@log_level = level unless level < Logger::Severity::DEBUG || Logger::Severity::FATAL < level
|
29
|
+
end
|
30
|
+
|
31
|
+
# Resets the error handler and log. Usually called on init and by the
|
32
|
+
# clear() method.
|
33
|
+
def self._clear()
|
34
|
+
@error_handler = Task.new(self, :error, Actors::ErrorHandler)
|
35
|
+
@log = Task.new(self, :log, Actors::Log)
|
36
|
+
end
|
37
|
+
|
38
|
+
_clear()
|
39
|
+
|
40
|
+
# Describes all the Flows and Tasks in the system.
|
41
|
+
def self.describe(detail=0, indent=0)
|
42
|
+
i = ' ' * indent
|
43
|
+
lines = ["#{i}#{self} {"]
|
44
|
+
@tasks.each_value { |t|
|
45
|
+
lines << t.describe(detail, indent + 2)
|
46
|
+
}
|
47
|
+
lines << i + "}"
|
48
|
+
lines.join("\n")
|
49
|
+
end
|
50
|
+
|
51
|
+
end # Env
|
52
|
+
end # OFlow
|
data/lib/oflow/errors.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
|
2
|
+
module OFlow
|
3
|
+
# An Exception indicating a Task was currently not receiving new requests.
|
4
|
+
class BlockedError < Exception
|
5
|
+
def initialize()
|
6
|
+
super("Blocked, try again later")
|
7
|
+
end
|
8
|
+
end # BlockedError
|
9
|
+
|
10
|
+
# An Exception indicating a Task was too busy to complete the requested
|
11
|
+
# operation.
|
12
|
+
class BusyError < Exception
|
13
|
+
def initialize()
|
14
|
+
super("Busy, try again later")
|
15
|
+
end
|
16
|
+
end # BusyError
|
17
|
+
|
18
|
+
# An Exception indicating a data value is frozen and can not be modified.
|
19
|
+
class FrozenError < Exception
|
20
|
+
def initialize(name, value)
|
21
|
+
super("#{name}, a #{value.class} Object is frozen")
|
22
|
+
end
|
23
|
+
end # FrozenError
|
24
|
+
|
25
|
+
# An Exception indicating an error in setup or configuration.
|
26
|
+
class ConfigError < Exception
|
27
|
+
def initialize(msg)
|
28
|
+
super(msg)
|
29
|
+
end
|
30
|
+
end # ConfigError
|
31
|
+
|
32
|
+
# An Exception raised when no destination is found.
|
33
|
+
class LinkError < Exception
|
34
|
+
def initialize(dest)
|
35
|
+
super("No destination found for '#{dest}'.")
|
36
|
+
end
|
37
|
+
end # LinkError
|
38
|
+
|
39
|
+
# An Exception raised when there are validation errors.
|
40
|
+
class ValidateError < Exception
|
41
|
+
attr_accessor :problems
|
42
|
+
|
43
|
+
def initialize(errors)
|
44
|
+
@problems = errors
|
45
|
+
ma = ["#{errors.size} validation errors."]
|
46
|
+
errors.each { |e| ma << e.to_s }
|
47
|
+
super(ma.join("\n "))
|
48
|
+
end
|
49
|
+
|
50
|
+
class Problem
|
51
|
+
LINK_ERROR = 'link_error'
|
52
|
+
MISSING_ERROR = 'missing_link_error'
|
53
|
+
INPUT_ERROR = 'input_link_error'
|
54
|
+
|
55
|
+
attr_reader :task_name
|
56
|
+
attr_reader :kind
|
57
|
+
attr_reader :message
|
58
|
+
|
59
|
+
def initialize(task_name, kind, msg)
|
60
|
+
@task_name = task_name
|
61
|
+
@kind = kind
|
62
|
+
@message = msg
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_s()
|
66
|
+
"#{@task_name}: #{@message}"
|
67
|
+
end
|
68
|
+
alias inpsect to_s
|
69
|
+
|
70
|
+
end # Problem
|
71
|
+
|
72
|
+
end # ValidateError
|
73
|
+
|
74
|
+
end # OFlow
|
data/lib/oflow/flow.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
|
2
|
+
module OFlow
|
3
|
+
|
4
|
+
# The Class used to managing interactions between Tasks and sub-Flows. It can
|
5
|
+
# be thought of as a container for Tasks where the Flow keeps track of the
|
6
|
+
# Links between the Tasks.
|
7
|
+
class Flow
|
8
|
+
include HasTasks
|
9
|
+
include HasLinks
|
10
|
+
include HasName
|
11
|
+
include HasErrorHandler
|
12
|
+
include HasLog
|
13
|
+
|
14
|
+
# Create a new Flow.
|
15
|
+
# @param flow [Flow] Flow containing the Flow
|
16
|
+
# @param name [name] Flow base name
|
17
|
+
# @param options [Hash] additional options for the Flow
|
18
|
+
def initialize(flow, name, options)
|
19
|
+
init_name(flow, name)
|
20
|
+
init_tasks()
|
21
|
+
init_links()
|
22
|
+
end
|
23
|
+
|
24
|
+
# Add a Link from the edge of the Flow to a Task contained in the Flow.
|
25
|
+
# @param label [Symbol|String] identifier for the Link
|
26
|
+
# @param task_name [Symbol|String] _name base name of teh Task to link to
|
27
|
+
# @param op [Symbol|String] operation to call when forwarding a request to the target Task
|
28
|
+
def route(label, task_name, op)
|
29
|
+
op = op.to_sym unless op.nil?
|
30
|
+
label = label.to_sym unless label.nil?
|
31
|
+
raise ConfigError.new("Link #{label} already exists.") unless find_link(label).nil?
|
32
|
+
@links[label] = Link.new(task_name.to_sym, op, true)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Receive a request which is redirected to a Linked target Task.
|
36
|
+
# @param op [Symbol] identifies the link that points to the destination Task or Flow
|
37
|
+
# @param box [Box] contents or data for the request
|
38
|
+
def receive(op, box)
|
39
|
+
box = box.receive(full_name, op)
|
40
|
+
lnk = find_link(op)
|
41
|
+
raise LinkError.new(op) if lnk.nil? || lnk.target.nil?
|
42
|
+
lnk.target.receive(lnk.op, box)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns true if the Flow has a Link identified by the op.
|
46
|
+
# @param op [Symbol] identifies the Link in question
|
47
|
+
def has_input(op)
|
48
|
+
!find_link(op).nil?
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns a String describing the Flow.
|
52
|
+
# @param detail [Fixnum] higher values result in more detail in the description
|
53
|
+
# @param indent [Fixnum] the number of spaces to indent the description
|
54
|
+
def describe(detail=0, indent=0)
|
55
|
+
i = ' ' * indent
|
56
|
+
lines = ["#{i}#{name} (#{self.class}) {"]
|
57
|
+
@tasks.each_value { |t|
|
58
|
+
lines << t.describe(detail, indent + 2)
|
59
|
+
}
|
60
|
+
@links.each { |local,link|
|
61
|
+
if link.ingress
|
62
|
+
lines << " #{i}#{local} * #{link.target_name}:#{link.op}"
|
63
|
+
else
|
64
|
+
lines << " #{i}#{local} => #{link.target_name}:#{link.op}"
|
65
|
+
end
|
66
|
+
}
|
67
|
+
lines << i + "}"
|
68
|
+
lines.join("\n")
|
69
|
+
end
|
70
|
+
|
71
|
+
def _clear()
|
72
|
+
end
|
73
|
+
|
74
|
+
end # Flow
|
75
|
+
end # OFlow
|
@@ -0,0 +1,48 @@
|
|
1
|
+
|
2
|
+
module OFlow
|
3
|
+
|
4
|
+
# Provides functionality to find an error handler Task which is how error are
|
5
|
+
# handled in the system. Each Flow or Task can have a different error
|
6
|
+
# handler. If a Flow does not have an error handler the error bubbles up to
|
7
|
+
# the next Flow until an error handler is found.
|
8
|
+
module HasErrorHandler
|
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
|
+
# Sets avaliable for handling errors.
|
30
|
+
# @param t [Task|nil] Task for handling error or nil to unset
|
31
|
+
def error_handler=(t)
|
32
|
+
@error_handler = t
|
33
|
+
end
|
34
|
+
|
35
|
+
# Handles errors by putting a requestion on the error handler Task.
|
36
|
+
# @param e [Exception] error to handle
|
37
|
+
def handle_error(e)
|
38
|
+
handler = error_handler()
|
39
|
+
unless handler.nil?
|
40
|
+
handler.receive(nil, Box.new([e, full_name()]))
|
41
|
+
else
|
42
|
+
puts "** [#{full_name()}] #{e.class}: #{e.message}"
|
43
|
+
e.backtrace.each { |line| puts " #{line}" }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end # HasErrorHandler
|
48
|
+
end # OFlow
|
@@ -0,0 +1,64 @@
|
|
1
|
+
|
2
|
+
module OFlow
|
3
|
+
|
4
|
+
# Adds support for Links. Used by Flow and Env.
|
5
|
+
module HasLinks
|
6
|
+
|
7
|
+
# Sets up the links attribute.
|
8
|
+
def init_links()
|
9
|
+
@links = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
# Creates a Link identified by the label that has a target Task or Flow and
|
13
|
+
# operation.
|
14
|
+
# @param label [Symbol|String] identifer of the Link
|
15
|
+
# @param target [Symbol|String] identifer of the target Task
|
16
|
+
# @param op [Symbol|String] operation to perform on the target Task
|
17
|
+
def link(label, target, op)
|
18
|
+
label = label.to_sym unless label.nil?
|
19
|
+
op = op.to_sym unless op.nil?
|
20
|
+
raise ConfigError.new("Link #{label} already exists.") unless @links[label].nil?
|
21
|
+
label = label.to_sym unless label.nil?
|
22
|
+
@links[label] = Link.new(target.to_sym, op)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Attempts to find and resolve the Link identified by the label. Resolving a
|
26
|
+
# Link uses the target identifier to find the target Task and save that in
|
27
|
+
# the Link.
|
28
|
+
# @param label [Symbol|String] identifer of the Link
|
29
|
+
# @return [Link] returns the Link for the label
|
30
|
+
def resolve_link(label)
|
31
|
+
label = label.to_sym unless label.nil?
|
32
|
+
lnk = @links[label] || @links[nil]
|
33
|
+
return nil if lnk.nil?
|
34
|
+
set_link_target(lnk) if lnk.target.nil?
|
35
|
+
lnk
|
36
|
+
end
|
37
|
+
|
38
|
+
# Sets the target Task for a Link.
|
39
|
+
# @param lnk [Link] Link to find the target Task for.
|
40
|
+
def set_link_target(lnk)
|
41
|
+
if lnk.ingress
|
42
|
+
task = find_task(lnk.target_name)
|
43
|
+
else
|
44
|
+
task = @flow.find_task(lnk.target_name)
|
45
|
+
end
|
46
|
+
lnk.instance_variable_set(:@target, task)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Attempts to find the Link identified by the label.
|
50
|
+
# @param label [Symbol|String] identifer of the Link
|
51
|
+
# @return [Link] returns the Link for the label
|
52
|
+
def find_link(label)
|
53
|
+
label = label.to_sym unless label.nil?
|
54
|
+
@links[label] || @links[nil]
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns the Links.
|
58
|
+
# @return [Hash] Hash of Links with the keys as Symbols that are the labels of the Links.
|
59
|
+
def links()
|
60
|
+
@links
|
61
|
+
end
|
62
|
+
|
63
|
+
end # HasLinks
|
64
|
+
end # OFlow
|
data/lib/oflow/haslog.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
|
2
|
+
module OFlow
|
3
|
+
|
4
|
+
# Adds the ability to log by sending log requests to a log Task.
|
5
|
+
module HasLog
|
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
|
+
# Sets the log attribute.
|
22
|
+
# @param t [Task] log Task
|
23
|
+
def log=(t)
|
24
|
+
@log = t
|
25
|
+
end
|
26
|
+
|
27
|
+
# Lower level logging method. Generally only used when one of the primary
|
28
|
+
# severity methods are called.
|
29
|
+
# @param level [String] message severity or level
|
30
|
+
# @param msg [String] message to log
|
31
|
+
# @param fn [String] full name of Task or Flow calling the log function
|
32
|
+
def log_msg(level, msg, fn)
|
33
|
+
lt = log()
|
34
|
+
unless lt.nil?
|
35
|
+
lt.receive(level, Box.new([msg, fn]))
|
36
|
+
else
|
37
|
+
puts "[#{fn}] #{msg}"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Logs the message if logging level is at least debug.
|
42
|
+
# @param msg [String] message to log
|
43
|
+
def debug(msg)
|
44
|
+
log_msg(:debug, msg, full_name())
|
45
|
+
end
|
46
|
+
|
47
|
+
# Logs the message if logging level is at least info.
|
48
|
+
# @param msg [String] message to display or log
|
49
|
+
def info(msg)
|
50
|
+
log_msg(:info, msg, full_name())
|
51
|
+
end
|
52
|
+
|
53
|
+
# Logs the message if logging level is at least error.
|
54
|
+
# @param msg [String] message to display or log
|
55
|
+
def error(msg)
|
56
|
+
log_msg(:error, msg, full_name())
|
57
|
+
end
|
58
|
+
|
59
|
+
# Logs the message if logging level is at least warn.
|
60
|
+
# @param msg [String] message to display or log
|
61
|
+
def warn(msg)
|
62
|
+
log_msg(:warn, msg, full_name())
|
63
|
+
end
|
64
|
+
|
65
|
+
# Logs the message if logging level is at least fatal.
|
66
|
+
# @param msg [String] message to display or log
|
67
|
+
def fatal(msg)
|
68
|
+
log_msg(:fatal, msg, full_name())
|
69
|
+
end
|
70
|
+
|
71
|
+
end # HasLog
|
72
|
+
end # OFlow
|
@@ -0,0 +1,31 @@
|
|
1
|
+
|
2
|
+
module OFlow
|
3
|
+
|
4
|
+
# Adds support for a name attribute and the ability to form full name for a
|
5
|
+
# named item.
|
6
|
+
module HasName
|
7
|
+
# The name.
|
8
|
+
attr_reader :name
|
9
|
+
|
10
|
+
# The containing Flow is used to support the full_name() method otherwise it
|
11
|
+
# just sets the name.
|
12
|
+
# @param flow [Flow|Env] containing Flow
|
13
|
+
# @param name [Symbol|String] base name
|
14
|
+
def init_name(flow, name)
|
15
|
+
@flow = flow
|
16
|
+
@name = name.to_sym
|
17
|
+
end
|
18
|
+
|
19
|
+
# Similar to a full file path. The full_name described the containment of
|
20
|
+
# the named item.
|
21
|
+
# @return [String] full name of item
|
22
|
+
def full_name()
|
23
|
+
if @flow.respond_to?(:full_name)
|
24
|
+
@flow.full_name() + ':' + @name.to_s
|
25
|
+
else
|
26
|
+
@name.to_s
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end # HasName
|
31
|
+
end # OFlow
|