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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: ee7675981468e33b70d8f03bb43ade771225a34b
4
- data.tar.gz: 2770f1c82005d427bcc29c8aa08d84f395f971f8
3
+ metadata.gz: cf536dca7ee73ba80bf51318292bf07e006d10a7
4
+ data.tar.gz: 3a5bfd9e58632e117a86de20ea949c469b2f1801
5
5
  SHA512:
6
- metadata.gz: 557aa30bcff78d439b02008c86c5a1a4f43264fb23d1405012e0f3430006581c5ee2ce092aa0421fa89357b9ff7576b51cca6788f0fc15d22ca5e99be8f3ab75
7
- data.tar.gz: dd1c1fabe78d2ca9a7ad0f89ce6ae0975a478e1383a904119fa6a55fdc2111340f3aa2fbdc9deb56faf43a8d52ac88f9ec18bc61ae6925e17e7f49e7371cb9ee
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 tasks is predefined:
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::Task' with the given name will be instantiated, which simply
112
- iterates over the child items of the given work item and performs each sub-task on each of the child items. If a 'class'
113
- value is given, an instance of that class will be created and the task will be handed the work item to process on. See
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. If the item was replaced by a new item or if the
280
- item tree has changed, it is recommended to return the new item. After a successful return the item's status will be
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
- status will be set to failed for the given task and the exception message will be printed in the error log. Processing
284
- will continue with the next item. This action is recommended for temporary or recoverable errors.
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
- failure status will be escalated to all items up the item hierarchy. Due to the escalating behaviour, no message is
287
- printed in the error log automatically, so it is up to the task to an appropriate log the error itself.
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
- given task and the exception message to be logged in the error log. It will attempt to process the other items.
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
- #### Aborting the task whenever any item fails
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
- #### Always running a task, even on items that failed in an earlier stage in the workflow
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
- The opposite of aborting on error, the parameter 'always_run' can be set to true to force a task to process the items
328
- even if the item has a failed status from a previous task. Note that this will cause the item's status to no longer be
329
- failing until a task fails on the item again. The old failed status will be tracable in the status history.
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 parameter only configures the task on which it is set. If the task is a subtask it will only be forced to run if
332
- any of it's previous sibling tasks failed. In order to force the run, even if the item failed in another branch of the
333
- task hiearchy, the parent tasks should have their 'always_run' parameter also set to true.
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
- def run
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 save version of the to_s output. The output should be safe to use as a file name to store work item
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
- alias :<< :add_item
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', 'always_run', 'abort_on_error'. For more information about these see
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::Task class will be instatiated. It will do nothing itself, but will execute the
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 = Task
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 rescue nil
146
+ end
148
147
  task_instance
149
148
  end
150
149
 
@@ -1,4 +1,5 @@
1
1
  require_relative 'base/logger'
2
+ require_relative 'base/logging'
2
3
  require_relative 'base/work_item'
3
4
  require_relative 'base/file_item'
4
5
  require_relative 'base/dir_item'
@@ -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