libis-workflow 2.0.beta.9 → 2.0.beta.10
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 +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