libis-workflow 2.0.beta.13 → 2.0.beta.14
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +42 -44
- data/lib/libis/workflow/action.rb +24 -0
- data/lib/libis/workflow/base/logging.rb +76 -0
- data/lib/libis/workflow/base/run.rb +11 -7
- data/lib/libis/workflow/base/work_item.rb +30 -108
- data/lib/libis/workflow/base/workflow.rb +10 -11
- data/lib/libis/workflow/base.rb +1 -0
- data/lib/libis/workflow/status.rb +83 -0
- data/lib/libis/workflow/task.rb +133 -145
- data/lib/libis/workflow/task_group.rb +62 -0
- data/lib/libis/workflow/tasks/analyzer.rb +2 -4
- data/lib/libis/workflow/version.rb +1 -1
- data/lib/libis/workflow/work_item.rb +2 -10
- data/lib/libis/workflow/worker.rb +3 -3
- data/lib/libis/workflow.rb +4 -0
- data/spec/task_spec.rb +0 -1
- data/spec/workflow_spec.rb +42 -39
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: cf536dca7ee73ba80bf51318292bf07e006d10a7
|
4
|
+
data.tar.gz: 3a5bfd9e58632e117a86de20ea949c469b2f1801
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ef7cf24225f7f0e56615afd279ab54bc33ee484aa0bec30956b1db90179da92c30e4f856c548488da99ce26534cc20b2c7a3ea9826453dc621f090a212f216f2
|
7
|
+
data.tar.gz: c01a212ea18e595a25c79c1e55d49d94e8df43f431e8b5f8f79e1756aa8513beb0fe56f985853e00604588d137c6010dadc939358ee05e68a90fdb478dcacae0
|
data/README.md
CHANGED
@@ -49,7 +49,7 @@ name has to be provided to the job configuration so that the job can instantiate
|
|
49
49
|
will be able to execute the tasks in proper order on all the WorkItems supplied/collected. Each task can be implemented
|
50
50
|
with code to run or simply contain a list of child tasks.
|
51
51
|
|
52
|
-
One
|
52
|
+
One task is predefined:
|
53
53
|
::Libis::Workflow::Tasks::Analyzer - analyzes the workflow run and summarizes the results. It is always included as the
|
54
54
|
last task by the workflow unless you supply a closing task called 'Analyzer' yourself.
|
55
55
|
|
@@ -103,15 +103,17 @@ is a Hash with:
|
|
103
103
|
|
104
104
|
The ::Libis::Workflow::Task base class allready defines the following parameters:
|
105
105
|
* quiet: Prevent generating log output. Default: false
|
106
|
-
* abort_on_error: Stop all tasks when an error occurs. Default: false
|
107
|
-
* always_run: Run this task, even if the item failed a previous task. Default: false
|
108
|
-
* subitems: Do not process the given item, but only the subitems. Default: false
|
109
106
|
* recursive: Run the task on all subitems recursively. Default: false
|
107
|
+
* retry_count: Number of times to retry the task. Default: 0
|
108
|
+
* retry_interval: Number of seconds to wait between retries. Default: 10
|
110
109
|
|
111
|
-
If 'class' is not present, the default '::Libis::Workflow::
|
112
|
-
|
113
|
-
|
114
|
-
the chapter on 'Tasks' below for more information on tasks.
|
110
|
+
If 'class' is not present, the default '::Libis::Workflow::TaskGroup' with the given name will be instantiated, which
|
111
|
+
performs each sub-task on the item. If the task is configured to be recursive, it will iterate over the child items and
|
112
|
+
perform each sub-task on each of the child items. If a 'class' value is given, an instance of that class will be created
|
113
|
+
and the task will be handed the work item to process on. See the chapter on 'Tasks' below for more information on tasks.
|
114
|
+
|
115
|
+
Note that a task with custom processing will not execute sub-tasks. If you configured a processing task with subtasks
|
116
|
+
an exception will be thrown when trying to execute the job.
|
115
117
|
|
116
118
|
##### Input variable definition
|
117
119
|
|
@@ -276,17 +278,17 @@ perform on each work item:
|
|
276
278
|
As seen above, the task should define a method called process_item that takes one argument. The argument will be a
|
277
279
|
reference to the work item that it needs to perform an action on. The task has several option to progress after
|
278
280
|
performing its actions:
|
279
|
-
* return. This is considered a normal and successful operation result.
|
280
|
-
|
281
|
-
set to 'done' for the given task.
|
281
|
+
* return. This is considered a normal and successful operation result. After a successful return the item's status will
|
282
|
+
be set to 'done' for the given task.
|
282
283
|
* raise a ::Libis::WorkflowError. Indicates that something went wrong during the processing of the item. The item's
|
283
|
-
|
284
|
-
|
284
|
+
status will be set to failed for the given task and the exception message will be printed in the error log. Processing
|
285
|
+
will continue with the next item. This action is recommended for temporary or recoverable errors. The parent item will
|
286
|
+
be flagged as 'failed' if any of the child items failed.
|
285
287
|
* raise a ::Libis::WorkflowAbort. A severe and fatal error has occured. Processing will abort immediately and the
|
286
|
-
|
287
|
-
|
288
|
+
failure status will be escalated to all items up the item hierarchy. Due to the escalating behaviour, no message is
|
289
|
+
printed in the error log automatically, so it is up to the task to an appropriate log the error itself.
|
288
290
|
* raise any other Exception. Should be avoided, but if it happens nevertheless, it will cause the item to fail for the
|
289
|
-
|
291
|
+
given task and the exception message to be logged in the error log. It will not attempt to process the other items.
|
290
292
|
|
291
293
|
### Controlling behavior with parameters
|
292
294
|
|
@@ -294,43 +296,32 @@ You have some options to control how the task will behave in special cases. Thes
|
|
294
296
|
the task, which can be set (and fixed with the 'frozen' option) on the task, but can be configured at run-time with the
|
295
297
|
help of workflow input parameters and run options.
|
296
298
|
|
297
|
-
#### Performing an action on the work item and all child items recursively
|
298
|
-
|
299
|
-
With the 'recursive' parameter set to true, your task's process_item method will be called for the work item and then
|
300
|
-
once for each child and each child's children recursively.
|
301
|
-
|
302
|
-
#### Performing an action only on the child items, skipping the work item itself
|
303
|
-
|
304
|
-
The parameter 'subitems' decides if the item handed over to the task will be processed or if it will process only it's
|
305
|
-
child items. This will only work once, not recursively, but by organizing tasks hierarchically with 'subitems' set to
|
306
|
-
true, it is possible to make sure that only items on a certain hierarchy level are performed. Recommended for workflows
|
307
|
-
where a well-known and fixed hierarchical structure has to be processed selectively.
|
308
|
-
|
309
|
-
Alternatively the 'recursive' option can be set and the 'process_item' method could check for certain item types,
|
310
|
-
properties or hierarchy levels to decide to perform the operation. This approach is more flexible but harder to
|
311
|
-
understand unless well documented.
|
312
|
-
|
313
299
|
#### Preventing any logging output from the task
|
314
300
|
|
315
301
|
Logging output can be blocked on a task-by-task basis by setting the 'quiet' parameter to true. Probably not usefull
|
316
302
|
except for the Analyzer task where the parameter is fixed to true. Logging output would otherwise intervene with the
|
317
303
|
log summary processing performed by the task.
|
318
304
|
|
319
|
-
####
|
320
|
-
|
321
|
-
When a task is so critical in the workflow process that any failure renders further processing useless, the parameter
|
322
|
-
'abort_on_error' can be turned on. Raising a ::Libis::WorkflowAbort exception would perform the same thing, but the
|
323
|
-
parameter makes the configuration more flexible.
|
305
|
+
#### Performing an action on the work item and all child items recursively
|
324
306
|
|
325
|
-
|
307
|
+
With the 'recursive' parameter set to true, your task's process_item method will be called for the work item and then
|
308
|
+
once for each child and each child's children recursively.
|
326
309
|
|
327
|
-
|
328
|
-
|
329
|
-
|
310
|
+
Note: you should not make both parent and child tasks recursive as this will cause the subitems to be processed
|
311
|
+
multiple times. If you make the parent task recursive, all tasks and sub-tasks will be performed on each item in the
|
312
|
+
tree. Making the child tasks recursive makes the parent task only perform on the top item and then performs each
|
313
|
+
sub-task one-by-one for the whole item tree. The last option is the most efficient.
|
314
|
+
|
315
|
+
Attention should be paid for the
|
316
|
+
|
317
|
+
#### Retrying if task failed
|
330
318
|
|
331
|
-
The
|
332
|
-
|
333
|
-
task
|
319
|
+
The parameters 'retry_count' and 'retry_interval' control the task's behaviour if a task has to wait for a result for an
|
320
|
+
asynchonous job. A task could be waiting for a result from the other job which will be indicated by a 'ASYNC_WAIT'
|
321
|
+
status. Alternatively the task may know that the job is halted and waiting for user interaction, indicated with the
|
322
|
+
'ASYNC_HALT' status. Only when the status is 'ASYNC_WAIT', the task will retry its process. By default the 'retry_count'
|
323
|
+
is 0, which causes the task not to retry. Before retrying the task will pause for the number of seconds given in the
|
324
|
+
parameter 'retry_interval', which is 30 by default.
|
334
325
|
|
335
326
|
### Pre- and postprocessing
|
336
327
|
|
@@ -338,6 +329,13 @@ The default implementation of 'process' is to call 'pre_process' and then call '
|
|
338
329
|
followed by calling 'post_process'. The methods 'pre_process' and 'post_process' are no-operation methods by default,
|
339
330
|
but can be overwritten if needed.
|
340
331
|
|
332
|
+
The 'pre_process' is intended to re-initialize the task before processing a new item. It can also be used to force the
|
333
|
+
task to skip processing the items altogether by calling the 'skip_processing_item' method or to prevent a recursive
|
334
|
+
task from traveling further down the item tree by calling the 'stop_processing_subitems' method. The temporary locks
|
335
|
+
behave as reset-on-read switches and are only active for the processing of the current item.
|
336
|
+
|
337
|
+
The 'post_process' method can be used to update any object after the item processing.
|
338
|
+
|
341
339
|
### Convenience functions
|
342
340
|
|
343
341
|
#### get_root_item()
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Libis
|
2
|
+
module Workflow
|
3
|
+
module Action
|
4
|
+
|
5
|
+
RUN = 0
|
6
|
+
CONTINUE = 1
|
7
|
+
RETRY = 2
|
8
|
+
ALWAYS_RUN = 3
|
9
|
+
FAILED = 4
|
10
|
+
|
11
|
+
def next_success_action(action)
|
12
|
+
case action
|
13
|
+
when :run, :continue, :retry, :always_run
|
14
|
+
:always_run
|
15
|
+
when :failed
|
16
|
+
:failed
|
17
|
+
else
|
18
|
+
:failed
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Libis
|
2
|
+
module Workflow
|
3
|
+
module Base
|
4
|
+
module Logging
|
5
|
+
|
6
|
+
# Helper function for the Tasks to add a log entry to the log_history.
|
7
|
+
#
|
8
|
+
# The supplied message structure is expected to contain the following fields:
|
9
|
+
# - :severity : ::Logger::Severity value
|
10
|
+
# - :id : optional message id
|
11
|
+
# - :text : message text
|
12
|
+
# - :task : list of tasks names (task hierarchy) that submits the message
|
13
|
+
#
|
14
|
+
# @param [Hash] message
|
15
|
+
def add_log(message = {})
|
16
|
+
msg = message_struct(message)
|
17
|
+
add_log_entry(msg)
|
18
|
+
self.save
|
19
|
+
end
|
20
|
+
|
21
|
+
def <=(message = {})
|
22
|
+
self.add_log(message)
|
23
|
+
end
|
24
|
+
|
25
|
+
# Add a structured message to the log history. The message text can be submitted as an integer or text. If an
|
26
|
+
# integer is submitted, it will be used to look up the text in the MessageRegistry. The message text will be
|
27
|
+
# passed to the % operator with the args parameter. If that failes (e.g. because the format string is not correct)
|
28
|
+
# the args value is appended to the message.
|
29
|
+
#
|
30
|
+
# @param [Symbol] severity
|
31
|
+
# @param [Hash] msg should contain message text as :id or :text and the hierarchical name of the task as :task
|
32
|
+
# @param [Array] args string format values
|
33
|
+
def log_message(severity, msg, *args)
|
34
|
+
# Prepare info from msg struct for use with string substitution
|
35
|
+
message_id, message_text = if msg[:id]
|
36
|
+
[msg[:id], MessageRegistry.instance.get_message(msg[:id])]
|
37
|
+
elsif msg[:text]
|
38
|
+
[0, msg[:text]]
|
39
|
+
else
|
40
|
+
[0, '']
|
41
|
+
end
|
42
|
+
task = msg[:task] || '*UNKNOWN*'
|
43
|
+
message_text = (message_text % args rescue "#{message_text} - #{args}")
|
44
|
+
|
45
|
+
self.add_log severity: severity, id: message_id.to_i, text: message_text, task: task
|
46
|
+
name = ''
|
47
|
+
begin
|
48
|
+
name = self.to_s
|
49
|
+
name = self.name
|
50
|
+
name = self.namepath
|
51
|
+
rescue
|
52
|
+
# do nothing
|
53
|
+
end
|
54
|
+
Config.logger.add(severity, message_text, ('%s - %s ' % [task, name]))
|
55
|
+
end
|
56
|
+
|
57
|
+
protected
|
58
|
+
|
59
|
+
SEV_LABEL = %w(DEBUG INFO WARN ERROR FATAL ANY) unless const_defined? :SEV_LABEL
|
60
|
+
|
61
|
+
# create and return a proper message structure
|
62
|
+
# @param [Hash] opts
|
63
|
+
def message_struct(opts = {})
|
64
|
+
opts.reverse_merge!(severity: ::Logger::INFO, code: nil, text: '')
|
65
|
+
{
|
66
|
+
severity: SEV_LABEL[opts[:severity]],
|
67
|
+
task: opts[:task],
|
68
|
+
code: opts[:code],
|
69
|
+
message: opts[:text]
|
70
|
+
}.cleanup
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -23,7 +23,7 @@ module Libis
|
|
23
23
|
module Run
|
24
24
|
include ::Libis::Workflow::Base::WorkItem
|
25
25
|
|
26
|
-
attr_accessor :tasks
|
26
|
+
attr_accessor :tasks, :action
|
27
27
|
|
28
28
|
def work_dir
|
29
29
|
# noinspection RubyResolve
|
@@ -49,7 +49,16 @@ module Libis
|
|
49
49
|
end
|
50
50
|
|
51
51
|
# Execute the workflow.
|
52
|
-
|
52
|
+
#
|
53
|
+
# The action parameter defines how the execution of the tasks will behave:
|
54
|
+
# - With the default :run action each task will be executed regardsless how the task performed on the item
|
55
|
+
# previously.
|
56
|
+
# - When using the :retry action a task will not perform on an item if it was successful the last time. This
|
57
|
+
# allows you to retry a run when an temporary error (e.g. asynchronous wait or halt) occured.
|
58
|
+
#
|
59
|
+
# @param [Symbol] action the type of action to take during this run. :run or :retry
|
60
|
+
def run(action = :run)
|
61
|
+
self.action = action
|
53
62
|
|
54
63
|
self.start_date = Time.now
|
55
64
|
|
@@ -58,16 +67,11 @@ module Libis
|
|
58
67
|
self.tasks = workflow.tasks(self)
|
59
68
|
configure_tasks self.options
|
60
69
|
|
61
|
-
self.status = :STARTED
|
62
70
|
|
63
71
|
self.tasks.each do |task|
|
64
|
-
# note: do not return as we want to give any remaining task in the queue the oportunity to run
|
65
|
-
next if self.failed? and not task.parameter(:always_run)
|
66
72
|
task.run self
|
67
73
|
end
|
68
74
|
|
69
|
-
self.status = :DONE unless self.failed?
|
70
|
-
|
71
75
|
end
|
72
76
|
|
73
77
|
protected
|
@@ -4,6 +4,8 @@ require 'backports/rails/hash'
|
|
4
4
|
require 'libis/tools/extend/hash'
|
5
5
|
|
6
6
|
require 'libis/workflow/config'
|
7
|
+
require 'libis/workflow/status'
|
8
|
+
require_relative 'logging'
|
7
9
|
|
8
10
|
module Libis
|
9
11
|
module Workflow
|
@@ -66,12 +68,11 @@ module Libis
|
|
66
68
|
# self.status_log << { c_at: ::Time.now, tasklist: tasklist, text: message }.cleanup
|
67
69
|
# end
|
68
70
|
#
|
69
|
-
# def status_label(status_entry)
|
70
|
-
# "#{status_entry[:tasklist].last rescue nil}#{status_entry[:text] rescue nil}"
|
71
|
-
# end
|
72
71
|
#
|
73
72
|
module WorkItem
|
74
73
|
include Enumerable
|
74
|
+
include Libis::Workflow::Status
|
75
|
+
include Libis::Workflow::Base::Logging
|
75
76
|
|
76
77
|
# String representation of the identity of the work item.
|
77
78
|
#
|
@@ -97,7 +98,9 @@ module Libis
|
|
97
98
|
self.names.join('/');
|
98
99
|
end
|
99
100
|
|
100
|
-
# File name
|
101
|
+
# File name safe version of the to_s output.
|
102
|
+
#
|
103
|
+
# The output should be safe to use as a file name to store work item
|
101
104
|
# data. Typical use is when extra file items are created by a task and need to be stored on disk. The default
|
102
105
|
# implementation URL-encodes (%xx) all characters except alphanumeric, '.' and '-'.
|
103
106
|
#
|
@@ -106,51 +109,6 @@ module Libis
|
|
106
109
|
self.to_s.gsub(/[^\w.-]/) { |s| '%%%02x' % s.ord }
|
107
110
|
end
|
108
111
|
|
109
|
-
# Gets the current status of the object.
|
110
|
-
#
|
111
|
-
# @return [Symbol] status code
|
112
|
-
def status
|
113
|
-
s = self.status_log.last
|
114
|
-
label = status_label(s)
|
115
|
-
label.empty? ? :NOT_STARTED : label.to_sym
|
116
|
-
end
|
117
|
-
|
118
|
-
# Changes the status of the object. The status changed is logged in the status_log with the current timestamp.
|
119
|
-
#
|
120
|
-
# @param [Symbol] s
|
121
|
-
def status=(s, tasklist = nil)
|
122
|
-
s, tasklist = s if s.is_a? Array
|
123
|
-
s = s.to_sym
|
124
|
-
add_status_log(s, tasklist)
|
125
|
-
self.save
|
126
|
-
end
|
127
|
-
|
128
|
-
# Check ingest status of the object. The status is checked to see if it ends in 'Failed'.
|
129
|
-
#
|
130
|
-
# @return [Boolean] true if the object failed, false otherwise
|
131
|
-
def failed?
|
132
|
-
self.status.to_s =~ /Failed$/i ? true : false
|
133
|
-
end
|
134
|
-
|
135
|
-
# Helper function for the Tasks to add a log entry to the log_history.
|
136
|
-
#
|
137
|
-
# The supplied message structure is expected to contain the following fields:
|
138
|
-
# - :severity : ::Logger::Severity value
|
139
|
-
# - :id : optional message id
|
140
|
-
# - :text : message text
|
141
|
-
# - :task : list of tasks names (task hierarchy) that submits the message
|
142
|
-
#
|
143
|
-
# @param [Hash] message
|
144
|
-
def add_log(message = {})
|
145
|
-
msg = message_struct(message)
|
146
|
-
add_log_entry(msg)
|
147
|
-
self.save
|
148
|
-
end
|
149
|
-
|
150
|
-
def <=(message = {})
|
151
|
-
; self.add_log(message);
|
152
|
-
end
|
153
|
-
|
154
112
|
# Iterates over the work item clients and invokes code on each of them.
|
155
113
|
def each
|
156
114
|
self.items.each { |item| yield item }
|
@@ -160,7 +118,7 @@ module Libis
|
|
160
118
|
#
|
161
119
|
# @param [WorkItem] item to be added to the child list :items
|
162
120
|
def add_item(item)
|
163
|
-
return self unless item and item.is_a? WorkItem
|
121
|
+
return self unless item and item.is_a? Libis::Workflow::Base::WorkItem
|
164
122
|
self.items << item
|
165
123
|
item.parent = self
|
166
124
|
self.save!
|
@@ -168,7 +126,28 @@ module Libis
|
|
168
126
|
self
|
169
127
|
end
|
170
128
|
|
171
|
-
|
129
|
+
alias_method :<<, :add_item
|
130
|
+
|
131
|
+
# Return item's parent
|
132
|
+
# @return [Libis::Workflow::Base::WorkItem]
|
133
|
+
def get_parent
|
134
|
+
self.parent
|
135
|
+
end
|
136
|
+
|
137
|
+
# go up the hierarchy and return the topmost work item
|
138
|
+
#
|
139
|
+
# @return [Libis::Workflow::Base::WorkItem]
|
140
|
+
def get_root
|
141
|
+
self.get_parent && self.get_parent.is_a?(Libis::Workflow::Base::WorkItem) && self.get_parent.get_root || self
|
142
|
+
end
|
143
|
+
|
144
|
+
# Get the top
|
145
|
+
#
|
146
|
+
# @return [Libis::Workflow::Base::Run]
|
147
|
+
def get_run
|
148
|
+
return self if self.is_a?(Libis::Workflow::Base::Run)
|
149
|
+
self.get_parent && self.get_parent.get_run || nil
|
150
|
+
end
|
172
151
|
|
173
152
|
# Dummy method. It is a placeholder for DB backed implementations. Wherever appropriate WorkItem#save will be
|
174
153
|
# called to save the current item's state. If state needs to persisted, you should override this method or make
|
@@ -182,63 +161,6 @@ module Libis
|
|
182
161
|
def save!
|
183
162
|
end
|
184
163
|
|
185
|
-
# Add a structured message to the log history. The message text can be submitted as an integer or text. If an
|
186
|
-
# integer is submitted, it will be used to look up the text in the MessageRegistry. The message text will be
|
187
|
-
# passed to the % operator with the args parameter. If that failes (e.g. because the format string is not correct)
|
188
|
-
# the args value is appended to the message.
|
189
|
-
#
|
190
|
-
# @param [Symbol] severity
|
191
|
-
# @param [Hash] msg should contain message text as :id or :text and the hierarchical name of the task as :task
|
192
|
-
# @param [Array] args string format values
|
193
|
-
def log_message(severity, msg, *args)
|
194
|
-
# Prepare info from msg struct for use with string substitution
|
195
|
-
message_id, message_text = if msg[:id]
|
196
|
-
[msg[:id], MessageRegistry.instance.get_message(msg[:id])]
|
197
|
-
elsif msg[:text]
|
198
|
-
[0, msg[:text]]
|
199
|
-
else
|
200
|
-
[0, '']
|
201
|
-
end
|
202
|
-
task = msg[:task] || '*UNKNOWN*'
|
203
|
-
message_text = (message_text % args rescue "#{message_text} - #{args}")
|
204
|
-
|
205
|
-
self.add_log severity: severity, id: message_id.to_i, text: message_text, task: task
|
206
|
-
name = ''
|
207
|
-
begin
|
208
|
-
name = self.to_s
|
209
|
-
name = self.name
|
210
|
-
name = self.namepath
|
211
|
-
rescue
|
212
|
-
# do nothing
|
213
|
-
end
|
214
|
-
Config.logger.add(severity, message_text, ('%s - %s ' % [task, name]))
|
215
|
-
end
|
216
|
-
|
217
|
-
protected
|
218
|
-
|
219
|
-
SEV_LABEL = %w(DEBUG INFO WARN ERROR FATAL ANY) unless const_defined? :SEV_LABEL
|
220
|
-
|
221
|
-
# go up the hierarchy and return the topmost work item
|
222
|
-
#
|
223
|
-
# @return [WorkItem] the root work item
|
224
|
-
def root
|
225
|
-
root = self
|
226
|
-
root = root.parent while root.parent and root.parent.is_a? WorkItem
|
227
|
-
root
|
228
|
-
end
|
229
|
-
|
230
|
-
# create and return a proper message structure
|
231
|
-
# @param [Hash] opts
|
232
|
-
def message_struct(opts = {})
|
233
|
-
opts.reverse_merge!(severity: ::Logger::INFO, code: nil, text: '')
|
234
|
-
{
|
235
|
-
severity: SEV_LABEL[opts[:severity]],
|
236
|
-
task: opts[:task],
|
237
|
-
code: opts[:code],
|
238
|
-
message: opts[:text]
|
239
|
-
}.cleanup
|
240
|
-
end
|
241
|
-
|
242
164
|
end
|
243
165
|
|
244
166
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
3
|
require 'libis/tools/parameter'
|
4
|
+
require 'libis/workflow/task_group'
|
4
5
|
|
5
6
|
module Libis
|
6
7
|
module Workflow
|
@@ -31,17 +32,14 @@ module Libis
|
|
31
32
|
# A task definition is a Hash with the following values:
|
32
33
|
# - class: [String] the class name of the task including the module names
|
33
34
|
# - name: [String] optional if class is present. A friendly name for the task that will be used in the logs.
|
34
|
-
# - subitems: [Boolean] execute the task on the items in the current level or on the
|
35
|
-
# child items of the current level. This parameter can be used in combination with the subtasks to
|
36
|
-
# control what objects in the hierarchy the tasks are executed against.
|
37
|
-
# - recursive: [Boolean] execute the task for the current level items only or automatically recurse through
|
38
|
-
# the item's hierarchy and execute on all items below.
|
39
35
|
# - tasks: [Array] a list of subtask defintions for this task.
|
36
|
+
#
|
40
37
|
# Additionally the task definition Hash may specify values for any other parameter that the task knows of.
|
41
|
-
# All tasks have parameters 'quiet', '
|
42
|
-
# the documentation of the task class.
|
38
|
+
# All tasks have parameters 'quiet', 'recursive', 'retry_count' and 'retry_interval'. For more
|
39
|
+
# information about these see the documentation of the task class.
|
40
|
+
#
|
43
41
|
# A task definition does not require to have a 'class' entry. If not present the default
|
44
|
-
# ::Libis::Workflow::
|
42
|
+
# ::Libis::Workflow::TaskGroup class will be instatiated. It will do nothing itself, but will execute the
|
45
43
|
# subtasks on the item(s). In such case a 'name' is mandatory.
|
46
44
|
#
|
47
45
|
# These values should be set by calling the #configure method which takes a Hash as argument with :name,
|
@@ -75,6 +73,7 @@ module Libis
|
|
75
73
|
end
|
76
74
|
|
77
75
|
def self.included(base)
|
76
|
+
|
78
77
|
base.extend ClassMethods
|
79
78
|
end
|
80
79
|
|
@@ -138,13 +137,13 @@ module Libis
|
|
138
137
|
end
|
139
138
|
|
140
139
|
def instantize_task(parent, cfg)
|
141
|
-
task_class =
|
140
|
+
task_class = Libis::Workflow::TaskGroup
|
142
141
|
task_class = cfg[:class].constantize if cfg[:class]
|
143
142
|
# noinspection RubyArgCount
|
144
143
|
task_instance = task_class.new(parent, cfg)
|
145
|
-
cfg[:tasks].map do |task_cfg|
|
144
|
+
cfg[:tasks] && cfg[:tasks].map do |task_cfg|
|
146
145
|
task_instance << instantize_task(task_instance, task_cfg)
|
147
|
-
end
|
146
|
+
end
|
148
147
|
task_instance
|
149
148
|
end
|
150
149
|
|
data/lib/libis/workflow/base.rb
CHANGED
@@ -0,0 +1,83 @@
|
|
1
|
+
module Libis
|
2
|
+
module Workflow
|
3
|
+
module Status
|
4
|
+
|
5
|
+
STATUS = {
|
6
|
+
NOT_STARTED: 0,
|
7
|
+
STARTED: 1,
|
8
|
+
DONE: 2,
|
9
|
+
ASYNC_WAIT: 3,
|
10
|
+
ASYNC_HALT: 4,
|
11
|
+
FAILED: 5
|
12
|
+
}
|
13
|
+
|
14
|
+
# Changes the status of the object. The status changed is logged in the status_log with the current timestamp.
|
15
|
+
#
|
16
|
+
# @param [Array] x Array with status and task
|
17
|
+
def status=(x)
|
18
|
+
s, task = x
|
19
|
+
self.add_status_log(task: task, status: s)
|
20
|
+
self.save
|
21
|
+
end
|
22
|
+
|
23
|
+
# Get last known status symbol for a given task
|
24
|
+
#
|
25
|
+
# @param [String] task task name to check item status for
|
26
|
+
# @return [Symbol] the status code
|
27
|
+
def status(task = nil)
|
28
|
+
entry = status_entry(task)
|
29
|
+
status_symbol(entry[:status]) rescue :NOT_STARTED
|
30
|
+
end
|
31
|
+
|
32
|
+
# Gets the last known status label of the object.
|
33
|
+
#
|
34
|
+
# @param [String] task name of task to get the status for
|
35
|
+
# @return [String] status label ( = task name + status )
|
36
|
+
def status_label(task = nil)
|
37
|
+
entry = self.status_entry(task)
|
38
|
+
"#{entry[:task] rescue nil}#{entry[:status].capitalize rescue nil}"
|
39
|
+
end
|
40
|
+
|
41
|
+
# Check status of the object.
|
42
|
+
#
|
43
|
+
# @param [Symbol] state status to look for
|
44
|
+
# @param [String] task name of task whose status to check
|
45
|
+
# @return [Boolean] true if the object status matches
|
46
|
+
def check_status(state, task = nil)
|
47
|
+
self.status(task) == state
|
48
|
+
end
|
49
|
+
|
50
|
+
# Compare status with current status of the object.
|
51
|
+
#
|
52
|
+
# @param [Symbol] state
|
53
|
+
# @return [Integer] 1, 0 or -1 depnding on which
|
54
|
+
def compare_status(state, task = nil)
|
55
|
+
STATUS[self.status(task)] <=> STATUS[state]
|
56
|
+
end
|
57
|
+
|
58
|
+
protected
|
59
|
+
|
60
|
+
# Get last known status entry for a given task
|
61
|
+
#
|
62
|
+
# @param [String] task task name to check item status for
|
63
|
+
# @return [Hash] the status entry
|
64
|
+
def status_entry(task = nil)
|
65
|
+
return self.status_log.last if task.blank?
|
66
|
+
self.status_log.select { |entry| entry[:task] == task }.last
|
67
|
+
end
|
68
|
+
|
69
|
+
# Convert String, Symbol or Integer to correct symbol for the status.
|
70
|
+
# If the input value is nil, the fist status entry is returned.
|
71
|
+
#
|
72
|
+
# @param [String|Symbol|Integer] x string, symbol or integer for status code.
|
73
|
+
# @return [Symbol] the corresponding STATUS symbol
|
74
|
+
def status_symbol(x)
|
75
|
+
return STATUS.key(x) if x.is_a?(Integer)
|
76
|
+
return x if STATUS.has_key?(x)
|
77
|
+
x = x.to_s.upcase.to_sym
|
78
|
+
STATUS.has_key?(x) ? x : nil
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|