taskinator 0.2.0 → 0.3.0

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.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +10 -0
  3. data/Gemfile +17 -2
  4. data/Gemfile.lock +57 -18
  5. data/README.md +20 -16
  6. data/lib/taskinator/definition.rb +2 -2
  7. data/lib/taskinator/instrumentation.rb +77 -0
  8. data/lib/taskinator/persistence.rb +72 -61
  9. data/lib/taskinator/process.rb +118 -99
  10. data/lib/taskinator/queues/delayed_job.rb +0 -14
  11. data/lib/taskinator/queues/resque.rb +0 -18
  12. data/lib/taskinator/queues/sidekiq.rb +0 -14
  13. data/lib/taskinator/queues.rb +0 -5
  14. data/lib/taskinator/task.rb +113 -70
  15. data/lib/taskinator/version.rb +1 -1
  16. data/lib/taskinator/visitor.rb +6 -0
  17. data/lib/taskinator/workflow.rb +36 -0
  18. data/lib/taskinator.rb +3 -2
  19. data/spec/examples/process_examples.rb +6 -9
  20. data/spec/examples/queue_adapter_examples.rb +2 -12
  21. data/spec/examples/task_examples.rb +5 -8
  22. data/spec/support/process_methods.rb +25 -0
  23. data/spec/support/task_methods.rb +13 -0
  24. data/spec/support/test_flows.rb +1 -3
  25. data/spec/support/test_instrumenter.rb +39 -0
  26. data/spec/support/test_queue.rb +0 -12
  27. data/spec/taskinator/definition_spec.rb +3 -5
  28. data/spec/taskinator/instrumentation_spec.rb +98 -0
  29. data/spec/taskinator/persistence_spec.rb +3 -41
  30. data/spec/taskinator/process_spec.rb +36 -34
  31. data/spec/taskinator/queues/delayed_job_spec.rb +0 -41
  32. data/spec/taskinator/queues/resque_spec.rb +0 -51
  33. data/spec/taskinator/queues/sidekiq_spec.rb +0 -50
  34. data/spec/taskinator/queues_spec.rb +1 -1
  35. data/spec/taskinator/task_spec.rb +96 -64
  36. data/spec/taskinator/test_flows_spec.rb +266 -1
  37. data/taskinator.gemspec +0 -21
  38. metadata +12 -173
  39. data/lib/taskinator/job_worker.rb +0 -17
  40. data/spec/taskinator/job_worker_spec.rb +0 -62
@@ -0,0 +1,98 @@
1
+ require 'spec_helper'
2
+
3
+ describe Taskinator::Instrumentation, :redis => true do
4
+
5
+ let(:definition) { TestDefinition }
6
+
7
+ subject do
8
+ klass = Class.new do
9
+ include Taskinator::Persistence
10
+ include Taskinator::Instrumentation
11
+
12
+ def self.base_key
13
+ 'base_key'
14
+ end
15
+
16
+ attr_reader :uuid
17
+ attr_reader :options
18
+
19
+ def initialize
20
+ @uuid = SecureRandom.uuid
21
+ @options = { :bar => :baz }
22
+ end
23
+ end
24
+
25
+ klass.new
26
+ end
27
+
28
+ describe "#instrument" do
29
+ it {
30
+ event = 'foo_bar'
31
+
32
+ expect(Taskinator.instrumenter).to receive(:instrument).with(event, {}).and_call_original
33
+
34
+ block = SpecSupport::Block.new
35
+ expect(block).to receive(:call)
36
+
37
+ subject.instrument(event, &block)
38
+ }
39
+
40
+ it {
41
+ event = 'foo_bar'
42
+
43
+ expect(Taskinator.instrumenter).to receive(:instrument).with(event, {:baz => :qux}).and_call_original
44
+
45
+ block = SpecSupport::Block.new
46
+ expect(block).to receive(:call)
47
+
48
+ subject.instrument(event, :baz => :qux, &block)
49
+ }
50
+ end
51
+
52
+ describe "#enqueued_payload" do
53
+ end
54
+
55
+ describe "#processing_payload" do
56
+ end
57
+
58
+ describe "#completed_payload" do
59
+ it {
60
+ Taskinator.redis do |conn|
61
+ conn.hset(subject.key, :process_uuid, subject.uuid)
62
+ conn.hmset(
63
+ subject.process_key,
64
+ [:options, YAML.dump({:foo => :bar})],
65
+ [:tasks_count, 100],
66
+ [:tasks_processing, 1],
67
+ [:tasks_completed, 2],
68
+ [:tasks_cancelled, 3],
69
+ [:tasks_failed, 4]
70
+ )
71
+ end
72
+
73
+ expect(subject.completed_payload(:baz => :qux)).to eq(
74
+ OpenStruct.new({
75
+ :type => subject.class,
76
+ :process_uuid => subject.uuid,
77
+ :process_options => {:foo => :bar},
78
+ :uuid => subject.uuid,
79
+ :state => :completed,
80
+ :options => subject.options,
81
+ :percentage_processing => 1.0,
82
+ :percentage_completed => 2.0,
83
+ :percentage_cancelled => 3.0,
84
+ :percentage_failed => 4.0,
85
+ :instance => subject,
86
+ :baz => :qux
87
+ })
88
+ )
89
+ }
90
+ end
91
+
92
+ describe "#cancelled_payload" do
93
+ end
94
+
95
+ describe "#failed_payload" do
96
+ end
97
+
98
+ end
@@ -30,16 +30,6 @@ describe Taskinator::Persistence, :redis => true do
30
30
  }
