taskinator 0.3.3 → 0.3.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|