libis-workflow 2.0.beta.9 → 2.0.beta.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +8 -13
- data/README.md +190 -94
- data/lib/libis/workflow/base/dir_item.rb +15 -0
- data/lib/libis/workflow/base/file_item.rb +82 -0
- data/lib/libis/workflow/base/run.rb +21 -9
- data/lib/libis/workflow/base/work_item.rb +246 -0
- data/lib/libis/workflow/base/workflow.rb +66 -13
- data/lib/libis/workflow/base.rb +6 -0
- data/lib/libis/workflow/dir_item.rb +12 -0
- data/lib/libis/workflow/file_item.rb +17 -0
- data/lib/libis/workflow/run.rb +3 -4
- data/lib/libis/workflow/task.rb +22 -17
- data/lib/libis/workflow/tasks/analyzer.rb +6 -3
- data/lib/libis/workflow/version.rb +1 -1
- data/lib/libis/workflow/work_item.rb +51 -0
- data/lib/libis/workflow.rb +12 -6
- data/spec/items/test_dir_item.rb +2 -3
- data/spec/items/test_file_item.rb +2 -3
- data/spec/items/test_run.rb +1 -1
- data/spec/tasks/camelize_name.rb +1 -1
- data/spec/tasks/checksum_tester.rb +1 -1
- data/spec/workflow_spec.rb +29 -80
- metadata +10 -7
- data/lib/libis/workflow/workitems/dir_item.rb +0 -12
- data/lib/libis/workflow/workitems/file_item.rb +0 -78
- data/lib/libis/workflow/workitems/work_item.rb +0 -231
- data/lib/libis/workflow/workitems.rb +0 -5
@@ -0,0 +1,246 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'backports/rails/hash'
|
4
|
+
require 'libis/tools/extend/hash'
|
5
|
+
|
6
|
+
require 'libis/workflow/config'
|
7
|
+
|
8
|
+
module Libis
|
9
|
+
module Workflow
|
10
|
+
module Base
|
11
|
+
|
12
|
+
# Base module for all work items.
|
13
|
+
#
|
14
|
+
# This module lacks the implementation for the data attributes. It functions as an interface that describes the
|
15
|
+
# common functionality regardless of the storage implementation. These attributes require some implementation:
|
16
|
+
#
|
17
|
+
# - status: [Symbol] the status field. Each task sets the status of the items it works on. Before starting processing
|
18
|
+
# the status is set to "#{task_name}Started". After successfull processing it is set to "#{task_name}Done" and if
|
19
|
+
# the task failed, it is set to "#{task_name}Failed". The status field can be used to perform real-time
|
20
|
+
# monitoring, reporting and error-recovery or restart of the ingest.
|
21
|
+
# The initial value for this attribute is :START.
|
22
|
+
# - parent: [Object|nil] a link to a parent work item. Work items can be organized in any hierarchy you think is
|
23
|
+
# relevant for your workflow (e.g. directory[/directory...]/file/line or library/section/book/page). Of course
|
24
|
+
# hierarchies are not mandatory.
|
25
|
+
# - items: [Array] a list of child work items. see above.
|
26
|
+
# - options: [Hash] a set of options for the task chain on how to deal with this work item. This attribute can be
|
27
|
+
# used to fine-tune the behaviour of tasks for a particular work item.
|
28
|
+
# - properties: [Hash] a set of properties, typically collected during the workflow processing and used to store
|
29
|
+
# final or intermediate resulst of tasks. The ::Lias::Ingester::FileItem module uses this attribute to store the
|
30
|
+
# properties (e.g. size, checksum, ...) of the file it represents.
|
31
|
+
# - log_history: [Array] a list of all logging messages collected for this work item. Whenever a task logs a message
|
32
|
+
# it will automatically be registered for the work item that it is processing or for the work item that was
|
33
|
+
# supplied as the first argument.
|
34
|
+
# - status_log: [Array] a list of all status changes the work item went through.
|
35
|
+
# - summary: [Hash] collected statistics about the ingest for the work item and its children. This structure will
|
36
|
+
# be filled in by the included task ::Lias::Ingester::Tasks::Analyzer wich is appended to the workflow by default.
|
37
|
+
#
|
38
|
+
# The module is created so that it is possible to implement an ActiveRecord/Datamapper/... implementation easily.
|
39
|
+
# A simple in-memory implementation would require:
|
40
|
+
#
|
41
|
+
# attr_accessor :parent
|
42
|
+
# attr_accessor :items
|
43
|
+
# attr_accessor :options, :properties
|
44
|
+
# attr_accessor :log_history, :status_log
|
45
|
+
# attr_accessor :summary
|
46
|
+
#
|
47
|
+
# def initialize
|
48
|
+
# self.parent = nil
|
49
|
+
# self.items = []
|
50
|
+
# self.options = {}
|
51
|
+
# self.properties = {}
|
52
|
+
# self.log_history = []
|
53
|
+
# self.status_log = []
|
54
|
+
# self.summary = {}
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# protected
|
58
|
+
#
|
59
|
+
# ## Methods below should be adapted to match the implementation of the log arrays
|
60
|
+
#
|
61
|
+
# def add_log_entry(msg)
|
62
|
+
# self.log_history << msg.merge(c_at: ::Time.now)
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# def add_status_log(message, tasklist = nil)
|
66
|
+
# self.status_log << { c_at: ::Time.now, tasklist: tasklist, text: message }.cleanup
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# def status_label(status_entry)
|
70
|
+
# "#{status_entry[:tasklist].last rescue nil}#{status_entry[:text] rescue nil}"
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
module WorkItem
|
74
|
+
include Enumerable
|
75
|
+
|
76
|
+
# String representation of the identity of the work item.
|
77
|
+
#
|
78
|
+
# You may want to overwrite this method as it tries the :name property or whatever #inspect returns if that
|
79
|
+
# failes. Typically this should return the key value, file name or id number. If that's what your :name property
|
80
|
+
# contains, you're fine.
|
81
|
+
#
|
82
|
+
# @return [String] string identification for this work item.
|
83
|
+
def name
|
84
|
+
# noinspection RubyResolve
|
85
|
+
self.properties[:name] || self.inspect
|
86
|
+
end
|
87
|
+
|
88
|
+
def to_s;
|
89
|
+
self.name;
|
90
|
+
end
|
91
|
+
|
92
|
+
def names
|
93
|
+
(self.parent.names rescue Array.new).push(name).compact
|
94
|
+
end
|
95
|
+
|
96
|
+
def namepath;
|
97
|
+
self.names.join('/');
|
98
|
+
end
|
99
|
+
|
100
|
+
# File name save version of the to_s output. The output should be safe to use as a file name to store work item
|
101
|
+
# data. Typical use is when extra file items are created by a task and need to be stored on disk. The default
|
102
|
+
# implementation URL-encodes (%xx) all characters except alphanumeric, '.' and '-'.
|
103
|
+
#
|
104
|
+
# @return [String] file name
|
105
|
+
def to_filename
|
106
|
+
self.to_s.gsub(/[^\w.-]/) { |s| '%%%02x' % s.ord }
|
107
|
+
end
|
108
|
+
|
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
|
+
# Iterates over the work item clients and invokes code on each of them.
|
155
|
+
def each
|
156
|
+
self.items.each { |item| yield item }
|
157
|
+
end
|
158
|
+
|
159
|
+
# Add a child work item
|
160
|
+
#
|
161
|
+
# @param [WorkItem] item to be added to the child list :items
|
162
|
+
def add_item(item)
|
163
|
+
return self unless item and item.is_a? WorkItem
|
164
|
+
self.items << item
|
165
|
+
item.parent = self
|
166
|
+
self.save!
|
167
|
+
item.save!
|
168
|
+
self
|
169
|
+
end
|
170
|
+
|
171
|
+
alias :<< :add_item
|
172
|
+
|
173
|
+
# Dummy method. It is a placeholder for DB backed implementations. Wherever appropriate WorkItem#save will be
|
174
|
+
# called to save the current item's state. If state needs to persisted, you should override this method or make
|
175
|
+
# sure your persistence layer implements it in your class.
|
176
|
+
def save
|
177
|
+
end
|
178
|
+
|
179
|
+
# Dummy method. It is a placeholder for DB backed implementations. Wherever appropriate WorkItem#save will be
|
180
|
+
# called to save the current item's state. If state needs to persisted, you should override this method or make
|
181
|
+
# sure your persistence layer implements it in your class.
|
182
|
+
def save!
|
183
|
+
end
|
184
|
+
|
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
|
+
end
|
243
|
+
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
@@ -5,12 +5,70 @@ require 'libis/tools/parameter'
|
|
5
5
|
module Libis
|
6
6
|
module Workflow
|
7
7
|
module Base
|
8
|
+
|
9
|
+
# This is the base module for Workflows.
|
10
|
+
#
|
11
|
+
# This module lacks the implementation for the data attributes. It functions as an interface that describes the
|
12
|
+
# common functionality regardless of the storage implementation. These attributes require some implementation:
|
13
|
+
#
|
14
|
+
# - name: [String] the name of the Workflow. The name will be used to identify the workflow. Each time a workflow
|
15
|
+
# is executed, a Run will be created. The Run will get a name that starts with the workflow name and ends with
|
16
|
+
# the date and time the Run was started. As such this name attribute serves as an identifier and should be
|
17
|
+
# treated as such. If possible is should be unique.
|
18
|
+
# - description: [String] more information about the workflow.
|
19
|
+
# - config: [Hash] detailed configuration for the workflow. The application assumes it behaves as a Hash and will
|
20
|
+
# access it with [], merge! and delete methods. If your implementation decides to implement it with another
|
21
|
+
# object, it should implement above methods. The config Hash requires the following keys:
|
22
|
+
# - run_object: [String] the full class name of the Run implementation object that should be created when the
|
23
|
+
# Workflow is executed.
|
24
|
+
# - input: [Hash] all input parameter definitions where the key is the parameter name and the value is another
|
25
|
+
# Hash with arguments for the parameter definition. It typically contains the following arguments:
|
26
|
+
# - default: default value if no value specified when the workflow is executed
|
27
|
+
# - propagate_to: the task name (or path) and parameter name that any set value for this parameter will be
|
28
|
+
# propagated to. The syntax is <task name|task path>[#<parameter name>]. It the #<parameter name> part
|
29
|
+
# is not present, the same name as the input parameter is used. If you want to push the value to
|
30
|
+
# multiple task parameters, you can either supply an array of propagate paths or put them in a string
|
31
|
+
# separated by a ','.
|
32
|
+
# - tasks: [Array] task definitions that define the order in which the tasks should be executed for the workflow.
|
33
|
+
# A task definition is a Hash with the following values:
|
34
|
+
# - class: [String] the class name of the task including the module names
|
35
|
+
# - name: [String] optional if class is present. A friendly name for the task that will be used in the logs.
|
36
|
+
# - subitems: [Boolean] execute the task on the items in the current level or on the
|
37
|
+
# child items of the current level. This parameter can be used in combination with the subtasks to
|
38
|
+
# control what objects in the hierarchy the tasks are executed against.
|
39
|
+
# - recursive: [Boolean] execute the task for the current level items only or automatically recurse through
|
40
|
+
# the item's hierarchy and execute on all items below.
|
41
|
+
# - tasks: [Array] a list of subtask defintions for this task.
|
42
|
+
# Additionally the task definition Hash may specify values for any other parameter that the task knows of.
|
43
|
+
# All tasks have parameters 'quiet', 'always_run', 'abort_on_error'. For more information about these see
|
44
|
+
# the documentation of the task class.
|
45
|
+
# A task definition does not require to have a 'class' entry. If not present the default
|
46
|
+
# ::Libis::Workflow::Task class will be instatiated. It will do nothing itself, but will execute the
|
47
|
+
# subtasks on the item(s). In such case a 'name' is mandatory.
|
48
|
+
#
|
49
|
+
# A minimal in-memory implementation could be:
|
50
|
+
#
|
51
|
+
# class Workflow
|
52
|
+
# include ::Libis::Workflow::Base::Workflow
|
53
|
+
#
|
54
|
+
# attr_accessor :name, :description, :config
|
55
|
+
#
|
56
|
+
# def initialize
|
57
|
+
# @name = ''
|
58
|
+
# @descripition = ''
|
59
|
+
# @config = Hash.new
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# end
|
63
|
+
#
|
8
64
|
module Workflow
|
9
65
|
|
10
66
|
module ClassMethods
|
11
67
|
def require_all
|
12
68
|
Config.require_all(File.join(File.dirname(__FILE__), '..', 'tasks'))
|
69
|
+
# noinspection RubyResolve
|
13
70
|
Config.require_all(Config.taskdir)
|
71
|
+
# noinspection RubyResolve
|
14
72
|
Config.require_all(Config.itemdir)
|
15
73
|
end
|
16
74
|
end
|
@@ -19,20 +77,11 @@ module Libis
|
|
19
77
|
base.extend ClassMethods
|
20
78
|
end
|
21
79
|
|
22
|
-
def name; raise RuntimeError.new "Method not implemented: #{caller[0]}"; end
|
23
|
-
def name=(_) ; raise RuntimeError.new "Method not implemented: #{caller[0]}"; end
|
24
|
-
|
25
|
-
def description; raise RuntimeError.new "Method not implemented: #{caller[0]}"; end
|
26
|
-
def description=(_); raise RuntimeError.new "Method not implemented: #{caller[0]}"; end
|
27
|
-
|
28
|
-
def config; raise RuntimeError.new "Method not implemented: #{caller[0]}"; end
|
29
|
-
def config=(_); raise RuntimeError.new "Method not implemented: #{caller[0]}"; end
|
30
|
-
|
31
80
|
def configure(cfg)
|
81
|
+
self.name = cfg.delete(:name) || self.class.name
|
82
|
+
self.description = cfg.delete(:description) || ''
|
32
83
|
self.config.merge! input: {}, tasks: []
|
33
84
|
self.config.merge! cfg
|
34
|
-
self.name = self.config.delete(:name) || self.class.name
|
35
|
-
self.description = self.config.delete(:description) || ''
|
36
85
|
|
37
86
|
self.class.require_all
|
38
87
|
|
@@ -45,11 +94,15 @@ module Libis
|
|
45
94
|
|
46
95
|
def input
|
47
96
|
self.config[:input].inject({}) do |hash, input_def|
|
48
|
-
|
97
|
+
name = input_def.first.to_sym
|
98
|
+
default = input_def.last[:default] || ''
|
99
|
+
parameter = ::Libis::Tools::Parameter.new name, default
|
49
100
|
input_def.last.each { |k, v| parameter[k.to_sym] = v}
|
50
101
|
hash[input_def.first.to_sym] = parameter
|
51
102
|
hash
|
52
103
|
end
|
104
|
+
rescue
|
105
|
+
{}
|
53
106
|
end
|
54
107
|
|
55
108
|
def run_name(timestamp = Time.now)
|
@@ -89,7 +142,7 @@ module Libis
|
|
89
142
|
options[key] = parameter.parse(options[key])
|
90
143
|
propagate_to = []
|
91
144
|
propagate_to = parameter[:propagate_to] if parameter[:propagate_to].is_a? Array
|
92
|
-
propagate_to =
|
145
|
+
propagate_to = parameter[:propagate_to].split(/\s*,\s*/) if parameter[:propagate_to].is_a? String
|
93
146
|
propagate_to.each do |target|
|
94
147
|
task_name, param_name = target.split('#')
|
95
148
|
param_name ||= key
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
5
|
+
require 'libis/workflow/base/file_item'
|
6
|
+
require 'libis/workflow/work_item'
|
7
|
+
|
8
|
+
module Libis
|
9
|
+
module Workflow
|
10
|
+
|
11
|
+
# noinspection RubyResolve
|
12
|
+
class FileItem < ::Libis::Workflow::WorkItem
|
13
|
+
include ::Libis::Workflow::Base::FileItem
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/lib/libis/workflow/run.rb
CHANGED
@@ -4,20 +4,19 @@ require 'libis/workflow/config'
|
|
4
4
|
require 'libis/workflow/workflow'
|
5
5
|
|
6
6
|
require 'libis/workflow/base/run'
|
7
|
+
require 'libis/workflow/work_item'
|
7
8
|
|
8
9
|
module Libis
|
9
10
|
module Workflow
|
10
11
|
|
11
|
-
class Run
|
12
|
+
class Run < ::Libis::Workflow::WorkItem
|
12
13
|
include ::Libis::Workflow::Base::Run
|
13
14
|
|
14
|
-
attr_accessor :start_date, :
|
15
|
+
attr_accessor :start_date, :workflow
|
15
16
|
|
16
17
|
def initialize
|
17
18
|
@start_date = Time.now
|
18
|
-
@tasks = nil
|
19
19
|
@workflow = nil
|
20
|
-
# noinspection RubySuperCallWithoutSuperclassInspection
|
21
20
|
super
|
22
21
|
end
|
23
22
|
|
data/lib/libis/workflow/task.rb
CHANGED
@@ -3,9 +3,9 @@ require 'backports/rails/hash'
|
|
3
3
|
require 'backports/rails/string'
|
4
4
|
|
5
5
|
require 'libis/tools/parameter'
|
6
|
+
require 'libis/tools/extend/hash'
|
6
7
|
|
7
8
|
require 'libis/workflow'
|
8
|
-
require 'libis/workflow/base/logger'
|
9
9
|
|
10
10
|
module Libis
|
11
11
|
module Workflow
|
@@ -39,7 +39,7 @@ module Libis
|
|
39
39
|
|
40
40
|
def run(item)
|
41
41
|
|
42
|
-
check_item_type WorkItem, item
|
42
|
+
check_item_type ::Libis::Workflow::Base::WorkItem, item
|
43
43
|
|
44
44
|
return if item.failed? unless parameter(:always_run)
|
45
45
|
|
@@ -64,8 +64,9 @@ module Libis
|
|
64
64
|
log_started item
|
65
65
|
|
66
66
|
pre_process item
|
67
|
-
process_item item
|
68
|
-
|
67
|
+
i = process_item item
|
68
|
+
item = i if i.is_a? Libis::Workflow::Base::WorkItem
|
69
|
+
post_process item
|
69
70
|
|
70
71
|
rescue WorkflowError => e
|
71
72
|
error e.message
|
@@ -92,14 +93,20 @@ module Libis
|
|
92
93
|
def namepath; self.names.join('/'); end
|
93
94
|
|
94
95
|
def apply_options(opts)
|
95
|
-
o =
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
96
|
+
o = {}
|
97
|
+
o.merge!(opts[self.class.to_s] || {})
|
98
|
+
o.merge!(opts[self.name] || opts[self.names.join('/')] || {})
|
99
|
+
o.key_strings_to_symbols! recursive: true
|
100
|
+
|
101
|
+
if o and o.is_a? Hash
|
102
|
+
default_values.each do |name, _|
|
103
|
+
next unless o.key?(name)
|
104
|
+
next unless o[name]
|
105
|
+
parameter = get_parameter_definition name
|
106
|
+
next unless (value = parameter.parse(o[name]))
|
107
|
+
self.parameter(name, value)
|
108
|
+
end
|
109
|
+
end
|
103
110
|
|
104
111
|
self.tasks.each do |task|
|
105
112
|
task.apply_options opts
|
@@ -110,16 +117,14 @@ module Libis
|
|
110
117
|
|
111
118
|
def log_started(item)
|
112
119
|
item.status = to_status :started
|
113
|
-
debug 'Started', item
|
114
120
|
end
|
115
121
|
|
116
122
|
def log_failed(item, message = nil)
|
117
|
-
warn (message
|
123
|
+
warn (message), item if message
|
118
124
|
item.status = to_status :failed
|
119
125
|
end
|
120
126
|
|
121
127
|
def log_done(item)
|
122
|
-
debug 'Completed', item
|
123
128
|
item.status = to_status :done
|
124
129
|
end
|
125
130
|
|
@@ -210,7 +215,7 @@ module Libis
|
|
210
215
|
cfg[:options] || {}
|
211
216
|
).merge(
|
212
217
|
cfg.reject { |k, _| [:options].include? k.to_sym }
|
213
|
-
).symbolize_keys!.each { |k,v|
|
218
|
+
).symbolize_keys!.each { |k,v| self[k] = v }
|
214
219
|
end
|
215
220
|
|
216
221
|
def to_status(text)
|
@@ -248,7 +253,7 @@ module Libis
|
|
248
253
|
end
|
249
254
|
|
250
255
|
def self.default_values
|
251
|
-
|
256
|
+
parameter_defs.inject({}) do |hash,parameter|
|
252
257
|
hash[parameter.first] = parameter.last[:default]
|
253
258
|
hash
|
254
259
|
end
|
@@ -8,8 +8,11 @@ module Libis
|
|
8
8
|
|
9
9
|
class Analyzer < Task
|
10
10
|
|
11
|
-
parameter quiet: true
|
12
|
-
parameter
|
11
|
+
parameter quiet: true, frozen: true
|
12
|
+
parameter abort_on_error: false, frozen: true
|
13
|
+
parameter always_run: true, frozen: true
|
14
|
+
parameter subitems: false, frozen: true
|
15
|
+
parameter recursive: false, frozen: true
|
13
16
|
|
14
17
|
def run(item)
|
15
18
|
|
@@ -30,7 +33,7 @@ module Libis
|
|
30
33
|
end
|
31
34
|
end
|
32
35
|
|
33
|
-
rescue
|
36
|
+
rescue RuntimeError => ex
|
34
37
|
|
35
38
|
puts 'Failed to analyze item: %s - %s' % [item.class, item.name]
|
36
39
|
puts 'Exception: %s' % ex.message
|
@@ -2,6 +2,6 @@
|
|
2
2
|
|
3
3
|
module Libis
|
4
4
|
module Workflow
|
5
|
-
VERSION = '2.0.beta.
|
5
|
+
VERSION = '2.0.beta.10' unless const_defined? :VERSION # the guard is against a redefinition warning that happens on Travis
|
6
6
|
end
|
7
7
|
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require 'libis/tools/extend/hash'
|
3
|
+
require 'libis/workflow/base/work_item'
|
4
|
+
|
5
|
+
module Libis
|
6
|
+
module Workflow
|
7
|
+
|
8
|
+
# In-memory implementation of ::Libis::Workflow::Base::WorkItem
|
9
|
+
class WorkItem
|
10
|
+
include ::Libis::Workflow::Base::WorkItem
|
11
|
+
|
12
|
+
attr_accessor :parent
|
13
|
+
attr_accessor :items
|
14
|
+
attr_accessor :options, :properties
|
15
|
+
attr_accessor :log_history, :status_log
|
16
|
+
attr_accessor :summary
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
self.parent = nil
|
20
|
+
self.items = []
|
21
|
+
self.options = {}
|
22
|
+
self.properties = {}
|
23
|
+
self.log_history = []
|
24
|
+
self.status_log = []
|
25
|
+
self.summary = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def add_log_entry(msg)
|
31
|
+
# noinspection RubyResolve
|
32
|
+
self.log_history << msg.merge(c_at: ::Time.now)
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_status_log(message, tasklist = nil)
|
36
|
+
# noinspection RubyResolve
|
37
|
+
self.status_log << {
|
38
|
+
timestamp: ::Time.now,
|
39
|
+
tasklist: tasklist,
|
40
|
+
text: message
|
41
|
+
}.cleanup
|
42
|
+
end
|
43
|
+
|
44
|
+
def status_label(status_entry)
|
45
|
+
"#{status_entry[:tasklist].last rescue nil}#{status_entry[:text] rescue nil}"
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
data/lib/libis/workflow.rb
CHANGED
@@ -7,16 +7,23 @@ module Libis
|
|
7
7
|
autoload :MessageRegistry, 'libis/workflow/message_registry'
|
8
8
|
autoload :Config, 'libis/workflow/config'
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
module Base
|
11
|
+
autoload :WorkItem, 'libis/workflow/base/work_item'
|
12
|
+
autoload :FileItem, 'libis/workflow/base/file_item'
|
13
|
+
autoload :DirItem, 'libis/workflow/base/dir_item'
|
14
|
+
autoload :Logger, 'libis/workflow/base/logger'
|
15
|
+
autoload :Run, 'libis/workflow/base/run'
|
16
|
+
autoload :Workflow, 'libis/workflow/base/workflow'
|
17
|
+
end
|
18
|
+
|
19
|
+
autoload :WorkItem, 'libis/workflow/work_item'
|
20
|
+
autoload :FileItem, 'libis/workflow/file_item'
|
21
|
+
autoload :DirItem, 'libis/workflow/dir_item'
|
13
22
|
|
14
23
|
autoload :Workflow, 'libis/workflow/workflow'
|
15
24
|
autoload :Run, 'libis/workflow/run'
|
16
25
|
autoload :Task, 'libis/workflow/task'
|
17
26
|
|
18
|
-
autoload :Parameter, 'libis/workflow/parameter'
|
19
|
-
|
20
27
|
autoload :Worker, 'libis/workflow/worker'
|
21
28
|
|
22
29
|
def self.configure
|
@@ -25,4 +32,3 @@ module Libis
|
|
25
32
|
|
26
33
|
end
|
27
34
|
end
|
28
|
-
|
data/spec/items/test_dir_item.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
require 'libis/workflow/
|
2
|
+
require 'libis/workflow/dir_item'
|
3
3
|
|
4
|
-
class TestDirItem
|
5
|
-
include ::Libis::Workflow::DirItem
|
4
|
+
class TestDirItem < ::Libis::Workflow::DirItem
|
6
5
|
|
7
6
|
def name=(dir)
|
8
7
|
raise RuntimeError, "'#{dir}' is not a directory" unless File.directory? dir
|
@@ -1,10 +1,9 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'libis/tools/checksum'
|
3
3
|
|
4
|
-
require 'libis/workflow/
|
4
|
+
require 'libis/workflow/file_item'
|
5
5
|
|
6
|
-
class TestFileItem
|
7
|
-
include ::Libis::Workflow::FileItem
|
6
|
+
class TestFileItem < ::Libis::Workflow::FileItem
|
8
7
|
|
9
8
|
def filename=(file)
|
10
9
|
raise RuntimeError, "'#{file}' is not a file" unless File.file? file
|
data/spec/items/test_run.rb
CHANGED