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