libis-workflow 2.0.beta.19-java
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +36 -0
- data/.travis.yml +32 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +397 -0
- data/Rakefile +7 -0
- data/lib/libis/exceptions.rb +8 -0
- data/lib/libis/workflow/action.rb +24 -0
- 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/job.rb +85 -0
- data/lib/libis/workflow/base/logger.rb +30 -0
- data/lib/libis/workflow/base/logging.rb +76 -0
- data/lib/libis/workflow/base/run.rb +86 -0
- data/lib/libis/workflow/base/work_item.rb +176 -0
- data/lib/libis/workflow/base/workflow.rb +153 -0
- data/lib/libis/workflow/base.rb +7 -0
- data/lib/libis/workflow/config.rb +24 -0
- data/lib/libis/workflow/dir_item.rb +12 -0
- data/lib/libis/workflow/file_item.rb +17 -0
- data/lib/libis/workflow/job.rb +26 -0
- data/lib/libis/workflow/message_registry.rb +32 -0
- data/lib/libis/workflow/run.rb +26 -0
- data/lib/libis/workflow/status.rb +83 -0
- data/lib/libis/workflow/task.rb +287 -0
- data/lib/libis/workflow/task_group.rb +62 -0
- data/lib/libis/workflow/tasks/analyzer.rb +49 -0
- data/lib/libis/workflow/version.rb +7 -0
- data/lib/libis/workflow/work_item.rb +43 -0
- data/lib/libis/workflow/worker.rb +42 -0
- data/lib/libis/workflow/workflow.rb +22 -0
- data/lib/libis/workflow.rb +40 -0
- data/lib/libis-workflow.rb +2 -0
- data/libis-workflow.gemspec +38 -0
- data/spec/items/test_dir_item.rb +15 -0
- data/spec/items/test_file_item.rb +18 -0
- data/spec/items/test_run.rb +10 -0
- data/spec/items.rb +3 -0
- data/spec/spec_helper.rb +8 -0
- data/spec/task_spec.rb +16 -0
- data/spec/tasks/camelize_name.rb +13 -0
- data/spec/tasks/checksum_tester.rb +33 -0
- data/spec/tasks/collect_files.rb +48 -0
- data/spec/workflow_spec.rb +188 -0
- metadata +196 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'libis/tools/parameter'
|
4
|
+
|
5
|
+
module Libis
|
6
|
+
module Workflow
|
7
|
+
module Base
|
8
|
+
|
9
|
+
# This is the base module for Jobs.
|
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 Job. The name will be used to identify the job. Each time a job is executed,
|
15
|
+
# a Run will be created for the associated workflow. The Run will get a name that starts with the job name
|
16
|
+
# and ends with the date and time the Run was first started. As such this name attribute serves as an
|
17
|
+
# identifier and should be treated as such. If possible it should be unique.
|
18
|
+
# - description: [String] optional information about the job.
|
19
|
+
# - workflow: [Object] the workflow containing the tasks that need to run.
|
20
|
+
# - run_obj: [String] the full class name of the Run implementation object that should be created when the
|
21
|
+
# Job is executed.
|
22
|
+
# - input: [Hash] workflow input parameter values. Each input parameter of the workflow can be set by the entries
|
23
|
+
# in this Hash.
|
24
|
+
#
|
25
|
+
# A minimal in-memory implementation could be:
|
26
|
+
#
|
27
|
+
# class Job
|
28
|
+
# include ::Libis::Workflow::Base::Job
|
29
|
+
#
|
30
|
+
# attr_accessor :name, :description, :workflow, :run_object, :input
|
31
|
+
#
|
32
|
+
# def initialize
|
33
|
+
# @name = ''
|
34
|
+
# @description = ''
|
35
|
+
# @input = Hash.new
|
36
|
+
# @workflow = ::Libis::Workflow::Workflow.new
|
37
|
+
# @run_object = ::Libis::Workflow::Run.new
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
module Job
|
43
|
+
|
44
|
+
def run_name(timestamp = Time.now)
|
45
|
+
"#{self.name}-#{timestamp.strftime('%Y%m%d%H%M%S')}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def configure(cfg = {})
|
49
|
+
self.name ||= ''
|
50
|
+
self.description ||= ''
|
51
|
+
self.input ||= {}
|
52
|
+
|
53
|
+
self.name = cfg[:name] if cfg.has_key?(:name)
|
54
|
+
self.description = cfg[:description] if cfg.has_key?(:description)
|
55
|
+
self.workflow = cfg[:workflow] if cfg.has_key?(:workflow)
|
56
|
+
self.run_object = cfg[:run_object] if cfg.has_key?(:run_object)
|
57
|
+
self.input.merge!(cfg[:input] || {})
|
58
|
+
end
|
59
|
+
|
60
|
+
# noinspection RubyResolve
|
61
|
+
# @param [Hash] opts optional extra conguration values for this particular run
|
62
|
+
def execute(opts = {})
|
63
|
+
run = self.create_run_object
|
64
|
+
raise RuntimeError.new "Could not create instance of run object '#{self.run_object}'" unless run
|
65
|
+
|
66
|
+
run.job = self
|
67
|
+
run.options = self.input.merge(opts)
|
68
|
+
run.save
|
69
|
+
|
70
|
+
run.run
|
71
|
+
|
72
|
+
run
|
73
|
+
end
|
74
|
+
|
75
|
+
protected
|
76
|
+
|
77
|
+
# noinspection RubyResolve
|
78
|
+
def create_run_object
|
79
|
+
self.run_object.constantize.new
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'libis/tools/logger'
|
2
|
+
|
3
|
+
module Libis
|
4
|
+
module Workflow
|
5
|
+
module Base
|
6
|
+
module Logger
|
7
|
+
include ::Libis::Tools::Logger
|
8
|
+
|
9
|
+
def message(severity, msg, *args)
|
10
|
+
item = self.workitem
|
11
|
+
item = args.shift if args.size > 0 and args[0].is_a?(WorkItem)
|
12
|
+
|
13
|
+
item.log_message(severity, to_msg(msg), *args) if item
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_msg(msg)
|
17
|
+
case msg
|
18
|
+
when String
|
19
|
+
{text: msg}
|
20
|
+
when Integer
|
21
|
+
{id: msg}
|
22
|
+
else
|
23
|
+
{text: (msg.to_s rescue '')}
|
24
|
+
end.merge task: self.namepath
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
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
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
require 'libis/workflow/base/work_item'
|
6
|
+
|
7
|
+
module Libis
|
8
|
+
module Workflow
|
9
|
+
module Base
|
10
|
+
|
11
|
+
# Base module for all workflow runs. It is created by an associated workflow when the workflow is executed.
|
12
|
+
#
|
13
|
+
# This module lacks the implementation for the data attributes. It functions as an interface that describes the
|
14
|
+
# common functionality regardless of the storage implementation. These attributes require some implementation:
|
15
|
+
#
|
16
|
+
# - start_date: [Time] the timestamp of the execution of the run
|
17
|
+
# - job: [Object] a reference to the Job this Run belongs to
|
18
|
+
#
|
19
|
+
# Note that ::Libis::Workflow::Base::WorkItem is a parent module and therefore requires implementation of the
|
20
|
+
# attributes of that module too.
|
21
|
+
#
|
22
|
+
# A simple in-memory implementation can be found in ::Libis::Workflow::Run
|
23
|
+
module Run
|
24
|
+
include ::Libis::Workflow::Base::WorkItem
|
25
|
+
|
26
|
+
attr_accessor :tasks, :action
|
27
|
+
|
28
|
+
def work_dir
|
29
|
+
# noinspection RubyResolve
|
30
|
+
dir = File.join(Config.workdir, self.name)
|
31
|
+
FileUtils.mkpath dir unless Dir.exist?(dir)
|
32
|
+
dir
|
33
|
+
end
|
34
|
+
|
35
|
+
def name
|
36
|
+
self.job.run_name(self.start_date)
|
37
|
+
end
|
38
|
+
|
39
|
+
def names
|
40
|
+
Array.new
|
41
|
+
end
|
42
|
+
|
43
|
+
def namepath
|
44
|
+
self.name
|
45
|
+
end
|
46
|
+
|
47
|
+
def workflow
|
48
|
+
self.job.workflow
|
49
|
+
end
|
50
|
+
|
51
|
+
# Execute the workflow.
|
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
|
62
|
+
|
63
|
+
self.start_date = Time.now
|
64
|
+
|
65
|
+
self.options = workflow.prepare_input(self.options)
|
66
|
+
|
67
|
+
self.tasks = workflow.tasks(self)
|
68
|
+
configure_tasks self.options
|
69
|
+
|
70
|
+
|
71
|
+
self.tasks.each do |task|
|
72
|
+
task.run self
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
def configure_tasks(opts)
|
80
|
+
self.tasks.each { |task| task.apply_options opts }
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,176 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'backports/rails/hash'
|
4
|
+
require 'libis/tools/extend/hash'
|
5
|
+
|
6
|
+
require 'libis/workflow/config'
|
7
|
+
require 'libis/workflow/status'
|
8
|
+
require_relative 'logging'
|
9
|
+
|
10
|
+
module Libis
|
11
|
+
module Workflow
|
12
|
+
module Base
|
13
|
+
|
14
|
+
# Base module for all work items.
|
15
|
+
#
|
16
|
+
# This module lacks the implementation for the data attributes. It functions as an interface that describes the
|
17
|
+
# common functionality regardless of the storage implementation. These attributes require some implementation:
|
18
|
+
#
|
19
|
+
# - status: [Symbol] the status field. Each task sets the status of the items it works on. Before starting processing
|
20
|
+
# the status is set to "#{task_name}Started". After successfull processing it is set to "#{task_name}Done" and if
|
21
|
+
# the task failed, it is set to "#{task_name}Failed". The status field can be used to perform real-time
|
22
|
+
# monitoring, reporting and error-recovery or restart of the ingest.
|
23
|
+
# The initial value for this attribute is :START.
|
24
|
+
# - parent: [Object|nil] a link to a parent work item. Work items can be organized in any hierarchy you think is
|
25
|
+
# relevant for your workflow (e.g. directory[/directory...]/file/line or library/section/book/page). Of course
|
26
|
+
# hierarchies are not mandatory.
|
27
|
+
# - items: [Array] a list of child work items. see above.
|
28
|
+
# - options: [Hash] a set of options for the task chain on how to deal with this work item. This attribute can be
|
29
|
+
# used to fine-tune the behaviour of tasks for a particular work item.
|
30
|
+
# - properties: [Hash] a set of properties, typically collected during the workflow processing and used to store
|
31
|
+
# final or intermediate resulst of tasks. The ::Lias::Ingester::FileItem module uses this attribute to store the
|
32
|
+
# properties (e.g. size, checksum, ...) of the file it represents.
|
33
|
+
# - log_history: [Array] a list of all logging messages collected for this work item. Whenever a task logs a message
|
34
|
+
# it will automatically be registered for the work item that it is processing or for the work item that was
|
35
|
+
# supplied as the first argument.
|
36
|
+
# - status_log: [Array] a list of all status changes the work item went through.
|
37
|
+
# - summary: [Hash] collected statistics about the ingest for the work item and its children. This structure will
|
38
|
+
# be filled in by the included task ::Lias::Ingester::Tasks::Analyzer wich is appended to the workflow by default.
|
39
|
+
#
|
40
|
+
# The module is created so that it is possible to implement an ActiveRecord/Datamapper/... implementation easily.
|
41
|
+
# A simple in-memory implementation would require:
|
42
|
+
#
|
43
|
+
# attr_accessor :parent
|
44
|
+
# attr_accessor :items
|
45
|
+
# attr_accessor :options, :properties
|
46
|
+
# attr_accessor :log_history, :status_log
|
47
|
+
# attr_accessor :summary
|
48
|
+
#
|
49
|
+
# def initialize
|
50
|
+
# self.parent = nil
|
51
|
+
# self.items = []
|
52
|
+
# self.options = {}
|
53
|
+
# self.properties = {}
|
54
|
+
# self.log_history = []
|
55
|
+
# self.status_log = []
|
56
|
+
# self.summary = {}
|
57
|
+
# end
|
58
|
+
#
|
59
|
+
# protected
|
60
|
+
#
|
61
|
+
# ## Methods below should be adapted to match the implementation of the log arrays
|
62
|
+
#
|
63
|
+
# def add_log_entry(msg)
|
64
|
+
# self.log_history << msg.merge(c_at: ::Time.now)
|
65
|
+
# end
|
66
|
+
#
|
67
|
+
# def add_status_log(message, tasklist = nil)
|
68
|
+
# self.status_log << { c_at: ::Time.now, tasklist: tasklist, text: message }.cleanup
|
69
|
+
# end
|
70
|
+
#
|
71
|
+
#
|
72
|
+
module WorkItem
|
73
|
+
include Enumerable
|
74
|
+
include Libis::Workflow::Status
|
75
|
+
include Libis::Workflow::Base::Logging
|
76
|
+
|
77
|
+
# String representation of the identity of the work item.
|
78
|
+
#
|
79
|
+
# You may want to overwrite this method as it tries the :name property or whatever #inspect returns if that
|
80
|
+
# failes. Typically this should return the key value, file name or id number. If that's what your :name property
|
81
|
+
# contains, you're fine.
|
82
|
+
#
|
83
|
+
# @return [String] string identification for this work item.
|
84
|
+
def name
|
85
|
+
# noinspection RubyResolve
|
86
|
+
self.properties[:name] || self.inspect
|
87
|
+
end
|
88
|
+
|
89
|
+
def to_s;
|
90
|
+
self.name;
|
91
|
+
end
|
92
|
+
|
93
|
+
def names
|
94
|
+
(self.parent.names rescue Array.new).push(name).compact
|
95
|
+
end
|
96
|
+
|
97
|
+
def namepath;
|
98
|
+
self.names.join('/');
|
99
|
+
end
|
100
|
+
|
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
|
104
|
+
# data. Typical use is when extra file items are created by a task and need to be stored on disk. The default
|
105
|
+
# implementation URL-encodes (%xx) all characters except alphanumeric, '.' and '-'.
|
106
|
+
#
|
107
|
+
# @return [String] file name
|
108
|
+
def to_filename
|
109
|
+
self.to_s.gsub(/[^\w.-]/) { |s| '%%%02x' % s.ord }
|
110
|
+
end
|
111
|
+
|
112
|
+
# Iterates over the work item clients and invokes code on each of them.
|
113
|
+
def each
|
114
|
+
self.items.each { |item| yield item }
|
115
|
+
end
|
116
|
+
|
117
|
+
# Add a child work item
|
118
|
+
#
|
119
|
+
# @param [WorkItem] item to be added to the child list :items
|
120
|
+
def add_item(item)
|
121
|
+
return self unless item and item.is_a? Libis::Workflow::Base::WorkItem
|
122
|
+
self.items << item
|
123
|
+
item.parent = self
|
124
|
+
self.save!
|
125
|
+
item.save!
|
126
|
+
self
|
127
|
+
end
|
128
|
+
|
129
|
+
alias_method :<<, :add_item
|
130
|
+
|
131
|
+
def get_items
|
132
|
+
self.items.dup
|
133
|
+
end
|
134
|
+
|
135
|
+
def item_count
|
136
|
+
self.items.size
|
137
|
+
end
|
138
|
+
|
139
|
+
# Return item's parent
|
140
|
+
# @return [Libis::Workflow::Base::WorkItem]
|
141
|
+
def get_parent
|
142
|
+
self.parent
|
143
|
+
end
|
144
|
+
|
145
|
+
# go up the hierarchy and return the topmost work item
|
146
|
+
#
|
147
|
+
# @return [Libis::Workflow::Base::WorkItem]
|
148
|
+
def get_root
|
149
|
+
self.get_parent && self.get_parent.is_a?(Libis::Workflow::Base::WorkItem) && self.get_parent.get_root || self
|
150
|
+
end
|
151
|
+
|
152
|
+
# Get the top
|
153
|
+
#
|
154
|
+
# @return [Libis::Workflow::Base::Run]
|
155
|
+
def get_run
|
156
|
+
return self if self.is_a?(Libis::Workflow::Base::Run)
|
157
|
+
self.get_parent && self.get_parent.get_run || nil
|
158
|
+
end
|
159
|
+
|
160
|
+
# Dummy method. It is a placeholder for DB backed implementations. Wherever appropriate WorkItem#save will be
|
161
|
+
# called to save the current item's state. If state needs to persisted, you should override this method or make
|
162
|
+
# sure your persistence layer implements it in your class.
|
163
|
+
def save
|
164
|
+
end
|
165
|
+
|
166
|
+
# Dummy method. It is a placeholder for DB backed implementations. Wherever appropriate WorkItem#save will be
|
167
|
+
# called to save the current item's state. If state needs to persisted, you should override this method or make
|
168
|
+
# sure your persistence layer implements it in your class.
|
169
|
+
def save!
|
170
|
+
end
|
171
|
+
|
172
|
+
end
|
173
|
+
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'libis/tools/parameter'
|
4
|
+
require 'libis/workflow/task_group'
|
5
|
+
|
6
|
+
module Libis
|
7
|
+
module Workflow
|
8
|
+
module Base
|
9
|
+
|
10
|
+
# This is the base module for Workflows.
|
11
|
+
#
|
12
|
+
# This module lacks the implementation for the data attributes. It functions as an interface that describes the
|
13
|
+
# common functionality regardless of the storage implementation. These attributes require some implementation:
|
14
|
+
#
|
15
|
+
# - name: [String] the name of the Workflow. The name will be used to identify the workflow. Each time a workflow
|
16
|
+
# is executed, a Run will be created. The Run will get a name that starts with the workflow name and ends with
|
17
|
+
# the date and time the Run was started. As such this name attribute serves as an identifier and should be
|
18
|
+
# treated as such. If possible is should be unique.
|
19
|
+
# - description: [String] more information about the workflow.
|
20
|
+
# - config: [Hash] detailed configuration for the workflow. The application assumes it behaves as a Hash and will
|
21
|
+
# access it with [], merge! and delete methods. If your implementation decides to implement it with another
|
22
|
+
# object, it should implement above methods. The config Hash requires the following keys:
|
23
|
+
# - input: [Hash] all input parameter definitions where the key is the parameter name and the value is another
|
24
|
+
# Hash with arguments for the parameter definition. It typically contains the following arguments:
|
25
|
+
# - default: default value if no value specified when the workflow is executed
|
26
|
+
# - propagate_to: the task name (or path) and parameter name that any set value for this parameter will be
|
27
|
+
# propagated to. The syntax is <task name|task path>[#<parameter name>]. It the #<parameter name> part
|
28
|
+
# is not present, the same name as the input parameter is used. If you want to push the value to
|
29
|
+
# multiple task parameters, you can either supply an array of propagate paths or put them in a string
|
30
|
+
# separated by a ','.
|
31
|
+
# - tasks: [Array] task definitions that define the order in which the tasks should be executed for the workflow.
|
32
|
+
# A task definition is a Hash with the following values:
|
33
|
+
# - class: [String] the class name of the task including the module names
|
34
|
+
# - name: [String] optional if class is present. A friendly name for the task that will be used in the logs.
|
35
|
+
# - tasks: [Array] a list of subtask defintions for this task.
|
36
|
+
#
|
37
|
+
# Additionally the task definition Hash may specify values for any other parameter that the task knows of.
|
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
|
+
#
|
41
|
+
# A task definition does not require to have a 'class' entry. If not present the default
|
42
|
+
# ::Libis::Workflow::TaskGroup class will be instatiated. It will do nothing itself, but will execute the
|
43
|
+
# subtasks on the item(s). In such case a 'name' is mandatory.
|
44
|
+
#
|
45
|
+
# These values should be set by calling the #configure method which takes a Hash as argument with :name,
|
46
|
+
# :description, :input and :tasks keys.
|
47
|
+
#
|
48
|
+
# A minimal in-memory implementation could be:
|
49
|
+
#
|
50
|
+
# class Workflow
|
51
|
+
# include ::Libis::Workflow::Base::Workflow
|
52
|
+
#
|
53
|
+
# attr_accessor :name, :description, :config
|
54
|
+
#
|
55
|
+
# def initialize
|
56
|
+
# @name = ''
|
57
|
+
# @description = ''
|
58
|
+
# @config = Hash.new
|
59
|
+
# end
|
60
|
+
#
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
module Workflow
|
64
|
+
|
65
|
+
module ClassMethods
|
66
|
+
def require_all
|
67
|
+
Config.require_all(File.join(File.dirname(__FILE__), '..', 'tasks'))
|
68
|
+
# noinspection RubyResolve
|
69
|
+
Config.require_all(Config.taskdir)
|
70
|
+
# noinspection RubyResolve
|
71
|
+
Config.require_all(Config.itemdir)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.included(base)
|
76
|
+
|
77
|
+
base.extend ClassMethods
|
78
|
+
end
|
79
|
+
|
80
|
+
def configure(cfg)
|
81
|
+
self.name = cfg.delete(:name) || self.class.name
|
82
|
+
self.description = cfg.delete(:description) || ''
|
83
|
+
self.config.merge! input: {}, tasks: []
|
84
|
+
self.config.merge! cfg
|
85
|
+
|
86
|
+
self.class.require_all
|
87
|
+
|
88
|
+
unless self.config[:tasks].last[:class] && self.config[:tasks].last[:class].split('::').last == 'Analyzer'
|
89
|
+
self.config[:tasks] << {class: '::Libis::Workflow::Tasks::Analyzer'}
|
90
|
+
end
|
91
|
+
|
92
|
+
self.config
|
93
|
+
end
|
94
|
+
|
95
|
+
def input
|
96
|
+
self.config[:input].inject({}) do |hash, input_def|
|
97
|
+
name = input_def.first.to_sym
|
98
|
+
default = input_def.last[:default]
|
99
|
+
parameter = ::Libis::Tools::Parameter.new name, default
|
100
|
+
input_def.last.each { |k, v| parameter[k.to_sym] = v }
|
101
|
+
hash[name] = parameter
|
102
|
+
hash
|
103
|
+
end
|
104
|
+
rescue
|
105
|
+
{}
|
106
|
+
end
|
107
|
+
|
108
|
+
# @param [Hash] options
|
109
|
+
def prepare_input(options)
|
110
|
+
result = {}
|
111
|
+
self.input.each do |key, parameter|
|
112
|
+
if options.has_key?(key)
|
113
|
+
value = parameter.parse(options[key])
|
114
|
+
elsif !parameter[:default].nil?
|
115
|
+
value = parameter[:default]
|
116
|
+
else
|
117
|
+
next
|
118
|
+
end
|
119
|
+
propagate_to = []
|
120
|
+
propagate_to = parameter[:propagate_to] if parameter[:propagate_to].is_a? Array
|
121
|
+
propagate_to = parameter[:propagate_to].split(/[\s,;]+/) if parameter[:propagate_to].is_a? String
|
122
|
+
result[key] = value if propagate_to.empty?
|
123
|
+
propagate_to.each do |target|
|
124
|
+
task_name, param_name = target.split('#')
|
125
|
+
param_name ||= key
|
126
|
+
result[task_name] ||= {}
|
127
|
+
result[task_name][param_name.to_sym] = value
|
128
|
+
end
|
129
|
+
end
|
130
|
+
result
|
131
|
+
end
|
132
|
+
|
133
|
+
def tasks(parent = nil)
|
134
|
+
self.config[:tasks].map do |cfg|
|
135
|
+
instantize_task(parent || self, cfg)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def instantize_task(parent, cfg)
|
140
|
+
task_class = Libis::Workflow::TaskGroup
|
141
|
+
task_class = cfg[:class].constantize if cfg[:class]
|
142
|
+
# noinspection RubyArgCount
|
143
|
+
task_instance = task_class.new(parent, cfg)
|
144
|
+
cfg[:tasks] && cfg[:tasks].map do |task_cfg|
|
145
|
+
task_instance << instantize_task(task_instance, task_cfg)
|
146
|
+
end
|
147
|
+
task_instance
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'libis/tools/config'
|
4
|
+
|
5
|
+
module Libis
|
6
|
+
module Workflow
|
7
|
+
|
8
|
+
# noinspection RubyConstantNamingConvention
|
9
|
+
Config = ::Libis::Tools::Config
|
10
|
+
|
11
|
+
Config.define_singleton_method(:require_all) do |dir|
|
12
|
+
Dir.glob(File.join(dir, '*.rb')).each do |filename|
|
13
|
+
# noinspection RubyResolve
|
14
|
+
require filename
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Config.require_all(File.join(File.dirname(__FILE__), 'tasks'))
|
19
|
+
Config[:workdir] = './work'
|
20
|
+
Config[:taskdir] = './tasks'
|
21
|
+
Config[:itemdir] = './items'
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -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
|