taskinator 0.2.0 → 0.3.0

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