taskinator 0.0.15 → 0.0.16
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +77 -0
- data/Gemfile.lock +2 -1
- data/README.md +1 -1
- data/lib/taskinator/api.rb +3 -1
- data/lib/taskinator/create_process_worker.rb +60 -0
- data/lib/taskinator/definition.rb +47 -8
- data/lib/taskinator/job_worker.rb +2 -2
- data/lib/taskinator/logger.rb +13 -75
- data/lib/taskinator/persistence.rb +48 -40
- data/lib/taskinator/process.rb +13 -6
- data/lib/taskinator/process_worker.rb +3 -1
- data/lib/taskinator/queues/delayed_job.rb +12 -5
- data/lib/taskinator/queues/resque.rb +16 -5
- data/lib/taskinator/queues/sidekiq.rb +14 -5
- data/lib/taskinator/queues.rb +12 -0
- data/lib/taskinator/task.rb +21 -8
- data/lib/taskinator/task_worker.rb +3 -1
- data/lib/taskinator/tasks.rb +1 -1
- data/lib/taskinator/version.rb +1 -1
- data/lib/taskinator.rb +20 -1
- data/spec/examples/queue_adapter_examples.rb +10 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/support/mock_definition.rb +22 -0
- data/spec/support/spec_support.rb +3 -1
- data/spec/support/test_definition.rb +3 -0
- data/spec/support/test_process.rb +2 -0
- data/spec/support/test_queue.rb +7 -1
- data/spec/support/test_task.rb +2 -0
- data/spec/taskinator/api_spec.rb +2 -2
- data/spec/taskinator/create_process_worker_spec.rb +44 -0
- data/spec/taskinator/definition/builder_spec.rb +5 -5
- data/spec/taskinator/definition_spec.rb +103 -6
- data/spec/taskinator/executor_spec.rb +1 -1
- data/spec/taskinator/job_worker_spec.rb +3 -3
- data/spec/taskinator/persistence_spec.rb +12 -19
- data/spec/taskinator/process_spec.rb +29 -5
- data/spec/taskinator/process_worker_spec.rb +3 -3
- data/spec/taskinator/queues/delayed_job_spec.rb +23 -6
- data/spec/taskinator/queues/resque_spec.rb +27 -6
- data/spec/taskinator/queues/sidekiq_spec.rb +28 -10
- data/spec/taskinator/task_spec.rb +80 -7
- data/spec/taskinator/task_worker_spec.rb +3 -3
- data/spec/taskinator/{intermodal_spec.rb → taskinator_spec.rb} +35 -1
- data/spec/taskinator/tasks_spec.rb +1 -2
- data/taskinator.gemspec +17 -15
- metadata +30 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1c4844721e58ee539e62099b0cf6a9181bad692b
|
4
|
+
data.tar.gz: 00d8e01dbf3b6170231946bbeb18856bde5a6e8d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e2b21e17464032c8e59cbe5909ba1121b83e9cb77623f0314352563e416f10056b11e36e30717a3091ffa725d5f4ae6f5cc7f337bedf50b7cc41e606721ca672
|
7
|
+
data.tar.gz: 9e342b0f899929985f87c86e063b503a310227adec593e6506a591ac8acc7ffc3cebd81b0159b091264f2da33574d842c05721a2d3c55c43759ec463ad6484e9
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
v0.0.16 - 25 Jun 2015
|
2
|
+
---
|
3
|
+
Added ability to enqueue the creation of processes; added a new worker, `CreateProcessWorker`
|
4
|
+
Added support for instrumentation
|
5
|
+
Improvements to error handling
|
6
|
+
Bug fix for the persistence of the `queue` attribute for `Process` and `Task`
|
7
|
+
Code clean up and additional specs added
|
8
|
+
|
9
|
+
v0.0.15 - 28 May 2015
|
10
|
+
---
|
11
|
+
Added ability to specify the queue to use when enqueing processes, tasks and jobs
|
12
|
+
Improvements to specs for testing with sidekiq; added `rspec-sidekiq` as development dependency
|
13
|
+
Gem dependencies updated as per Gemnasium advisory
|
14
|
+
|
15
|
+
v0.0.14 - 12 May 2015
|
16
|
+
---
|
17
|
+
Bug fix for fail! methods
|
18
|
+
Bug fix to parameter handling by for_each method
|
19
|
+
|
20
|
+
v0.0.13 - 11 May 2015
|
21
|
+
---
|
22
|
+
Bug fix to `Taskinator::Api` for listing of processes; should only include top-level processes
|
23
|
+
Gem dependencies updated as per Gemnasium advisory
|
24
|
+
|
25
|
+
v0.0.12 - 20 Apr 2015
|
26
|
+
---
|
27
|
+
Gem dependencies updated as per Gemnasium advisory
|
28
|
+
|
29
|
+
v0.0.11 - 2 Mar 2015
|
30
|
+
---
|
31
|
+
Gem dependencies updated as per Gemnasium advisory
|
32
|
+
|
33
|
+
v0.0.10 - 26 Feb 2015
|
34
|
+
---
|
35
|
+
Documentation updates
|
36
|
+
|
37
|
+
v0.0.9 - 19 Dec 2014
|
38
|
+
---
|
39
|
+
Various bug fixes
|
40
|
+
Added error logging
|
41
|
+
Workflow states now include `complete` event
|
42
|
+
Gem dependencies updated as per Gemnasium advisory
|
43
|
+
|
44
|
+
v0.0.8 - 11 Nov 2014
|
45
|
+
---
|
46
|
+
Added support for argument chaining with `for_each` and `transform`
|
47
|
+
Documentation updates
|
48
|
+
Gem dependencies updated as per Gemnasium advisory
|
49
|
+
|
50
|
+
v0.0.7 - 16 Oct 2014
|
51
|
+
---
|
52
|
+
Added better option handling; introduced `option?(key)` method
|
53
|
+
Added support for definining the expected arguments for a process
|
54
|
+
Gem dependencies updated as per Gemnasium advisory
|
55
|
+
|
56
|
+
v0.0.5 - 17 Sep 2014
|
57
|
+
---
|
58
|
+
Various of bug fixes
|
59
|
+
Improved error handling
|
60
|
+
Added logging for queuing of processes, tasks and jobs
|
61
|
+
|
62
|
+
v0.0.4 - 12 Sep 2014
|
63
|
+
---
|
64
|
+
Improvements to serialization; make use of GlobalID functionality
|
65
|
+
Added support for "job" tasks; reusing existing workers as tasks
|
66
|
+
|
67
|
+
v0.0.3 - 2 Sep 2014
|
68
|
+
---
|
69
|
+
Added failure steps to workflow of processes and tasks
|
70
|
+
|
71
|
+
v0.0.2 - 12 Aug 2014
|
72
|
+
---
|
73
|
+
Refactored how tasks are defined in definitions
|
74
|
+
|
75
|
+
v0.0.1 - 12 Aug 2014
|
76
|
+
---
|
77
|
+
Initial release
|
data/Gemfile.lock
CHANGED
@@ -8,7 +8,7 @@ GIT
|
|
8
8
|
PATH
|
9
9
|
remote: .
|
10
10
|
specs:
|
11
|
-
taskinator (0.0.
|
11
|
+
taskinator (0.0.16)
|
12
12
|
connection_pool (>= 2.2.0)
|
13
13
|
json (>= 1.8.2)
|
14
14
|
redis (>= 3.2.1)
|
@@ -134,6 +134,7 @@ PLATFORMS
|
|
134
134
|
ruby
|
135
135
|
|
136
136
|
DEPENDENCIES
|
137
|
+
activesupport (>= 4.0.0)
|
137
138
|
bundler (>= 1.6.0)
|
138
139
|
coveralls (>= 0.7.0)
|
139
140
|
delayed_job (>= 4.0.0)
|
data/README.md
CHANGED
@@ -543,7 +543,7 @@ In reality, each task is executed by a worker process, possibly on another host,
|
|
543
543
|
To monitor the state of the processes, use the `Taskinator::Api::Processes` class. This is still a work in progress.
|
544
544
|
|
545
545
|
```ruby
|
546
|
-
processes = Taskinator::Api::Processes.new
|
546
|
+
processes = Taskinator::Api::Processes.new
|
547
547
|
processes.each do |process|
|
548
548
|
# => output the unique process identifier and current state
|
549
549
|
puts [:process, process.uuid, process.current_state.name]
|
data/lib/taskinator/api.rb
CHANGED
@@ -0,0 +1,60 @@
|
|
1
|
+
module Taskinator
|
2
|
+
class CreateProcessWorker
|
3
|
+
|
4
|
+
attr_reader :definition
|
5
|
+
attr_reader :uuid
|
6
|
+
attr_reader :args
|
7
|
+
|
8
|
+
def initialize(definition_name, uuid, args)
|
9
|
+
|
10
|
+
# convert to the module
|
11
|
+
@definition = constantize(definition_name)
|
12
|
+
|
13
|
+
# this will be uuid of the created process
|
14
|
+
@uuid = uuid
|
15
|
+
|
16
|
+
# convert to the typed arguments
|
17
|
+
@args = Taskinator::Persistence.deserialize(args)
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
def perform
|
22
|
+
@definition._create_process_(*@args, :uuid => @uuid).enqueue!
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# borrowed from activesupport/lib/active_support/inflector/methods.rb
|
28
|
+
def constantize(camel_cased_word)
|
29
|
+
names = camel_cased_word.split('::')
|
30
|
+
|
31
|
+
# Trigger a built-in NameError exception including the ill-formed constant in the message.
|
32
|
+
Object.const_get(camel_cased_word) if names.empty?
|
33
|
+
|
34
|
+
# Remove the first blank element in case of '::ClassName' notation.
|
35
|
+
names.shift if names.size > 1 && names.first.empty?
|
36
|
+
|
37
|
+
names.inject(Object) do |constant, name|
|
38
|
+
if constant == Object
|
39
|
+
constant.const_get(name)
|
40
|
+
else
|
41
|
+
candidate = constant.const_get(name)
|
42
|
+
next candidate if constant.const_defined?(name, false)
|
43
|
+
next candidate unless Object.const_defined?(name)
|
44
|
+
|
45
|
+
# Go down the ancestors to check if it is owned directly. The check
|
46
|
+
# stops when we reach Object or the end of ancestors tree.
|
47
|
+
constant = constant.ancestors.inject do |const, ancestor|
|
48
|
+
break const if ancestor == Object
|
49
|
+
break ancestor if ancestor.const_defined?(name, false)
|
50
|
+
const
|
51
|
+
end
|
52
|
+
|
53
|
+
# owner is in Object, so raise
|
54
|
+
constant.const_get(name, false)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -1,9 +1,17 @@
|
|
1
1
|
module Taskinator
|
2
2
|
module Definition
|
3
|
-
|
3
|
+
|
4
|
+
# errors
|
5
|
+
class ProcessUndefinedError < StandardError; end
|
6
|
+
class ProcessAlreadyDefinedError < StandardError; end
|
7
|
+
|
8
|
+
# for backward compatibility
|
9
|
+
UndefinedProcessError = ProcessUndefinedError
|
4
10
|
|
5
11
|
# defines a process
|
6
12
|
def define_process(*arg_list, &block)
|
13
|
+
raise ProcessAlreadyDefinedError if respond_to?(:_create_process_)
|
14
|
+
|
7
15
|
define_singleton_method :_create_process_ do |args, options={}|
|
8
16
|
|
9
17
|
# TODO: better validation of arguments
|
@@ -12,28 +20,59 @@ module Taskinator
|
|
12
20
|
|
13
21
|
raise ArgumentError, "wrong number of arguments (#{args.length} for #{arg_list.length})" if args.length < arg_list.length
|
14
22
|
|
15
|
-
process = Process.define_sequential_process_for(self)
|
16
|
-
Builder.new(process, self, *args).instance_eval(&block)
|
17
|
-
process.save
|
23
|
+
process = Process.define_sequential_process_for(self, options)
|
18
24
|
|
19
|
-
#
|
20
|
-
|
25
|
+
# this may take long... up to users definition
|
26
|
+
Taskinator.instrumenter.instrument(:create_process, :uuid => process.uuid) do
|
27
|
+
Builder.new(process, self, *args).instance_eval(&block)
|
28
|
+
end
|
29
|
+
|
30
|
+
# instrument separately
|
31
|
+
Taskinator.instrumenter.instrument(:save_process, :uuid => process.uuid) do
|
32
|
+
process.save
|
33
|
+
# if this is a root process, then add it to the list
|
34
|
+
Persistence.add_process_to_list(process) unless options[:subprocess]
|
35
|
+
end
|
21
36
|
|
22
37
|
process
|
23
38
|
end
|
24
39
|
end
|
25
40
|
|
41
|
+
attr_accessor :queue
|
42
|
+
|
43
|
+
#
|
26
44
|
# creates an instance of the process
|
27
45
|
# NOTE: the supplied @args are serialized and ultimately passed to each method of the defined process
|
46
|
+
#
|
28
47
|
def create_process(*args)
|
29
|
-
|
48
|
+
assert_valid_process_module
|
30
49
|
_create_process_(args)
|
31
50
|
end
|
32
51
|
|
52
|
+
#
|
53
|
+
# returns a placeholder process, with the uuid attribute of the
|
54
|
+
# actual process. the callee can call `reload` if required to
|
55
|
+
# get the actual process, once it has been built by the CreateProcessWorker
|
56
|
+
#
|
57
|
+
def create_process_remotely(*args)
|
58
|
+
assert_valid_process_module
|
59
|
+
uuid = SecureRandom.uuid
|
60
|
+
Taskinator.queue.enqueue_create_process(self, uuid, args)
|
61
|
+
|
62
|
+
Taskinator::Persistence::LazyLoader.new(Taskinator::Process, uuid)
|
63
|
+
end
|
64
|
+
|
33
65
|
def create_sub_process(*args)
|
34
|
-
|
66
|
+
assert_valid_process_module
|
35
67
|
_create_process_(args, :subprocess => true)
|
36
68
|
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def assert_valid_process_module
|
73
|
+
raise ProcessUndefinedError unless respond_to?(:_create_process_)
|
74
|
+
end
|
75
|
+
|
37
76
|
end
|
38
77
|
end
|
39
78
|
|
data/lib/taskinator/logger.rb
CHANGED
@@ -5,11 +5,6 @@
|
|
5
5
|
# the LGPLv3 license. Please see <http://www.gnu.org/licenses/lgpl-3.0.html>
|
6
6
|
# for license text.
|
7
7
|
#
|
8
|
-
# Sidekiq Pro has a commercial-friendly license allowing private forks
|
9
|
-
# and modifications of Sidekiq. Please see http://sidekiq.org/pro/ for
|
10
|
-
# more detail. You can find the commercial license terms in COMM-LICENSE.
|
11
|
-
#
|
12
|
-
|
13
8
|
require 'time'
|
14
9
|
require 'logger'
|
15
10
|
|
@@ -28,82 +23,25 @@ module Taskinator
|
|
28
23
|
end
|
29
24
|
end
|
30
25
|
|
31
|
-
|
32
|
-
begin
|
33
|
-
Thread.current[:taskinator_context] = msg
|
34
|
-
yield
|
35
|
-
ensure
|
36
|
-
Thread.current[:taskinator_context] = nil
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
def self.initialize_logger(log_target = STDOUT)
|
41
|
-
oldlogger = defined?(@logger) ? @logger : nil
|
42
|
-
@logger = Logger.new(log_target)
|
43
|
-
@logger.level = Logger::INFO
|
44
|
-
@logger.formatter = Pretty.new
|
45
|
-
oldlogger.close if oldlogger && !$TESTING # don't want to close testing's STDOUT logging
|
46
|
-
@logger
|
47
|
-
end
|
48
|
-
|
49
|
-
def self.logger
|
50
|
-
defined?(@logger) ? @logger : initialize_logger
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.logger=(log)
|
54
|
-
@logger = (log ? log : Logger.new('/dev/null'))
|
55
|
-
end
|
56
|
-
|
57
|
-
# This reopens ALL logfiles in the process that have been rotated
|
58
|
-
# using logrotate(8) (without copytruncate) or similar tools.
|
59
|
-
# A +File+ object is considered for reopening if it is:
|
60
|
-
# 1) opened with the O_APPEND and O_WRONLY flags
|
61
|
-
# 2) the current open file handle does not match its original open path
|
62
|
-
# 3) unbuffered (as far as userspace buffering goes, not O_SYNC)
|
63
|
-
# Returns the number of files reopened
|
64
|
-
def self.reopen_logs
|
65
|
-
to_reopen = []
|
66
|
-
append_flags = File::WRONLY | File::APPEND
|
26
|
+
class << self
|
67
27
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
28
|
+
def initialize_logger(log_target = STDOUT)
|
29
|
+
oldlogger = defined?(@logger) ? @logger : nil
|
30
|
+
@logger = Logger.new(log_target)
|
31
|
+
@logger.level = Logger::INFO
|
32
|
+
@logger.formatter = Pretty.new
|
33
|
+
oldlogger.close if oldlogger && !$TESTING # don't want to close testing's STDOUT logging
|
34
|
+
@logger
|
75
35
|
end
|
76
36
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
fp.stat
|
81
|
-
rescue IOError, Errno::EBADF
|
82
|
-
next
|
83
|
-
end
|
84
|
-
|
85
|
-
begin
|
86
|
-
b = File.stat(fp.path)
|
87
|
-
next if orig_st.ino == b.ino && orig_st.dev == b.dev
|
88
|
-
rescue Errno::ENOENT
|
89
|
-
end
|
37
|
+
def logger
|
38
|
+
defined?(@logger) ? @logger : initialize_logger
|
39
|
+
end
|
90
40
|
|
91
|
-
|
92
|
-
|
93
|
-
fp.sync = true
|
94
|
-
nr += 1
|
95
|
-
rescue IOError, Errno::EBADF
|
96
|
-
# not much we can do...
|
97
|
-
end
|
41
|
+
def logger=(log)
|
42
|
+
@logger = (log ? log : Logger.new('/dev/null'))
|
98
43
|
end
|
99
|
-
nr
|
100
|
-
rescue RuntimeError => ex
|
101
|
-
# RuntimeError: ObjectSpace is disabled; each_object will only work with Class, pass -X+O to enable
|
102
|
-
puts "Unable to reopen logs: #{ex.message}"
|
103
|
-
end
|
104
44
|
|
105
|
-
def logger
|
106
|
-
Taskinator::Logging.logger
|
107
45
|
end
|
108
46
|
end
|
109
47
|
end
|
@@ -94,13 +94,14 @@ module Taskinator
|
|
94
94
|
|
95
95
|
# persists the error information
|
96
96
|
def fail(error=nil)
|
97
|
-
return unless error
|
97
|
+
return unless error && error.is_a?(Exception)
|
98
|
+
|
98
99
|
Taskinator.redis do |conn|
|
99
100
|
conn.hmset(
|
100
101
|
self.key,
|
101
102
|
:error_type, error.class.name,
|
102
103
|
:error_message, error.message,
|
103
|
-
:error_backtrace, JSON.generate(error.backtrace)
|
104
|
+
:error_backtrace, JSON.generate(error.backtrace || [])
|
104
105
|
)
|
105
106
|
end
|
106
107
|
end
|
@@ -182,23 +183,8 @@ module Taskinator
|
|
182
183
|
|
183
184
|
def visit_args(attribute)
|
184
185
|
values = @instance.send(attribute)
|
185
|
-
|
186
|
-
|
187
|
-
if values.is_a?(Array)
|
188
|
-
|
189
|
-
values = values.collect {|value|
|
190
|
-
value.respond_to?(:global_id) ? value.global_id : value
|
191
|
-
}
|
192
|
-
|
193
|
-
elsif values.is_a?(Hash)
|
194
|
-
|
195
|
-
values.each {|key, value|
|
196
|
-
values[key] = value.global_id if value.respond_to?(:global_id)
|
197
|
-
}
|
198
|
-
|
199
|
-
end
|
200
|
-
|
201
|
-
@hmset += [attribute, YAML.dump(values)]
|
186
|
+
yaml = Taskinator::Persistence.serialize(values)
|
187
|
+
@hmset += [attribute, yaml]
|
202
188
|
end
|
203
189
|
end
|
204
190
|
|
@@ -289,30 +275,12 @@ module Taskinator
|
|
289
275
|
def visit_args(attribute)
|
290
276
|
yaml = @attribute_values[attribute]
|
291
277
|
if yaml
|
292
|
-
values =
|
293
|
-
|
294
|
-
# special case for models, so find model
|
295
|
-
if values.is_a?(Array)
|
296
|
-
|
297
|
-
values = values.collect {|value|
|
298
|
-
# is it a global id?
|
299
|
-
value.respond_to?(:model_id) && value.respond_to?(:find) ? value.find : value
|
300
|
-
}
|
301
|
-
|
302
|
-
elsif values.is_a?(Hash)
|
303
|
-
|
304
|
-
values.each {|key, value|
|
305
|
-
# is it a global id?
|
306
|
-
values[key] = value.find if value.respond_to?(:model_id) && value.respond_to?(:find)
|
307
|
-
}
|
308
|
-
|
309
|
-
end
|
310
|
-
|
278
|
+
values = Taskinator::Persistence.deserialize(yaml)
|
311
279
|
@instance.instance_variable_set("@#{attribute}", values)
|
312
280
|
end
|
313
281
|
end
|
314
282
|
|
315
|
-
|
283
|
+
private
|
316
284
|
|
317
285
|
#
|
318
286
|
# creates a proxy for the instance which
|
@@ -342,17 +310,57 @@ module Taskinator
|
|
342
310
|
# E.g. this is useful for tasks which refer to their parent processes
|
343
311
|
#
|
344
312
|
|
345
|
-
def initialize(type, uuid, instance_cache)
|
313
|
+
def initialize(type, uuid, instance_cache={})
|
346
314
|
@type = type
|
347
315
|
@uuid = uuid
|
348
316
|
@instance_cache = instance_cache
|
349
317
|
end
|
350
318
|
|
319
|
+
attr_reader :uuid # shadows the real method, but will be the same!
|
320
|
+
|
321
|
+
# attempts to reload the actual process
|
322
|
+
def reload
|
323
|
+
@instance = nil
|
324
|
+
__getobj__
|
325
|
+
@instance ? true : false
|
326
|
+
end
|
327
|
+
|
351
328
|
def __getobj__
|
352
329
|
# only fetch the object as needed
|
353
330
|
# and memoize for subsequent calls
|
354
331
|
@instance ||= @type.fetch(@uuid, @instance_cache)
|
355
332
|
end
|
356
333
|
end
|
334
|
+
|
335
|
+
class << self
|
336
|
+
def serialize(values)
|
337
|
+
# special case, convert models to global id's
|
338
|
+
if values.is_a?(Array)
|
339
|
+
values = values.collect {|value|
|
340
|
+
value.respond_to?(:global_id) ? value.global_id : value
|
341
|
+
}
|
342
|
+
elsif values.is_a?(Hash)
|
343
|
+
values.each {|key, value|
|
344
|
+
values[key] = value.global_id if value.respond_to?(:global_id)
|
345
|
+
}
|
346
|
+
end
|
347
|
+
YAML.dump(values)
|
348
|
+
end
|
349
|
+
|
350
|
+
def deserialize(yaml)
|
351
|
+
values = YAML.load(yaml)
|
352
|
+
if values.is_a?(Array)
|
353
|
+
values = values.collect {|value|
|
354
|
+
(value.respond_to?(:model_id) && value.respond_to?(:find)) ? value.find : value
|
355
|
+
}
|
356
|
+
elsif values.is_a?(Hash)
|
357
|
+
values.each {|key, value|
|
358
|
+
values[key] = value.find if value.respond_to?(:model_id) && value.respond_to?(:find)
|
359
|
+
}
|
360
|
+
end
|
361
|
+
values
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
357
365
|
end
|
358
366
|
end
|
data/lib/taskinator/process.rb
CHANGED
@@ -20,6 +20,7 @@ module Taskinator
|
|
20
20
|
attr_reader :uuid
|
21
21
|
attr_reader :definition
|
22
22
|
attr_reader :options
|
23
|
+
attr_reader :queue
|
23
24
|
|
24
25
|
# in the case of sub process tasks, the containing task
|
25
26
|
attr_accessor :parent
|
@@ -28,13 +29,14 @@ module Taskinator
|
|
28
29
|
raise ArgumentError, 'definition' if definition.nil?
|
29
30
|
raise ArgumentError, "#{definition.name} does not extend the #{Definition.name} module" unless definition.kind_of?(Definition)
|
30
31
|
|
31
|
-
@uuid = SecureRandom.uuid
|
32
|
+
@uuid = options.delete(:uuid) || SecureRandom.uuid
|
32
33
|
@definition = definition
|
33
34
|
@options = options
|
35
|
+
@queue = options.delete(:queue)
|
34
36
|
end
|
35
37
|
|
36
38
|
def tasks
|
37
|
-
@tasks ||= Tasks.new
|
39
|
+
@tasks ||= Tasks.new
|
38
40
|
end
|
39
41
|
|
40
42
|
def accept(visitor)
|
@@ -43,6 +45,7 @@ module Taskinator
|
|
43
45
|
visitor.visit_type(:definition)
|
44
46
|
visitor.visit_tasks(tasks)
|
45
47
|
visitor.visit_args(:options)
|
48
|
+
visitor.visit_attribute(:queue)
|
46
49
|
end
|
47
50
|
|
48
51
|
def <=>(other)
|
@@ -53,10 +56,6 @@ module Taskinator
|
|
53
56
|
"#<#{self.class.name}:#{uuid}>"
|
54
57
|
end
|
55
58
|
|
56
|
-
def queue
|
57
|
-
options[:queue]
|
58
|
-
end
|
59
|
-
|
60
59
|
workflow do
|
61
60
|
state :initial do
|
62
61
|
event :enqueue, :transitions_to => :enqueued
|
@@ -201,5 +200,13 @@ module Taskinator
|
|
201
200
|
visitor.visit_attribute(:complete_on)
|
202
201
|
end
|
203
202
|
end
|
203
|
+
|
204
|
+
# reloads the process from storage
|
205
|
+
# NB: only implemented by LazyLoader so that
|
206
|
+
# the process can be lazy loaded, thereafter
|
207
|
+
# it has no effect
|
208
|
+
def reload
|
209
|
+
false
|
210
|
+
end
|
204
211
|
end
|
205
212
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module Taskinator
|
2
2
|
class ProcessWorker
|
3
|
+
attr_reader :uuid
|
4
|
+
|
3
5
|
def initialize(uuid)
|
4
6
|
@uuid = uuid
|
5
7
|
end
|
@@ -9,7 +11,7 @@ module Taskinator
|
|
9
11
|
return if process.paused? || process.cancelled?
|
10
12
|
begin
|
11
13
|
process.start!
|
12
|
-
rescue
|
14
|
+
rescue => e
|
13
15
|
Taskinator.logger.error(e)
|
14
16
|
process.fail!(e)
|
15
17
|
raise e
|
@@ -9,11 +9,12 @@ module Taskinator
|
|
9
9
|
|
10
10
|
class DelayedJobAdapter
|
11
11
|
def initialize(config={})
|
12
|
-
@config =
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
@config = Taskinator::Queues::DefaultConfig.merge(config)
|
13
|
+
end
|
14
|
+
|
15
|
+
def enqueue_create_process(definition, uuid, args)
|
16
|
+
queue = definition.queue || @config[:definition_queue]
|
17
|
+
::Delayed::Job.enqueue CreateProcessWorker.new(definition.name, uuid, Taskinator::Persistence.serialize(args)), :queue => queue
|
17
18
|
end
|
18
19
|
|
19
20
|
def enqueue_process(process)
|
@@ -32,6 +33,12 @@ module Taskinator
|
|
32
33
|
::Delayed::Job.enqueue JobWorker.new(job.uuid), :queue => queue
|
33
34
|
end
|
34
35
|
|
36
|
+
CreateProcessWorker = Struct.new(:definition_name, :uuid, :args) do
|
37
|
+
def perform
|
38
|
+
Taskinator::CreateProcessWorker.new(definition_name, uuid, args).perform
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
35
42
|
ProcessWorker = Struct.new(:process_uuid) do
|
36
43
|
def perform
|
37
44
|
Taskinator::ProcessWorker.new(process_uuid).perform
|
@@ -9,11 +9,11 @@ module Taskinator
|
|
9
9
|
|
10
10
|
class ResqueAdapter
|
11
11
|
def initialize(config={})
|
12
|
-
config =
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
12
|
+
config = Taskinator::Queues::DefaultConfig.merge(config)
|
13
|
+
|
14
|
+
CreateProcessWorker.class_eval do
|
15
|
+
@queue = config[:definition_queue]
|
16
|
+
end
|
17
17
|
|
18
18
|
ProcessWorker.class_eval do
|
19
19
|
@queue = config[:process_queue]
|
@@ -28,6 +28,11 @@ module Taskinator
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
+
def enqueue_create_process(definition, uuid, args)
|
32
|
+
queue = definition.queue || Resque.queue_from_class(CreateProcessWorker)
|
33
|
+
Resque.enqueue_to(queue, CreateProcessWorker, definition.name, uuid, Taskinator::Persistence.serialize(args))
|
34
|
+
end
|
35
|
+
|
31
36
|
def enqueue_process(process)
|
32
37
|
queue = process.queue || Resque.queue_from_class(ProcessWorker)
|
33
38
|
Resque.enqueue_to(queue, ProcessWorker, process.uuid)
|
@@ -46,6 +51,12 @@ module Taskinator
|
|
46
51
|
Resque.enqueue_to(queue, JobWorker, job.uuid)
|
47
52
|
end
|
48
53
|
|
54
|
+
class CreateProcessWorker
|
55
|
+
def self.perform(definition_name, uuid, args)
|
56
|
+
Taskinator::CreateProcessWorker.new(definition_name, uuid, args).perform
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
49
60
|
class ProcessWorker
|
50
61
|
def self.perform(process_uuid)
|
51
62
|
Taskinator::ProcessWorker.new(process_uuid).perform
|