31
31
  end
32
32
 
33
- describe ".state_for" do
34
- before do
35
- allow(subject).to receive(:base_key) { 'base_key' }
36
- end
37
-
38
- it {
39
- expect(subject.state_for('uuid')).to eq(:initial)
40
- }
41
- end
42
-
43
33
  describe ".fetch" do
44
34
  before do
45
35
  allow(subject).to receive(:base_key) { 'base_key' }
@@ -320,7 +310,7 @@ describe Taskinator::Persistence, :redis => true do
320
310
  describe "#count_#{status}" do
321
311
  it {
322
312
  Taskinator.redis do |conn|
323
- conn.hset(subject.process_key, status, 99)
313
+ conn.hset(subject.process_key, "tasks_#{status}", 99)
324
314
  end
325
315
 
326
316
  expect(subject.send(:"count_#{status}")).to eq(99)
@@ -330,7 +320,7 @@ describe Taskinator::Persistence, :redis => true do
330
320
  describe "#incr_#{status}" do
331
321
  it {
332
322
  Taskinator.redis do |conn|
333
- conn.hset(subject.process_key, status, 99)
323
+ conn.hset(subject.process_key, "tasks_#{status}", 99)
334
324
  end
335
325
 
336
326
  subject.send(:"incr_#{status}")
@@ -345,7 +335,7 @@ describe Taskinator::Persistence, :redis => true do
345
335
  conn.hmset(
346
336
  subject.process_key,
347
337
  [:tasks_count, 100],
348
- [status, 1]
338
+ ["tasks_#{status}", 1]
349
339
  )
350
340
  end
351
341
 
@@ -365,33 +355,5 @@ describe Taskinator::Persistence, :redis => true do
365
355
  }
366
356
  end
367
357
 
368
- describe "#instrumentation_payload" do
369
- it {
370
- Taskinator.redis do |conn|
371
- conn.hset(subject.key, :process_uuid, subject.uuid)
372
- conn.hmset(
373
- subject.process_key,
374
- [:options, YAML.dump({:foo => :bar})],
375
- [:tasks_count, 100],
376
- [:completed, 3],
377
- [:cancelled, 2],
378
- [:failed, 1]
379
- )
380
- end
381
-
382
- expect(subject.instrumentation_payload(:baz => :qux)).to eq({
383
- :type => subject.class.name,
384
- :process_uuid => subject.uuid,
385
- :process_options => {:foo => :bar},
386
- :uuid => subject.uuid,
387
- :percentage_failed => 1.0,
388
- :percentage_cancelled => 2.0,
389
- :percentage_completed => 3.0,
390
- :tasks_count => 100,
391
- :baz => :qux
392
- })
393
- }
394
-
395
- end
396
358
  end
397
359
  end
@@ -6,7 +6,11 @@ describe Taskinator::Process do
6
6
 
7
7
  describe "Base" do
8
8
 
9
- subject { Class.new(Taskinator::Process).new(definition) }
9
+ subject do
10
+ Class.new(Taskinator::Process) do
11
+ include ProcessMethods
12
+ end.new(definition)
13
+ end
10
14
 
11
15
  describe "#initialize" do
12
16
  it { expect(subject.uuid).to_not be_nil }
@@ -47,17 +51,9 @@ describe Taskinator::Process do
47
51
  end
48
52
 
49
53
  describe "#current_state" do
50
- it { expect(subject).to be_a(::Workflow) }
54
+ it { expect(subject).to be_a(Taskinator::Workflow) }
51
55
  it { expect(subject.current_state).to_not be_nil }
52
- it { expect(subject.current_state.name).to eq(:initial) }
53
- end
54
-
55
- describe "#tasks_completed?" do
56
- it {
57
- expect {
58
- subject.tasks_completed?
59
- }.to raise_error(NotImplementedError)
60
- }
56
+ it { expect(subject.current_state).to eq(:initial) }
61
57
  end
62
58
 
63
59
  describe "workflow" do
@@ -70,9 +66,9 @@ describe Taskinator::Process do
70
66
  }
