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 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