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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0f24bbd204f659987e547f8d4b48f2b3357df9c6
4
- data.tar.gz: b6187087f34f5c61daee96ea03be840b4a56b669
3
+ metadata.gz: 1cea1440734a7bcbe3faa2198406e3cd7ebfde21
4
+ data.tar.gz: ffbb5077202225399db2dc748010654b5fbf8ef6
5
5
  SHA512:
6
- metadata.gz: d4ebe8d2c2df56501930be730694e0085790c453c6578f978c81262535e0f69936fac28b22e3cd53f5d9a984c0e09fac7559991b3c9e96e691177c85d5bbf60b
7
- data.tar.gz: 4bf4949db42aed309e81947b46db88fbdba4668497d05063bede4499d5dd413cc31394890834f1e69c20b5eaadc1edc5b50d7eda46a21aa6ad6247d87ddcd15f
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: a087e1db6b3bd4707e63b99858e03d06cf785a78
3
+ revision: c556c85b689af3a4f6a1da76c9f8b5208b9753c3
4
4
  specs:
5
- sidekiq (4.0.0.pre1)
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.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.10.3)
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
@@ -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("taskinator:processes")
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("taskinator:processes")
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 = SecureRandom.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 "taskinator:#{list_key}", process.uuid
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
- raise NotImplementedError
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.multi do
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
 
@@ -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
- attr_accessor :parent
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) || SecureRandom.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
- parent.complete! unless parent.nil?
126
+ unless parent.nil?
127
+ parent.complete!
128
+ else
129
+ cleanup
130
+ end
122
131
  end
123
132
  end
124
133
  end
@@ -0,0 +1,13 @@
1
+ module Taskinator
2
+ class ProcessWorker
3
+ attr_reader :uuid
4
+
5
+ def initialize(uuid)
6
+ @uuid = uuid
7
+ end
8
+
9
+ def perform
10
+ # TODO: build the process, and enqueue it. after it completes, report back to the containing process
11
+ end
12
+ end
13
+ 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
 
@@ -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)
@@ -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 = SecureRandom.uuid
36
+ @uuid = "#{process.uuid}:task:#{Taskinator.generate_uuid}"
41
37
  @process = process
42
38
  @options = options
43
39
  @queue = options.delete(:queue)
@@ -1,3 +1,3 @@
1
1
  module Taskinator
2
- VERSION = "0.3.3"
2
+ VERSION = "0.3.5"
3
3
  end
@@ -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
  #
@@ -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("taskinator:#{Taskinator::Persistence.list_key}", i) }
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("taskinator:#{Taskinator::Persistence.list_key}", i) }
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) { SecureRandom.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 = SecureRandom.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 = SecureRandom.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(/taskinator:base_key:#{subject.uuid}/)
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(/taskinator:process:#{subject.uuid}/)
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) { SecureRandom.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) { SecureRandom.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) { SecureRandom.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) { SecureRandom.uuid }
9
+ let(:uuid) { Taskinator.generate_uuid }
10
10
 
11
11
  subject { Taskinator::TaskWorker.new(uuid) }
12
12
 
data/taskinator.gemspec CHANGED
@@ -26,4 +26,5 @@ Gem::Specification.new do |spec|
26
26
  spec.add_dependency 'redis-semaphore' , '>= 0.2.4'
27
27
  spec.add_dependency 'connection_pool' , '>= 2.2.0'
28
28
  spec.add_dependency 'json' , '>= 1.8.2'
29
+ spec.add_dependency 'builder' , '>= 3.2.2'
29
30
  end
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.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-10-28 00:00:00.000000000 Z
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