71
67
 
72
68
  it {
73
- expect(subject.current_state.name).to eq(:initial)
69
+ expect(subject.current_state).to eq(:initial)
74
70
  subject.enqueue!
75
- expect(subject.current_state.name).to eq(:enqueued)
71
+ expect(subject.current_state).to eq(:enqueued)
76
72
  }
77
73
  end
78
74
 
@@ -83,9 +79,9 @@ describe Taskinator::Process do
83
79
  subject.start!
84
80
  }
85
81
  it {
86
- expect(subject.current_state.name).to eq(:initial)
82
+ expect(subject.current_state).to eq(:initial)
87
83
  subject.start!
88
- expect(subject.current_state.name).to eq(:processing)
84
+ expect(subject.current_state).to eq(:processing)
89
85
  }
90
86
  end
91
87
 
@@ -96,9 +92,9 @@ describe Taskinator::Process do
96
92
  subject.cancel!
97
93
  }
98
94
  it {
99
- expect(subject.current_state.name).to eq(:initial)
95
+ expect(subject.current_state).to eq(:initial)
100
96
  subject.cancel!
101
- expect(subject.current_state.name).to eq(:cancelled)
97
+ expect(subject.current_state).to eq(:cancelled)
102
98
  }
103
99
  end
104
100
 
@@ -112,7 +108,7 @@ describe Taskinator::Process do
112
108
  it {
113
109
  subject.start!
114
110
  subject.pause!
115
- expect(subject.current_state.name).to eq(:paused)
111
+ expect(subject.current_state).to eq(:paused)
116
112
  }
117
113
  end
118
114
 
@@ -128,7 +124,7 @@ describe Taskinator::Process do
128
124
  subject.start!
129
125
  subject.pause!
130
126
  subject.resume!
131
- expect(subject.current_state.name).to eq(:processing)
127
+ expect(subject.current_state).to eq(:processing)
132
128
  }
133
129
  end
134
130
 
@@ -143,21 +139,22 @@ describe Taskinator::Process do
143
139
  it {
144
140
  subject.start!
145
141
  subject.complete!
146
- expect(subject.current_state.name).to eq(:completed)
142
+ expect(subject.current_state).to eq(:completed)
147
143
  }
148
144
  end
149
145
 
150
146
  describe "#fail!" do
151
147
  it { expect(subject).to respond_to(:fail!) }
152
148
  it {
153
- expect(subject).to receive(:fail)
149
+ error = StandardError.new
150
+ expect(subject).to receive(:fail).with(error)
154
151
  subject.start!
155
- subject.fail!
152
+ subject.fail!(error)
156
153
  }
157
154
  it {
158
155
  subject.start!
159
- subject.fail!
160
- expect(subject.current_state.name).to eq(:failed)
156
+ subject.fail!(StandardError.new)
157
+ expect(subject.current_state).to eq(:failed)
161
158
  }
162
159
  end
163
160
  end
@@ -176,7 +173,7 @@ describe Taskinator::Process do
176
173
  subject.parent = double('parent')
177
174
  expect(subject.parent).to receive(:fail!)
178
175
  subject.start!
179
- subject.fail!
176
+ subject.fail!(StandardError.new)
180
177
  end
181
178
  end
182
179
 
@@ -196,6 +193,8 @@ describe Taskinator::Process do
196
193
  expect(visitor).to receive(:visit_task_reference).with(:parent)
197
194
  expect(visitor).to receive(:visit_tasks)
198
195
  expect(visitor).to receive(:visit_attribute).with(:queue)
196
+ expect(visitor).to receive(:visit_attribute_time).with(:created_at)
197
+ expect(visitor).to receive(:visit_attribute_time).with(:updated_at)
199
198
 
200
199
  subject.accept(visitor)
201
200
  }
@@ -211,12 +210,10 @@ describe Taskinator::Process do
211
210
 
212
211
  describe Taskinator::Process::Sequential do
213
212
 
214
- it_should_behave_like "a process", Taskinator::Process::Sequential do
215
- let(:process) { Taskinator::Process.define_sequential_process_for(definition) }
216
- end
217
-
218
213
  subject { Taskinator::Process.define_sequential_process_for(definition) }
