taskinator 0.3.3 → 0.3.5
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/CHANGELOG.md +11 -0
- data/Gemfile.lock +6 -7
- data/lib/taskinator/api.rb +9 -2
- data/lib/taskinator/definition.rb +3 -1
- data/lib/taskinator/persistence.rb +32 -7
- data/lib/taskinator/process.rb +16 -7
- data/lib/taskinator/process_worker.rb +13 -0
- data/lib/taskinator/queues/delayed_job.rb +11 -0
- data/lib/taskinator/queues/resque.rb +15 -0
- data/lib/taskinator/queues/sidekiq.rb +13 -0
- data/lib/taskinator/queues.rb +6 -2
- data/lib/taskinator/task.rb +1 -5
- data/lib/taskinator/version.rb +1 -1
- data/lib/taskinator/xml_visitor.rb +109 -0
- data/lib/taskinator.rb +6 -0
- data/spec/taskinator/api_spec.rb +2 -2
- data/spec/taskinator/create_process_worker_spec.rb +1 -1
- data/spec/taskinator/definition_spec.rb +20 -0
- data/spec/taskinator/instrumentation_spec.rb +5 -1
- data/spec/taskinator/persistence_spec.rb +3 -11
- data/spec/taskinator/process_spec.rb +5 -2
- data/spec/taskinator/queues/delayed_job_spec.rb +19 -1
- data/spec/taskinator/queues/resque_spec.rb +22 -1
- data/spec/taskinator/queues/sidekiq_spec.rb +20 -1
- data/spec/taskinator/task_worker_spec.rb +1 -1
- data/taskinator.gemspec +1 -0
- metadata +18 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1cea1440734a7bcbe3faa2198406e3cd7ebfde21
|
|
4
|
+
data.tar.gz: ffbb5077202225399db2dc748010654b5fbf8ef6
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 3c57fe34871813ecadb16414ca6bb6333e8dcc5d9de4128ed9ba38c6e1c74aed0ff69ebc8a061955301f42770816d4417db833973a7aaf47ff94413668bd0564
|
|
7
|
+
data.tar.gz: 99b036277ab71ad28511ad194c4b183efee3472769f22e7abe66a5270c312272fd2b68a62e89046ea566573fd2b86f937088ac55c989e8d6491ec893f639d501
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
v0.3.5 - 02 Nov 2015
|
|
2
|
+
---
|
|
3
|
+
Updated the keys used when persisting processes and tasks in Redis, so they fall in the same key space.
|
|
4
|
+
Added clean up code to remove data from Redis when a process completes.
|
|
5
|
+
Introduced `Taskinator.generate_uuid` method
|
|
6
|
+
Use Redis pipelined mode to persist processes and tasks.
|
|
7
|
+
Added warning output to log if serialized arguments are bigger than 2MB.
|
|
8
|
+
Introduced scoping for keys in Redis in order to better support multi-tenancy requirements.
|
|
9
|
+
Added XmlVisitor for extracting processes/tasks into XML.
|
|
10
|
+
Introduced `ProcessWorker` (incomplete) which will be used to incrementally build sub-process in order to speed up overall processing for big processes.
|
|
11
|
+
|
|
1
12
|
v0.3.3 - 29 Oct 2015
|
|
2
13
|
---
|
|
3
14
|
Bug fix for options handling when defining processes using `define_concurrent_process`.
|
data/Gemfile.lock
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
GIT
|
|
2
2
|
remote: git://github.com/mperham/sidekiq.git
|
|
3
|
-
revision:
|
|
3
|
+
revision: c556c85b689af3a4f6a1da76c9f8b5208b9753c3
|
|
4
4
|
specs:
|
|
5
|
-
sidekiq (4.0.0.
|
|
5
|
+
sidekiq (4.0.0.pre2)
|
|
6
6
|
concurrent-ruby (= 1.0.0.pre4)
|
|
7
7
|
connection_pool (~> 2.2, >= 2.2.0)
|
|
8
8
|
json (~> 1.0)
|
|
@@ -12,7 +12,8 @@ GIT
|
|
|
12
12
|
PATH
|
|
13
13
|
remote: .
|
|
14
14
|
specs:
|
|
15
|
-
taskinator (0.3.
|
|
15
|
+
taskinator (0.3.5)
|
|
16
|
+
builder (>= 3.2.2)
|
|
16
17
|
connection_pool (>= 2.2.0)
|
|
17
18
|
json (>= 1.8.2)
|
|
18
19
|
redis (>= 3.2.1)
|
|
@@ -28,6 +29,7 @@ GEM
|
|
|
28
29
|
minitest (~> 5.1)
|
|
29
30
|
thread_safe (~> 0.3, >= 0.3.4)
|
|
30
31
|
tzinfo (~> 1.1)
|
|
32
|
+
builder (3.2.2)
|
|
31
33
|
byebug (5.0.0)
|
|
32
34
|
columnize (= 0.9.0)
|
|
33
35
|
coderay (1.1.0)
|
|
@@ -55,7 +57,7 @@ GEM
|
|
|
55
57
|
minitest (5.8.2)
|
|
56
58
|
mono_logger (1.1.0)
|
|
57
59
|
multi_json (1.11.2)
|
|
58
|
-
netrc (0.
|
|
60
|
+
netrc (0.11.0)
|
|
59
61
|
pry (0.10.3)
|
|
60
62
|
coderay (~> 1.1.0)
|
|
61
63
|
method_source (~> 0.8.1)
|
|
@@ -144,6 +146,3 @@ DEPENDENCIES
|
|
|
144
146
|
rspec-sidekiq (>= 2.1.0)
|
|
145
147
|
sidekiq (>= 3.5.0)!
|
|
146
148
|
taskinator!
|
|
147
|
-
|
|
148
|
-
BUNDLED WITH
|
|
149
|
-
1.10.6
|
data/lib/taskinator/api.rb
CHANGED
|
@@ -3,12 +3,19 @@ module Taskinator
|
|
|
3
3
|
class Processes
|
|
4
4
|
include Enumerable
|
|
5
5
|
|
|
6
|
+
attr_reader :scope
|
|
7
|
+
|
|
8
|
+
def initialize(scope=:shared)
|
|
9
|
+
@scope = scope
|
|
10
|
+
@processes_list_key = Taskinator::Persistence.processes_list_key(scope)
|
|
11
|
+
end
|
|
12
|
+
|
|
6
13
|
def each(&block)
|
|
7
14
|
return to_enum(__method__) unless block_given?
|
|
8
15
|
|
|
9
16
|
instance_cache = {}
|
|
10
17
|
Taskinator.redis do |conn|
|
|
11
|
-
uuids = conn.smembers(
|
|
18
|
+
uuids = conn.smembers(@processes_list_key)
|
|
12
19
|
uuids.each do |uuid|
|
|
13
20
|
yield Process.fetch(uuid, instance_cache)
|
|
14
21
|
end
|
|
@@ -17,7 +24,7 @@ module Taskinator
|
|
|
17
24
|
|
|
18
25
|
def size
|
|
19
26
|
Taskinator.redis do |conn|
|
|
20
|
-
conn.scard(
|
|
27
|
+
conn.scard(@processes_list_key)
|
|
21
28
|
end
|
|
22
29
|
end
|
|
23
30
|
end
|
|
@@ -46,6 +46,8 @@ module Taskinator
|
|
|
46
46
|
raise ArgumentError, "wrong number of arguments (#{args.length} for #{arg_list.length})" if args.length < arg_list.length
|
|
47
47
|
|
|
48
48
|
options = (args.last.is_a?(Hash) ? args.last : {})
|
|
49
|
+
options[:scope] ||= :shared
|
|
50
|
+
|
|
49
51
|
process = factory.call(self, options)
|
|
50
52
|
|
|
51
53
|
# this may take long... up to users definition
|
|
@@ -98,7 +100,7 @@ module Taskinator
|
|
|
98
100
|
#
|
|
99
101
|
def create_process_remotely(*args)
|
|
100
102
|
assert_valid_process_module
|
|
101
|
-
uuid =
|
|
103
|
+
uuid = Taskinator.generate_uuid
|
|
102
104
|
|
|
103
105
|
Taskinator.queue.enqueue_create_process(self, uuid, args)
|
|
104
106
|
|
|
@@ -2,15 +2,15 @@ module Taskinator
|
|
|
2
2
|
module Persistence
|
|
3
3
|
|
|
4
4
|
class << self
|
|
5
|
+
def processes_list_key(scope=:shared)
|
|
6
|
+
"taskinator:#{scope}:processes"
|
|
7
|
+
end
|
|
8
|
+
|
|
5
9
|
def add_process_to_list(process)
|
|
6
10
|
Taskinator.redis do |conn|
|
|
7
|
-
conn.sadd
|
|
11
|
+
conn.sadd processes_list_key(process.scope), process.uuid
|
|
8
12
|
end
|
|
9
13
|
end
|
|
10
|
-
|
|
11
|
-
def list_key
|
|
12
|
-
'processes'
|
|
13
|
-
end
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
# mixin logic
|
|
@@ -27,7 +27,7 @@ module Taskinator
|
|
|
27
27
|
# to provide the base key to use for storing
|
|
28
28
|
# it's instances, and it must be unique!
|
|
29
29
|
def base_key
|
|
30
|
-
|
|
30
|
+
@base_key ||= 'shared'
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
# returns the storage key for the given identifier
|
|
@@ -53,7 +53,7 @@ module Taskinator
|
|
|
53
53
|
|
|
54
54
|
def save
|
|
55
55
|
Taskinator.redis do |conn|
|
|
56
|
-
conn.
|
|
56
|
+
conn.pipelined do
|
|
57
57
|
visitor = RedisSerializationVisitor.new(conn, self).visit
|
|
58
58
|
conn.hmset(
|
|
59
59
|
Taskinator::Process.key_for(uuid),
|
|
@@ -194,6 +194,25 @@ module Taskinator
|
|
|
194
194
|
end
|
|
195
195
|
end
|
|
196
196
|
|
|
197
|
+
def cleanup
|
|
198
|
+
Taskinator.redis do |conn|
|
|
199
|
+
|
|
200
|
+
process_key = self.process_key
|
|
201
|
+
|
|
202
|
+
# delete processes/tasks data
|
|
203
|
+
conn.scan_each(:match => "#{process_key}:*", :count => 1000) do |key|
|
|
204
|
+
conn.del(key)
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# remove the process
|
|
208
|
+
conn.del process_key
|
|
209
|
+
|
|
210
|
+
# remove from the list
|
|
211
|
+
conn.srem Persistence.processes_list_key(scope), uuid
|
|
212
|
+
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
197
216
|
end
|
|
198
217
|
|
|
199
218
|
class RedisSerializationVisitor < Visitor::Base
|
|
@@ -283,6 +302,12 @@ module Taskinator
|
|
|
283
302
|
def visit_args(attribute)
|
|
284
303
|
values = @instance.send(attribute)
|
|
285
304
|
yaml = Taskinator::Persistence.serialize(values)
|
|
305
|
+
|
|
306
|
+
# greater than 2 MB?
|
|
307
|
+
if (yaml.bytesize / (1024.0**2)) > 2
|
|
308
|
+
Taskinator.logger.warn("Large argument data detected for '#{self.to_s}'. Consider using intrinsic types instead, or try to reduce the amount of data provided.")
|
|
309
|
+
end
|
|
310
|
+
|
|
286
311
|
@hmset += [attribute, yaml]
|
|
287
312
|
end
|
|
288
313
|
|
data/lib/taskinator/process.rb
CHANGED
|
@@ -17,34 +17,38 @@ module Taskinator
|
|
|
17
17
|
def define_concurrent_process_for(definition, complete_on=CompleteOn::Default, options={})
|
|
18
18
|
Process::Concurrent.new(definition, complete_on, options)
|
|
19
19
|
end
|
|
20
|
-
|
|
21
|
-
def base_key
|
|
22
|
-
'process'
|
|
23
|
-
end
|
|
24
20
|
end
|
|
25
21
|
|
|
26
22
|
attr_reader :uuid
|
|
27
23
|
attr_reader :definition
|
|
28
24
|
attr_reader :options
|
|
25
|
+
attr_reader :scope
|
|
29
26
|
attr_reader :queue
|
|
30
27
|
attr_reader :created_at
|
|
31
28
|
attr_reader :updated_at
|
|
32
29
|
|
|
33
30
|
# in the case of sub process tasks, the containing task
|
|
34
|
-
|
|
31
|
+
attr_reader :parent
|
|
35
32
|
|
|
36
33
|
def initialize(definition, options={})
|
|
37
34
|
raise ArgumentError, 'definition' if definition.nil?
|
|
38
35
|
raise ArgumentError, "#{definition.name} does not extend the #{Definition.name} module" unless definition.kind_of?(Definition)
|
|
39
36
|
|
|
40
|
-
@uuid = options.delete(:uuid) ||
|
|
37
|
+
@uuid = options.delete(:uuid) || Taskinator.generate_uuid
|
|
41
38
|
@definition = definition
|
|
42
39
|
@options = options
|
|
40
|
+
@scope = options.delete(:scope)
|
|
43
41
|
@queue = options.delete(:queue)
|
|
44
42
|
@created_at = Time.now.utc
|
|
45
43
|
@updated_at = created_at
|
|
46
44
|
end
|
|
47
45
|
|
|
46
|
+
def parent=(value)
|
|
47
|
+
@parent = value
|
|
48
|
+
# update the uuid to be "scoped" within the parent task
|
|
49
|
+
@uuid = "#{@parent.uuid}:subprocess"
|
|
50
|
+
end
|
|
51
|
+
|
|
48
52
|
def tasks
|
|
49
53
|
@tasks ||= Tasks.new
|
|
50
54
|
end
|
|
@@ -59,6 +63,7 @@ module Taskinator
|
|
|
59
63
|
visitor.visit_type(:definition)
|
|
60
64
|
visitor.visit_tasks(tasks)
|
|
61
65
|
visitor.visit_args(:options)
|
|
66
|
+
visitor.visit_attribute(:scope)
|
|
62
67
|
visitor.visit_attribute(:queue)
|
|
63
68
|
visitor.visit_attribute_time(:created_at)
|
|
64
69
|
visitor.visit_attribute_time(:updated_at)
|
|
@@ -118,7 +123,11 @@ module Taskinator
|
|
|
118
123
|
complete if respond_to?(:complete)
|
|
119
124
|
# notify the parent task (if there is one) that this process has completed
|
|
120
125
|
# note: parent may be a proxy, so explicity check for nil?
|
|
121
|
-
|
|
126
|
+
unless parent.nil?
|
|
127
|
+
parent.complete!
|
|
128
|
+
else
|
|
129
|
+
cleanup
|
|
130
|
+
end
|
|
122
131
|
end
|
|
123
132
|
end
|
|
124
133
|
end
|
|
@@ -17,6 +17,11 @@ module Taskinator
|
|
|
17
17
|
::Delayed::Job.enqueue CreateProcessWorker.new(definition.name, uuid, Taskinator::Persistence.serialize(args)), :queue => queue
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
def enqueue_process(process)
|
|
21
|
+
queue = process.queue || @config[:process_queue]
|
|
22
|
+
::Delayed::Job.enqueue ProcessWorker.new(process.uuid), :queue => queue
|
|
23
|
+
end
|
|
24
|
+
|
|
20
25
|
def enqueue_task(task)
|
|
21
26
|
queue = task.queue || @config[:task_queue]
|
|
22
27
|
::Delayed::Job.enqueue TaskWorker.new(task.uuid), :queue => queue
|
|
@@ -28,6 +33,12 @@ module Taskinator
|
|
|
28
33
|
end
|
|
29
34
|
end
|
|
30
35
|
|
|
36
|
+
ProcessWorker = Struct.new(:process_uuid) do
|
|
37
|
+
def perform
|
|
38
|
+
Taskinator::ProcessWorker.new(process_uuid).perform
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
31
42
|
TaskWorker = Struct.new(:task_uuid) do
|
|
32
43
|
def perform
|
|
33
44
|
Taskinator::TaskWorker.new(task_uuid).perform
|
|
@@ -15,6 +15,10 @@ module Taskinator
|
|
|
15
15
|
@queue = config[:definition_queue]
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
ProcessWorker.class_eval do
|
|
19
|
+
@queue = config[:process_queue]
|
|
20
|
+
end
|
|
21
|
+
|
|
18
22
|
TaskWorker.class_eval do
|
|
19
23
|
@queue = config[:task_queue]
|
|
20
24
|
end
|
|
@@ -26,6 +30,11 @@ module Taskinator
|
|
|
26
30
|
Resque.enqueue_to(queue, CreateProcessWorker, definition.name, uuid, Taskinator::Persistence.serialize(args))
|
|
27
31
|
end
|
|
28
32
|
|
|
33
|
+
def enqueue_process(process)
|
|
34
|
+
queue = process.queue || Resque.queue_from_class(ProcessWorker)
|
|
35
|
+
Resque.enqueue_to(queue, ProcessWorker, process.uuid)
|
|
36
|
+
end
|
|
37
|
+
|
|
29
38
|
def enqueue_task(task)
|
|
30
39
|
queue = task.queue || Resque.queue_from_class(TaskWorker)
|
|
31
40
|
Resque.enqueue_to(queue, TaskWorker, task.uuid)
|
|
@@ -37,6 +46,12 @@ module Taskinator
|
|
|
37
46
|
end
|
|
38
47
|
end
|
|
39
48
|
|
|
49
|
+
class ProcessWorker
|
|
50
|
+
def self.perform(process_uuid)
|
|
51
|
+
Taskinator::ProcessWorker.new(process_uuid).perform
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
40
55
|
class TaskWorker
|
|
41
56
|
def self.perform(task_uuid)
|
|
42
57
|
Taskinator::TaskWorker.new(task_uuid).perform
|
|
@@ -17,6 +17,11 @@ module Taskinator
|
|
|
17
17
|
CreateProcessWorker.client_push('class' => CreateProcessWorker, 'args' => [definition.name, uuid, Taskinator::Persistence.serialize(args)], 'queue' => queue)
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
+
def enqueue_process(process)
|
|
21
|
+
queue = process.queue || @config[:process_queue]
|
|
22
|
+
TaskWorker.client_push('class' => ProcessWorker, 'args' => [process.uuid], 'queue' => queue)
|
|
23
|
+
end
|
|
24
|
+
|
|
20
25
|
def enqueue_task(task)
|
|
21
26
|
queue = task.queue || @config[:task_queue]
|
|
22
27
|
TaskWorker.client_push('class' => TaskWorker, 'args' => [task.uuid], 'queue' => queue)
|
|
@@ -30,6 +35,14 @@ module Taskinator
|
|
|
30
35
|
end
|
|
31
36
|
end
|
|
32
37
|
|
|
38
|
+
class ProcessWorker
|
|
39
|
+
include ::Sidekiq::Worker
|
|
40
|
+
|
|
41
|
+
def perform(process_uuid)
|
|
42
|
+
Taskinator::ProcessWorker.new(process_uuid).perform
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
33
46
|
class TaskWorker
|
|
34
47
|
include ::Sidekiq::Worker
|
|
35
48
|
|
data/lib/taskinator/queues.rb
CHANGED
|
@@ -4,8 +4,7 @@ module Taskinator
|
|
|
4
4
|
DefaultConfig = {
|
|
5
5
|
:definition_queue => :default,
|
|
6
6
|
:process_queue => :default,
|
|
7
|
-
:task_queue => :default
|
|
8
|
-
:job_queue => :default,
|
|
7
|
+
:task_queue => :default
|
|
9
8
|
}.freeze
|
|
10
9
|
|
|
11
10
|
def self.create_adapter(adapter, config={})
|
|
@@ -34,6 +33,11 @@ module Taskinator
|
|
|
34
33
|
adapter.enqueue_create_process(definition, uuid, args)
|
|
35
34
|
end
|
|
36
35
|
|
|
36
|
+
def enqueue_process(process)
|
|
37
|
+
Taskinator.logger.info("Enqueuing process #{process}")
|
|
38
|
+
adapter.enqueue_process(process)
|
|
39
|
+
end
|
|
40
|
+
|
|
37
41
|
def enqueue_task(task)
|
|
38
42
|
Taskinator.logger.info("Enqueuing task #{task}")
|
|
39
43
|
adapter.enqueue_task(task)
|
data/lib/taskinator/task.rb
CHANGED
|
@@ -18,10 +18,6 @@ module Taskinator
|
|
|
18
18
|
def define_sub_process_task(process, sub_process, options={})
|
|
19
19
|
SubProcess.new(process, sub_process, options)
|
|
20
20
|
end
|
|
21
|
-
|
|
22
|
-
def base_key
|
|
23
|
-
'task'
|
|
24
|
-
end
|
|
25
21
|
end
|
|
26
22
|
|
|
27
23
|
attr_reader :process
|
|
@@ -37,7 +33,7 @@ module Taskinator
|
|
|
37
33
|
def initialize(process, options={})
|
|
38
34
|
raise ArgumentError, 'process' if process.nil? || !process.is_a?(Process)
|
|
39
35
|
|
|
40
|
-
@uuid =
|
|
36
|
+
@uuid = "#{process.uuid}:task:#{Taskinator.generate_uuid}"
|
|
41
37
|
@process = process
|
|
42
38
|
@options = options
|
|
43
39
|
@queue = options.delete(:queue)
|
data/lib/taskinator/version.rb
CHANGED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
require 'builder'
|
|
2
|
+
|
|
3
|
+
module Taskinator
|
|
4
|
+
module Visitor
|
|
5
|
+
class XmlVisitor
|
|
6
|
+
class << self
|
|
7
|
+
def to_xml(process)
|
|
8
|
+
builder = ::Builder::XmlMarkup.new(:indent => 2)
|
|
9
|
+
builder.instruct!
|
|
10
|
+
builder.tag!('process') do
|
|
11
|
+
XmlVisitor.new(builder, process).visit
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
attr_reader :builder
|
|
17
|
+
attr_reader :instance
|
|
18
|
+
|
|
19
|
+
def initialize(builder, instance)
|
|
20
|
+
@builder = builder
|
|
21
|
+
@instance = instance
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def visit
|
|
25
|
+
@instance.accept(self)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def write_error(error)
|
|
29
|
+
return unless error[0]
|
|
30
|
+
builder.tag!('error', :type => error[0]) do
|
|
31
|
+
builder.message(error[1])
|
|
32
|
+
builder.cdata!(error[2].join("\n"))
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def visit_process(attribute)
|
|
37
|
+
process = @instance.send(attribute)
|
|
38
|
+
if process
|
|
39
|
+
p = process.__getobj__
|
|
40
|
+
|
|
41
|
+
attribs = {
|
|
42
|
+
:type => p.class.name,
|
|
43
|
+
:current_state => p.current_state
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
builder.tag!('process', attribs) do
|
|
47
|
+
XmlVisitor.new(builder, p).visit
|
|
48
|
+
write_error(p.error)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def visit_tasks(tasks)
|
|
54
|
+
tasks.each do |task|
|
|
55
|
+
t = task.__getobj__
|
|
56
|
+
|
|
57
|
+
attribs = {
|
|
58
|
+
:type => t.class.name,
|
|
59
|
+
:current_state => t.current_state
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
builder.tag!('task', attribs) do
|
|
63
|
+
XmlVisitor.new(builder, t).visit
|
|
64
|
+
write_error(t.error)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def visit_attribute(attribute)
|
|
70
|
+
value = @instance.send(attribute)
|
|
71
|
+
builder.tag!('attribute', :name => attribute, :value => value) if value
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def visit_attribute_time(attribute)
|
|
75
|
+
visit_attribute(attribute)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def visit_attribute_enum(attribute, type)
|
|
79
|
+
visit_attribute(attribute)
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def visit_process_reference(attribute)
|
|
83
|
+
process = @instance.send(attribute)
|
|
84
|
+
builder.tag!('attribute_ref', { :name => attribute, :type => process.__getobj__.class.name, :value => process.uuid }) if process
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def visit_task_reference(attribute)
|
|
88
|
+
task = @instance.send(attribute)
|
|
89
|
+
builder.tag!('attribute_ref', { :name => attribute, :type => task.__getobj__.class.name, :value => task.uuid }) if task
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def visit_type(attribute)
|
|
93
|
+
type = @instance.send(attribute)
|
|
94
|
+
builder.tag!('attribute', { :name => attribute, :value => type.name })
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def visit_args(attribute)
|
|
98
|
+
values = @instance.send(attribute)
|
|
99
|
+
|
|
100
|
+
builder.tag!('attribute', :name => attribute) do
|
|
101
|
+
builder.cdata!(values.to_json)
|
|
102
|
+
end if values && !values.empty?
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def task_count
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
data/lib/taskinator.rb
CHANGED
|
@@ -16,12 +16,14 @@ require 'taskinator/workflow'
|
|
|
16
16
|
require 'taskinator/visitor'
|
|
17
17
|
require 'taskinator/persistence'
|
|
18
18
|
require 'taskinator/instrumentation'
|
|
19
|
+
require 'taskinator/xml_visitor'
|
|
19
20
|
|
|
20
21
|
require 'taskinator/task'
|
|
21
22
|
require 'taskinator/tasks'
|
|
22
23
|
require 'taskinator/process'
|
|
23
24
|
|
|
24
25
|
require 'taskinator/task_worker'
|
|
26
|
+
require 'taskinator/process_worker'
|
|
25
27
|
require 'taskinator/create_process_worker'
|
|
26
28
|
|
|
27
29
|
require 'taskinator/executor'
|
|
@@ -46,6 +48,10 @@ module Taskinator
|
|
|
46
48
|
@options = opts
|
|
47
49
|
end
|
|
48
50
|
|
|
51
|
+
def generate_uuid
|
|
52
|
+
SecureRandom.uuid
|
|
53
|
+
end
|
|
54
|
+
|
|
49
55
|
##
|
|
50
56
|
# Configuration for Taskinator client, use like:
|
|
51
57
|
#
|
data/spec/taskinator/api_spec.rb
CHANGED
|
@@ -18,7 +18,7 @@ describe Taskinator::Api, :redis => true do
|
|
|
18
18
|
|
|
19
19
|
Taskinator.redis do |conn|
|
|
20
20
|
conn.multi do
|
|
21
|
-
3.times {|i| conn.sadd(
|
|
21
|
+
3.times {|i| conn.sadd(Taskinator::Persistence.processes_list_key, i) }
|
|
22
22
|
end
|
|
23
23
|
end
|
|
24
24
|
|
|
@@ -35,7 +35,7 @@ describe Taskinator::Api, :redis => true do
|
|
|
35
35
|
it "yields the number of processes" do
|
|
36
36
|
Taskinator.redis do |conn|
|
|
37
37
|
conn.multi do
|
|
38
|
-
3.times {|i| conn.sadd(
|
|
38
|
+
3.times {|i| conn.sadd(Taskinator::Persistence.processes_list_key, i) }
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
|
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
|
3
3
|
describe Taskinator::CreateProcessWorker do
|
|
4
4
|
|
|
5
5
|
let(:definition) { MockDefinition.create }
|
|
6
|
-
let(:uuid) {
|
|
6
|
+
let(:uuid) { Taskinator.generate_uuid }
|
|
7
7
|
let(:args) { [{:foo => :bar}] }
|
|
8
8
|
|
|
9
9
|
subject { Taskinator::CreateProcessWorker.new(definition.name, uuid, Taskinator::Persistence.serialize(args)) }
|
|
@@ -106,6 +106,26 @@ describe Taskinator::Definition do
|
|
|
106
106
|
expect(subject.create_process).to be_a(Taskinator::Process)
|
|
107
107
|
end
|
|
108
108
|
|
|
109
|
+
it "defaults the scope to :shared" do
|
|
110
|
+
block = SpecSupport::Block.new
|
|
111
|
+
allow(block).to receive(:to_proc) {
|
|
112
|
+
Proc.new {|*args| }
|
|
113
|
+
}
|
|
114
|
+
subject.define_process(&block)
|
|
115
|
+
|
|
116
|
+
expect(subject.create_process.scope).to eq(:shared)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
it "sets the scope" do
|
|
120
|
+
block = SpecSupport::Block.new
|
|
121
|
+
allow(block).to receive(:to_proc) {
|
|
122
|
+
Proc.new {|*args| }
|
|
123
|
+
}
|
|
124
|
+
subject.define_process(&block)
|
|
125
|
+
|
|
126
|
+
expect(subject.create_process(:scope => :foo).scope).to eq(:foo)
|
|
127
|
+
end
|
|
128
|
+
|
|
109
129
|
it "receives options" do
|
|
110
130
|
block = SpecSupport::Block.new
|
|
111
131
|
allow(block).to receive(:to_proc) {
|
|
@@ -17,7 +17,7 @@ describe Taskinator::Instrumentation, :redis => true do
|
|
|
17
17
|
attr_reader :options
|
|
18
18
|
|
|
19
19
|
def initialize
|
|
20
|
-
@uuid =
|
|
20
|
+
@uuid = Taskinator.generate_uuid
|
|
21
21
|
@options = { :bar => :baz }
|
|
22
22
|
end
|
|
23
23
|
end
|
|
@@ -50,9 +50,11 @@ describe Taskinator::Instrumentation, :redis => true do
|
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
describe "#enqueued_payload" do
|
|
53
|
+
pending
|
|
53
54
|
end
|
|
54
55
|
|
|
55
56
|
describe "#processing_payload" do
|
|
57
|
+
pending
|
|
56
58
|
end
|
|
57
59
|
|
|
58
60
|
describe "#completed_payload" do
|
|
@@ -90,9 +92,11 @@ describe Taskinator::Instrumentation, :redis => true do
|
|
|
90
92
|
end
|
|
91
93
|
|
|
92
94
|
describe "#cancelled_payload" do
|
|
95
|
+
pending
|
|
93
96
|
end
|
|
94
97
|
|
|
95
98
|
describe "#failed_payload" do
|
|
99
|
+
pending
|
|
96
100
|
end
|
|
97
101
|
|
|
98
102
|
end
|
|
@@ -11,14 +11,6 @@ describe Taskinator::Persistence, :redis => true do
|
|
|
11
11
|
end
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
-
describe ".base_key" do
|
|
15
|
-
it {
|
|
16
|
-
expect {
|
|
17
|
-
subject.base_key
|
|
18
|
-
}.to raise_error(NotImplementedError)
|
|
19
|
-
}
|
|
20
|
-
end
|
|
21
|
-
|
|
22
14
|
describe ".key_for" do
|
|
23
15
|
before do
|
|
24
16
|
allow(subject).to receive(:base_key) { 'base_key' }
|
|
@@ -208,7 +200,7 @@ describe Taskinator::Persistence, :redis => true do
|
|
|
208
200
|
attr_reader :uuid
|
|
209
201
|
|
|
210
202
|
def initialize
|
|
211
|
-
@uuid =
|
|
203
|
+
@uuid = Taskinator.generate_uuid
|
|
212
204
|
end
|
|
213
205
|
end
|
|
214
206
|
klass.new
|
|
@@ -220,7 +212,7 @@ describe Taskinator::Persistence, :redis => true do
|
|
|
220
212
|
|
|
221
213
|
describe "#key" do
|
|
222
214
|
it {
|
|
223
|
-
expect(subject.key).to match(
|
|
215
|
+
expect(subject.key).to match(/#{subject.uuid}/)
|
|
224
216
|
}
|
|
225
217
|
end
|
|
226
218
|
|
|
@@ -240,7 +232,7 @@ describe Taskinator::Persistence, :redis => true do
|
|
|
240
232
|
conn.hset(subject.key, :process_uuid, subject.uuid)
|
|
241
233
|
end
|
|
242
234
|
|
|
243
|
-
expect(subject.process_key).to match(
|
|
235
|
+
expect(subject.process_key).to match(/#{subject.uuid}/)
|
|
244
236
|
}
|
|
245
237
|
end
|
|
246
238
|
|
|
@@ -162,7 +162,7 @@ describe Taskinator::Process do
|
|
|
162
162
|
describe "#parent" do
|
|
163
163
|
it "notifies parent when completed" do
|
|
164
164
|
allow(subject).to receive(:tasks_completed?) { true }
|
|
165
|
-
subject.parent = double('parent')
|
|
165
|
+
subject.parent = double('parent', :uuid => 'foobar')
|
|
166
166
|
expect(subject.parent).to receive(:complete!)
|
|
167
167
|
subject.start!
|
|
168
168
|
subject.complete!
|
|
@@ -170,7 +170,7 @@ describe Taskinator::Process do
|
|
|
170
170
|
|
|
171
171
|
it "notifies parent when failed" do
|
|
172
172
|
allow(subject).to receive(:tasks_completed?) { true }
|
|
173
|
-
subject.parent = double('parent')
|
|
173
|
+
subject.parent = double('parent', :uuid => 'foobar')
|
|
174
174
|
expect(subject.parent).to receive(:fail!)
|
|
175
175
|
subject.start!
|
|
176
176
|
subject.fail!(StandardError.new)
|
|
@@ -192,6 +192,7 @@ describe Taskinator::Process do
|
|
|
192
192
|
expect(visitor).to receive(:visit_args).with(:options)
|
|
193
193
|
expect(visitor).to receive(:visit_task_reference).with(:parent)
|
|
194
194
|
expect(visitor).to receive(:visit_tasks)
|
|
195
|
+
expect(visitor).to receive(:visit_attribute).with(:scope)
|
|
195
196
|
expect(visitor).to receive(:visit_attribute).with(:queue)
|
|
196
197
|
expect(visitor).to receive(:visit_attribute_time).with(:created_at)
|
|
197
198
|
expect(visitor).to receive(:visit_attribute_time).with(:updated_at)
|
|
@@ -329,6 +330,7 @@ describe Taskinator::Process do
|
|
|
329
330
|
expect(visitor).to receive(:visit_args).with(:options)
|
|
330
331
|
expect(visitor).to receive(:visit_task_reference).with(:parent)
|
|
331
332
|
expect(visitor).to receive(:visit_tasks)
|
|
333
|
+
expect(visitor).to receive(:visit_attribute).with(:scope)
|
|
332
334
|
expect(visitor).to receive(:visit_attribute).with(:queue)
|
|
333
335
|
expect(visitor).to receive(:visit_attribute_time).with(:created_at)
|
|
334
336
|
expect(visitor).to receive(:visit_attribute_time).with(:updated_at)
|
|
@@ -522,6 +524,7 @@ describe Taskinator::Process do
|
|
|
522
524
|
expect(visitor).to receive(:visit_args).with(:options)
|
|
523
525
|
expect(visitor).to receive(:visit_task_reference).with(:parent)
|
|
524
526
|
expect(visitor).to receive(:visit_tasks)
|
|
527
|
+
expect(visitor).to receive(:visit_attribute).with(:scope)
|
|
525
528
|
expect(visitor).to receive(:visit_attribute).with(:queue)
|
|
526
529
|
expect(visitor).to receive(:visit_attribute_time).with(:created_at)
|
|
527
530
|
expect(visitor).to receive(:visit_attribute_time).with(:updated_at)
|
|
@@ -5,7 +5,7 @@ describe Taskinator::Queues::DelayedJobAdapter, :delayed_job do
|
|
|
5
5
|
it_should_behave_like "a queue adapter", :delayed_job, Taskinator::Queues::DelayedJobAdapter
|
|
6
6
|
|
|
7
7
|
let(:adapter) { Taskinator::Queues::DelayedJobAdapter }
|
|
8
|
-
let(:uuid) {
|
|
8
|
+
let(:uuid) { Taskinator.generate_uuid }
|
|
9
9
|
|
|
10
10
|
subject { adapter.new }
|
|
11
11
|
|
|
@@ -30,6 +30,24 @@ describe Taskinator::Queues::DelayedJobAdapter, :delayed_job do
|
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
+
describe "ProcessWorker" do
|
|
34
|
+
it "enqueues processes" do
|
|
35
|
+
expect {
|
|
36
|
+
subject.enqueue_process(double('process', :uuid => uuid, :queue => nil))
|
|
37
|
+
}.to change(Delayed::Job.queue, :size).by(1)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it "enqueues process to specified queue" do
|
|
41
|
+
subject.enqueue_process(double('process', :uuid => uuid, :queue => :other))
|
|
42
|
+
expect(Delayed::Job.contains?(adapter::ProcessWorker, uuid, :other)).to be
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "calls process worker" do
|
|
46
|
+
expect_any_instance_of(Taskinator::ProcessWorker).to receive(:perform)
|
|
47
|
+
adapter::ProcessWorker.new(uuid).perform
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
33
51
|
describe "TaskWorker" do
|
|
34
52
|
it "enqueues tasks" do
|
|
35
53
|
expect {
|
|
@@ -5,7 +5,7 @@ describe Taskinator::Queues::ResqueAdapter, :resque do
|
|
|
5
5
|
it_should_behave_like "a queue adapter", :resque, Taskinator::Queues::ResqueAdapter
|
|
6
6
|
|
|
7
7
|
let(:adapter) { Taskinator::Queues::ResqueAdapter }
|
|
8
|
-
let(:uuid) {
|
|
8
|
+
let(:uuid) { Taskinator.generate_uuid }
|
|
9
9
|
|
|
10
10
|
subject { adapter.new }
|
|
11
11
|
|
|
@@ -34,6 +34,27 @@ describe Taskinator::Queues::ResqueAdapter, :resque do
|
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
|
|
37
|
+
describe "ProcessWorker" do
|
|
38
|
+
it "enqueues processes" do
|
|
39
|
+
worker = adapter::ProcessWorker
|
|
40
|
+
subject.enqueue_process(double('process', :uuid => uuid, :queue => nil))
|
|
41
|
+
|
|
42
|
+
expect(worker).to have_queued(uuid)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
it "enqueues process to specified queue" do
|
|
46
|
+
worker = adapter::ProcessWorker
|
|
47
|
+
subject.enqueue_process(double('process', :uuid => uuid, :queue => :other))
|
|
48
|
+
|
|
49
|
+
expect(worker).to have_queued(uuid).in(:other)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
it "calls process worker" do
|
|
53
|
+
expect_any_instance_of(Taskinator::ProcessWorker).to receive(:perform)
|
|
54
|
+
adapter::ProcessWorker.perform(uuid)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
37
58
|
describe "TaskWorker" do
|
|
38
59
|
it "enqueues tasks" do
|
|
39
60
|
worker = adapter::TaskWorker
|
|
@@ -7,7 +7,7 @@ describe Taskinator::Queues::SidekiqAdapter, :sidekiq do
|
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
let(:adapter) { Taskinator::Queues::SidekiqAdapter }
|
|
10
|
-
let(:uuid) {
|
|
10
|
+
let(:uuid) { Taskinator.generate_uuid }
|
|
11
11
|
|
|
12
12
|
subject { adapter.new }
|
|
13
13
|
|
|
@@ -33,6 +33,25 @@ describe Taskinator::Queues::SidekiqAdapter, :sidekiq do
|
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
+
describe "ProcessWorker" do
|
|
37
|
+
it "enqueues processes" do
|
|
38
|
+
worker = adapter::ProcessWorker
|
|
39
|
+
process = double('process', :uuid => uuid, :queue => nil)
|
|
40
|
+
subject.enqueue_process(process)
|
|
41
|
+
expect(worker).to have_enqueued_job(process.uuid)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it "enqueues process to specified queue" do
|
|
45
|
+
subject.enqueue_process(double('process', :uuid => uuid, :queue => :other))
|
|
46
|
+
expect(adapter::ProcessWorker).to be_processed_in_x(:other)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "calls process worker" do
|
|
50
|
+
expect_any_instance_of(Taskinator::ProcessWorker).to receive(:perform)
|
|
51
|
+
adapter::ProcessWorker.new.perform(uuid)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
36
55
|
describe "TaskWorker" do
|
|
37
56
|
it "enqueues tasks" do
|
|
38
57
|
worker = adapter::TaskWorker
|
|
@@ -6,7 +6,7 @@ describe Taskinator::TaskWorker do
|
|
|
6
6
|
double('task', :paused? => paused, :cancelled? => cancelled, :can_complete? => can_complete)
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
-
let(:uuid) {
|
|
9
|
+
let(:uuid) { Taskinator.generate_uuid }
|
|
10
10
|
|
|
11
11
|
subject { Taskinator::TaskWorker.new(uuid) }
|
|
12
12
|
|
data/taskinator.gemspec
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: taskinator
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.5
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chris Stefano
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2015-
|
|
11
|
+
date: 2015-11-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: redis
|
|
@@ -80,6 +80,20 @@ dependencies:
|
|
|
80
80
|
- - ">="
|
|
81
81
|
- !ruby/object:Gem::Version
|
|
82
82
|
version: 1.8.2
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: builder
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: 3.2.2
|
|
90
|
+
type: :runtime
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: 3.2.2
|
|
83
97
|
description: Simple process orchestration
|
|
84
98
|
email:
|
|
85
99
|
- virtualstaticvoid@gmail.com
|
|
@@ -114,6 +128,7 @@ files:
|
|
|
114
128
|
- lib/taskinator/logger.rb
|
|
115
129
|
- lib/taskinator/persistence.rb
|
|
116
130
|
- lib/taskinator/process.rb
|
|
131
|
+
- lib/taskinator/process_worker.rb
|
|
117
132
|
- lib/taskinator/queues.rb
|
|
118
133
|
- lib/taskinator/queues/delayed_job.rb
|
|
119
134
|
- lib/taskinator/queues/resque.rb
|
|
@@ -125,6 +140,7 @@ files:
|
|
|
125
140
|
- lib/taskinator/version.rb
|
|
126
141
|
- lib/taskinator/visitor.rb
|
|
127
142
|
- lib/taskinator/workflow.rb
|
|
143
|
+
- lib/taskinator/xml_visitor.rb
|
|
128
144
|
- processes_workflow.png
|
|
129
145
|
- sequence.txt
|
|
130
146
|
- spec/examples/process_examples.rb
|