219
214
 
215
+ it_should_behave_like "a process", Taskinator::Process::Sequential
216
+
220
217
  let(:tasks) {
221
218
  [
222
219
  Class.new(Taskinator::Task).new(subject),
@@ -333,6 +330,8 @@ describe Taskinator::Process do
333
330
  expect(visitor).to receive(:visit_task_reference).with(:parent)
334
331
  expect(visitor).to receive(:visit_tasks)
335
332
  expect(visitor).to receive(:visit_attribute).with(:queue)
333
+ expect(visitor).to receive(:visit_attribute_time).with(:created_at)
334
+ expect(visitor).to receive(:visit_attribute_time).with(:updated_at)
336
335
 
337
336
  subject.accept(visitor)
338
337
  }
@@ -344,13 +343,14 @@ describe Taskinator::Process do
344
343
  end
345
344
 
346
345
  describe Taskinator::Process::Concurrent do
346
+
347
+ subject { Taskinator::Process.define_concurrent_process_for(definition) }
348
+
347
349
  it_should_behave_like "a process", Taskinator::Process::Concurrent do
348
- let(:process) { Taskinator::Process.define_concurrent_process_for(definition, Taskinator::CompleteOn::First) }
349
350
 
350
- it { expect(process.complete_on).to eq(Taskinator::CompleteOn::First) }
351
- end
351
+ it { expect(subject.complete_on).to eq(Taskinator::CompleteOn::Default) }
352
352
 
353
- subject { Taskinator::Process.define_concurrent_process_for(definition) }
353
+ end
354
354
 
355
355
  let(:tasks) {
356
356
  [
@@ -495,11 +495,13 @@ describe Taskinator::Process do
495
495
  visitor = double('visitor')
496
496
  expect(visitor).to receive(:visit_type).with(:definition)
497
497
  expect(visitor).to receive(:visit_attribute).with(:uuid)
498
- expect(visitor).to receive(:visit_attribute).with(:complete_on)
498
+ expect(visitor).to receive(:visit_attribute_enum).with(:complete_on, Taskinator::CompleteOn)
499
499
  expect(visitor).to receive(:visit_args).with(:options)
500
500
  expect(visitor).to receive(:visit_task_reference).with(:parent)
501
501
  expect(visitor).to receive(:visit_tasks)
502
502
  expect(visitor).to receive(:visit_attribute).with(:queue)
503
+ expect(visitor).to receive(:visit_attribute_time).with(:created_at)
504
+ expect(visitor).to receive(:visit_attribute_time).with(:updated_at)
503
505
 
504
506
  subject.accept(visitor)
505
507
  }
@@ -48,45 +48,4 @@ describe Taskinator::Queues::DelayedJobAdapter, :delayed_job do
48
48
  end
49
49
  end
50
50
 
51
- describe "JobWorker" do
52
- it "enqueues jobs" do
53
- job = double('job')
54
- job_task = double('job_task', :uuid => uuid, :job => job, :queue => nil)
55
-
56
- expect {
57
- subject.enqueue_job(job_task)
58
- }.to change(Delayed::Job.queue, :size).by(1)
59
- end
60
-
61
- it "enqueues job to specified queue" do
62
- job = double('job')
63
- job_task = double('job_task', :uuid => uuid, :job => job, :queue => :other)
64
- subject.enqueue_job(job_task)
65
-
66
- expect(Delayed::Job.contains?(adapter::JobWorker, uuid, :other)).to be
67
- end
68
-
69
- it "calls job worker" do
70
- expect_any_instance_of(Taskinator::JobWorker).to receive(:perform)
71
- adapter::JobWorker.new(uuid).perform
72
- end
73
-
74
- let(:definition) { TestDefinition }
75
-
76
- it "performs invocation on job" do
77
- args = [:foo, {:a => 1}]
78
- job = double('job')
79
- expect(job).to receive(:perform)
80
-
81
- job_class = double('job_class', :instance_methods => [:perform])
82
- allow(job_class).to receive(:new).with(*args) { job }
83
-
84
- process = Taskinator::Process::Sequential.new(definition)
85
- job_task = Taskinator::Task.define_job_task(process, job_class, args)
86
-
87
- allow(Taskinator::Task).to receive(:fetch).with(uuid) { job_task }
88
-
89
- adapter::JobWorker.new(uuid).perform
90
- end
91
- end
92
51
  end
@@ -55,55 +55,4 @@ describe Taskinator::Queues::ResqueAdapter, :resque do
55
55
  end
56
56
  end
57
57
 
58
- describe "JobWorker" do
59
- it "enqueues jobs" do
60
- worker = adapter::JobWorker
61
-
62
- job = double('job')
63
- job_task = double('job_task', :uuid => uuid, :job => job, :queue => nil)
64
-
65
- subject.enqueue_job(job_task)
66
- expect(worker).to have_queued(uuid)
67
- end
68
-
69
- it "enqueues job to queue of the job class" do
70
- worker = adapter::JobWorker
71
-
72
- job = double('job', :queue => :job)
73
- job_task = double('job_task', :uuid => uuid, :job => job, :queue => nil)
74
-
75
- subject.enqueue_job(job_task)
76
- expect(worker).to have_queued(uuid).in(:job)
77
- end
78
-
79
- it "enqueues job to specified queue" do
80
- worker = adapter::JobWorker
81
-
82
- job = double('job')
83
- job_task = double('job_task', :uuid => uuid, :job => job, :queue => :other)
84
-
85
- subject.enqueue_job(job_task)
86
- expect(worker).to have_queued(uuid).in(:other)
87
- end
88
-
89
- it "calls job worker" do
90
- expect_any_instance_of(Taskinator::JobWorker).to receive(:perform)
91
- adapter::JobWorker.perform(uuid)
92
- end
93
-
94
- let(:definition) { TestDefinition }
95
-
96
- it "performs invocation on job" do
97
- args = [:foo, {:a => 1}]
98
- job_class = double('job_class', :methods => [:perform])
99
- expect(job_class).to receive(:perform).with(*args)
100
-
101
- process = Taskinator::Process::Sequential.new(definition)
102
- job_task = Taskinator::Task.define_job_task(process, job_class, args)
103
-
104
- allow(Taskinator::Task).to receive(:fetch).with(uuid) { job_task }
105
-
106
- adapter::JobWorker.perform(uuid)
107
- end
108
- end
109
58
  end
@@ -52,54 +52,4 @@ describe Taskinator::Queues::SidekiqAdapter, :sidekiq do
52
52
  end
53
53
  end
54
54
 
55
- describe "JobWorker" do
56
- it "enqueues jobs" do
57
- worker = adapter::JobWorker
58
-
59
- job = double('job', :get_sidekiq_options => {})
60
- job_task = double('job_task', :uuid => uuid, :job => job, :queue => nil)
61
-
62
- subject.enqueue_job(job_task)
63
- expect(worker).to have_enqueued_job(job_task.uuid)
64
- end
65
-
66
- it "enqueues job to queue of the job class" do
67
- job = double('job', :get_sidekiq_options => {:queue => :job})
68
- job_task = double('job_task', :uuid => uuid, :job => job, :queue => nil)
69
- subject.enqueue_job(job_task)
70
-
71
- expect(adapter::JobWorker).to be_processed_in_x(:job)
72
- end
73
-
74
- it "enqueues job to specified queue" do
75
- job = double('job', :get_sidekiq_options => {})
76
- job_task = double('job_task', :uuid => uuid, :job => job, :queue => :other)
77
- subject.enqueue_job(job_task)
78
-
79
- expect(adapter::JobWorker).to be_processed_in_x(:other)
80
- end
81
-
82
- it "calls job worker" do
83
- expect_any_instance_of(Taskinator::JobWorker).to receive(:perform)
84
- adapter::JobWorker.new.perform(uuid)
85
- end
86
-
87
- let(:definition) { TestDefinition }
88
-
89
- it "performs invocation on job" do
90
- args = [:foo, {:a => 1}]
91
- job = double('job')
92
- expect(job).to receive(:perform).with(*args)
93
-
94
- job_class = double('job_class', :get_sidekiq_options => {}, :instance_methods => [:perform])
95
- allow(job_class).to receive(:new) { job }
96
-
97
- process = Taskinator::Process::Sequential.new(definition)
98
- job_task = Taskinator::Task.define_job_task(process, job_class, args)
99
-
100
- allow(Taskinator::Task).to receive(:fetch).with(uuid) { job_task }
101
-
102
- adapter::JobWorker.new.perform(uuid)
103
- end
104
- end
105
55
  end
@@ -5,7 +5,7 @@ describe Taskinator::Queues do
5
5
  it "raise error for an unknown adapter" do
6
6
  expect {
7
7
  Taskinator::Queues.create_adapter(:unknown)
8
- }.to raise_error
8
+ }.to raise_error(StandardError)
9
9
  end
10
10
 
11
11
  it "passes configuration to adapter initializer" do