taskinator 0.5.0 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -35,6 +35,14 @@ describe Taskinator::Process do
35
35
  it { expect(subject.tasks).to be_a(Taskinator::Tasks) }
36
36
  end
37
37
 
38
+ describe "#no_tasks_defined?" do
39
+ it { expect(subject.no_tasks_defined?).to be }
40
+ it {
41
+ subject.tasks << :mock
42
+ expect(subject.no_tasks_defined?).to_not be
43
+ }
44
+ end
45
+
38
46
  describe "#to_s" do
39
47
  it { expect(subject.to_s).to match(/#{subject.uuid}/) }
40
48
  end
@@ -288,7 +296,7 @@ describe Taskinator::Process do
288
296
  subject.task_completed(task1)
289
297
  end
290
298
 
291
- it "deincrements the pending task count" do
299
+ it "decrements the pending task count" do
292
300
  tasks.each {|t| subject.tasks << t }
293
301
  task1 = tasks[0]
294
302
  task2 = tasks[1]
@@ -367,7 +375,7 @@ describe Taskinator::Process do
367
375
  subject.task_failed(tasks.first, error)
368
376
  end
369
377
 
370
- it "doesn't deincement pending task count" do
378
+ it "doesn't decrement pending task count" do
371
379
  tasks.each {|t| subject.tasks << t }
372
380
 
373
381
  expect(subject).to_not receive(:deincr_pending_tasks)
@@ -515,7 +523,7 @@ describe Taskinator::Process do
515
523
  end
516
524
  end
517
525
 
518
- it "deincrements the pending task count" do
526
+ it "decrements the pending task count" do
519
527
  tasks.each {|t| subject.tasks << t }
520
528
  task1 = tasks[0]
521
529
  task2 = tasks[1]
@@ -93,6 +93,16 @@ describe Taskinator::Task do
93
93
  }
94
94
  end
95
95
 
96
+ describe "#cancel!" do
97
+ it { expect(subject).to respond_to(:cancel!) }
98
+ it {
99
+ expect(subject).to receive(:cancel)
100
+ subject.start!
101
+ subject.cancel!
102
+ expect(subject.current_state).to eq(:cancelled)
103
+ }
104
+ end
105
+
96
106
  describe "#fail!" do
97
107
  it { expect(subject).to respond_to(:fail!) }
98
108
  it {
@@ -144,6 +154,7 @@ describe Taskinator::Task do
144
154
  visitor = double('visitor')
145
155
  expect(visitor).to receive(:visit_attribute).with(:uuid)
146
156
  expect(visitor).to receive(:visit_process_reference).with(:process)
157
+ expect(visitor).to receive(:visit_type).with(:definition)
147
158
  expect(visitor).to receive(:visit_task_reference).with(:next)
148
159
  expect(visitor).to receive(:visit_args).with(:options)
149
160
  expect(visitor).to receive(:visit_attribute).with(:queue)
@@ -250,6 +261,18 @@ describe Taskinator::Task do
250
261
  expect(exec_context.options).to eq(subject.options)
251
262
  end
252
263
 
264
+ it "throws an exception for unknown definition type" do
265
+ executor = Taskinator::Executor.new(Taskinator::Persistence::UnknownType.new("Foo"), subject)
266
+
267
+ # replace the internal executor instance for the task
268
+ # with this one, so we can hook into the methods
269
+ subject.instance_eval { @executor = executor }
270
+
271
+ expect {
272
+ subject.start!
273
+ }.to raise_error(Taskinator::Persistence::UnknownTypeError)
274
+ end
275
+
253
276
  it "is instrumented" do
254
277
  instrumentation_block = SpecSupport::Block.new
255
278
 
@@ -314,36 +337,12 @@ describe Taskinator::Task do
314
337
 
315
338
  describe "#inspect" do
316
339
  it { expect(subject.inspect).to_not be_nil }
340
+ it { expect(subject.inspect).to include(definition.name) }
317
341
  end
318
342
  end
319
343
 
320
344
  describe Taskinator::Task::Job do
321
345
 
322
- module TestJob
323
- def self.perform(*args)
324
- end
325
- end
326
-
327
- class TestJobClass
328
- def perform(*args)
329
- end
330
- end
331
-
332
- module TestJobModule
333
- def self.perform(*args)
334
- end
335
- end
336
-
337
- class TestJobClassNoArgs
338
- def perform
339
- end
340
- end
341
-
342
- module TestJobModuleNoArgs
343
- def self.perform
344
- end
345
- end
346
-
347
346
  subject { Taskinator::Task.define_job_task(process, TestJob, [1, {:a => 1, :b => 2}]) }
348
347
 
349
348
  it_should_behave_like "a task", Taskinator::Task::Job
@@ -404,6 +403,22 @@ describe Taskinator::Task do
404
403
  task.start!
405
404
  }
406
405
 
406
+ it "throws an exception when unknown job type" do
407
+ task = Taskinator::Task.define_job_task(process, Taskinator::Persistence::UnknownType.new("Foo"), nil)
408
+
409
+ expect {
410
+ task.start!
411
+ }.to raise_error(Taskinator::Persistence::UnknownTypeError)
412
+ end
413
+
414
+ it "handles failure" do
415
+ task = Taskinator::Task.define_job_task(process, TestJobError, nil)
416
+
417
+ expect {
418
+ task.start!
419
+ }.to raise_error(ArgumentError)
420
+ end
421
+
407
422
  it "is instrumented" do
408
423
  allow(process).to receive(:task_completed).with(subject)
409
424
 
@@ -473,6 +488,7 @@ describe Taskinator::Task do
473
488
 
474
489
  describe "#inspect" do
475
490
  it { expect(subject.inspect).to_not be_nil }
491
+ it { expect(subject.inspect).to include(definition.name) }
476
492
  end
477
493
  end
478
494
 
@@ -582,6 +598,7 @@ describe Taskinator::Task do
582
598
  visitor = double('visitor')
583
599
  expect(visitor).to receive(:visit_attribute).with(:uuid)
584
600
  expect(visitor).to receive(:visit_process_reference).with(:process)
601
+ expect(visitor).to receive(:visit_type).with(:definition)
585
602
  expect(visitor).to receive(:visit_task_reference).with(:next)
586
603
  expect(visitor).to receive(:visit_args).with(:options)
587
604
  expect(visitor).to receive(:visit_process).with(:sub_process)
@@ -595,6 +612,7 @@ describe Taskinator::Task do
595
612
 
596
613
  describe "#inspect" do
597
614
  it { expect(subject.inspect).to_not be_nil }
615
+ it { expect(subject.inspect).to include(definition.name) }
598
616
  end
599
617
  end
600
618
 
@@ -86,6 +86,22 @@ describe Taskinator do
86
86
  end
87
87
  end
88
88
  end
89
+ end
90
+
91
+ [
92
+ Taskinator::NoOpInstrumenter,
93
+ Taskinator::ConsoleInstrumenter
94
+ ].each do |instrumenter|
95
+ describe instrumenter do
96
+ it "yields to given block" do
97
+ instance = instrumenter.new
98
+
99
+ block = SpecSupport::Block.new
100
+ expect(block).to receive(:call)
89
101
 
102
+ instance.instrument(:foo, :bar => :baz, &block)
103
+ end
104
+ end
90
105
  end
106
+
91
107
  end
data/taskinator.gemspec CHANGED
@@ -26,8 +26,7 @@ Gem::Specification.new do |spec|
26
26
  spec.add_dependency 'connection_pool' , '>= 2.2.0'
27
27
  spec.add_dependency 'json' , '>= 1.8.2'
28
28
  spec.add_dependency 'builder' , '>= 3.2.2'
29
- spec.add_dependency 'globalid' , '~> 0.3'
30
- spec.add_dependency 'statsd-ruby' , '~> 1.4.0'
31
- spec.add_dependency 'thwait' , '~> 0.2'
29
+ spec.add_dependency 'globalid' , '>= 0.3'
30
+ spec.add_dependency 'thwait' , '>= 0.2'
32
31
 
33
32
  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.5.0
4
+ version: 0.5.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Stefano
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-02-18 00:00:00.000000000 Z
11
+ date: 2023-01-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -84,42 +84,28 @@ dependencies:
84
84
  name: globalid
85
85
  requirement: !ruby/object:Gem::Requirement
86
86
  requirements:
87
- - - "~>"
87
+ - - ">="
88
88
  - !ruby/object:Gem::Version
89
89
  version: '0.3'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - "~>"
94
+ - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0.3'
97
- - !ruby/object:Gem::Dependency
98
- name: statsd-ruby
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - "~>"
102
- - !ruby/object:Gem::Version
103
- version: 1.4.0
104
- type: :runtime
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - "~>"
109
- - !ruby/object:Gem::Version
110
- version: 1.4.0
111
97
  - !ruby/object:Gem::Dependency
112
98
  name: thwait
113
99
  requirement: !ruby/object:Gem::Requirement
114
100
  requirements:
115
- - - "~>"
101
+ - - ">="
116
102
  - !ruby/object:Gem::Version
117
103
  version: '0.2'
118
104
  type: :runtime
119
105
  prerelease: false
120
106
  version_requirements: !ruby/object:Gem::Requirement
121
107
  requirements:
122
- - - "~>"
108
+ - - ">="
123
109
  - !ruby/object:Gem::Version
124
110
  version: '0.2'
125
111
  description: Simple process orchestration
@@ -134,7 +120,8 @@ files:
134
120
  - ".github/ISSUE_TEMPLATE/bug_report.md"
135
121
  - ".github/ISSUE_TEMPLATE/custom.md"
136
122
  - ".github/ISSUE_TEMPLATE/feature_request.md"
137
- - ".github/workflows/taskinator.yml"
123
+ - ".github/workflows/build.yml"
124
+ - ".github/workflows/release.yml"
138
125
  - ".gitignore"
139
126
  - ".rspec"
140
127
  - ".ruby-gemset"
@@ -151,13 +138,12 @@ files:
151
138
  - bin/setup
152
139
  - lib/taskinator.rb
153
140
  - lib/taskinator/api.rb
141
+ - lib/taskinator/builder.rb
154
142
  - lib/taskinator/complete_on.rb
155
143
  - lib/taskinator/create_process_worker.rb
156
144
  - lib/taskinator/definition.rb
157
- - lib/taskinator/definition/builder.rb
158
145
  - lib/taskinator/executor.rb
159
146
  - lib/taskinator/instrumentation.rb
160
- - lib/taskinator/log_stats.rb
161
147
  - lib/taskinator/logger.rb
162
148
  - lib/taskinator/persistence.rb
163
149
  - lib/taskinator/process.rb
@@ -173,7 +159,6 @@ files:
173
159
  - lib/taskinator/version.rb
174
160
  - lib/taskinator/visitor.rb
175
161
  - lib/taskinator/workflow.rb
176
- - lib/taskinator/xml_visitor.rb
177
162
  - processes_workflow.png
178
163
  - sequence.txt
179
164
  - spec/examples/process_examples.rb
@@ -191,13 +176,17 @@ files:
191
176
  - spec/support/test_flow.rb
192
177
  - spec/support/test_flows.rb
193
178
  - spec/support/test_instrumenter.rb
179
+ - spec/support/test_job.rb
180
+ - spec/support/test_job_task.rb
194
181
  - spec/support/test_process.rb
195
182
  - spec/support/test_queue.rb
183
+ - spec/support/test_step_task.rb
184
+ - spec/support/test_subprocess_task.rb
196
185
  - spec/support/test_task.rb
197
186
  - spec/taskinator/api_spec.rb
187
+ - spec/taskinator/builder_spec.rb
198
188
  - spec/taskinator/complex_process_spec.rb
199
189
  - spec/taskinator/create_process_worker_spec.rb
200
- - spec/taskinator/definition/builder_spec.rb
201
190
  - spec/taskinator/definition_spec.rb
202
191
  - spec/taskinator/executor_spec.rb
203
192
  - spec/taskinator/instrumentation_spec.rb
@@ -215,7 +204,6 @@ files:
215
204
  - spec/taskinator/tasks_spec.rb
216
205
  - spec/taskinator/test_flows_spec.rb
217
206
  - spec/taskinator/visitor_spec.rb
218
- - spec/taskinator/xml_visitor_spec.rb
219
207
  - taskinator.gemspec
220
208
  - tasks_workflow.png
221
209
  homepage: https://github.com/virtualstaticvoid/taskinator
@@ -258,13 +246,17 @@ test_files:
258
246
  - spec/support/test_flow.rb
259
247
  - spec/support/test_flows.rb
260
248
  - spec/support/test_instrumenter.rb
249
+ - spec/support/test_job.rb
250
+ - spec/support/test_job_task.rb
261
251
  - spec/support/test_process.rb
262
252
  - spec/support/test_queue.rb
253
+ - spec/support/test_step_task.rb
254
+ - spec/support/test_subprocess_task.rb
263
255
  - spec/support/test_task.rb
264
256
  - spec/taskinator/api_spec.rb
257
+ - spec/taskinator/builder_spec.rb
265
258
  - spec/taskinator/complex_process_spec.rb
266
259
  - spec/taskinator/create_process_worker_spec.rb
267
- - spec/taskinator/definition/builder_spec.rb
268
260
  - spec/taskinator/definition_spec.rb
269
261
  - spec/taskinator/executor_spec.rb
270
262
  - spec/taskinator/instrumentation_spec.rb
@@ -282,4 +274,3 @@ test_files:
282
274
  - spec/taskinator/tasks_spec.rb
283
275
  - spec/taskinator/test_flows_spec.rb
284
276
  - spec/taskinator/visitor_spec.rb
285
- - spec/taskinator/xml_visitor_spec.rb
@@ -1,129 +0,0 @@
1
- module Taskinator
2
- module Definition
3
- class Builder
4
-
5
- attr_reader :process
6
- attr_reader :definition
7
- attr_reader :args
8
- attr_reader :builder_options
9
-
10
- def initialize(process, definition, *args)
11
- @process = process
12
- @definition = definition
13
- @builder_options = args.last.is_a?(Hash) ? args.pop : {}
14
- @args = args
15
- @executor = Taskinator::Executor.new(@definition)
16
- end
17
-
18
- def option?(key, &block)
19
- yield if builder_options[key]
20
- end
21
-
22
- # defines a sub process of tasks which are executed sequentially
23
- def sequential(options={}, &block)
24
- raise ArgumentError, 'block' unless block_given?
25
-
26
- sub_process = Process.define_sequential_process_for(@definition, options)
27
- task = define_sub_process_task(@process, sub_process, options)
28
- Builder.new(sub_process, @definition, *@args).instance_eval(&block)
29
- @process.tasks << task if sub_process.tasks.any?
30
- nil
31
- end
32
-
33
- # defines a sub process of tasks which are executed concurrently
34
- def concurrent(complete_on=CompleteOn::Default, options={}, &block)
35
- raise ArgumentError, 'block' unless block_given?
36
-
37
- sub_process = Process.define_concurrent_process_for(@definition, complete_on, options)
38
- task = define_sub_process_task(@process, sub_process, options)
39
- Builder.new(sub_process, @definition, *@args).instance_eval(&block)
40
- @process.tasks << task if sub_process.tasks.any?
41
- nil
42
- end
43
-
44
- # dynamically defines tasks, using the given @iterator method
45
- # the definition will be evaluated for each yielded item
46
- def for_each(method, options={}, &block)
47
- raise ArgumentError, 'method' if method.nil?
48
- raise NoMethodError, method unless @executor.respond_to?(method)
49
- raise ArgumentError, 'block' unless block_given?
50
-
51
- #
52
- # `for_each` is an exception, since it invokes the definition
53
- # in order to yield elements to the builder, and any options passed
54
- # are included with the builder options
55
- #
56
- method_args = options.any? ? [*@args, options] : @args
57
- @executor.send(method, *method_args) do |*args|
58
- Builder.new(@process, @definition, *args).instance_eval(&block)
59
- end
60
- nil
61
- end
62
-
63
- alias_method :transform, :for_each
64
-
65
- # defines a task which executes the given @method
66
- def task(method, options={})
67
- raise ArgumentError, 'method' if method.nil?
68
- raise NoMethodError, method unless @executor.respond_to?(method)
69
-
70
- define_step_task(@process, method, @args, options)
71
- nil
72
- end
73
-
74
- # defines a task which executes the given @job
75
- # which is expected to implement a perform method either as a class or instance method
76
- def job(job, options={})
77
- raise ArgumentError, 'job' if job.nil?
78
- raise ArgumentError, 'job' unless job.methods.include?(:perform) || job.instance_methods.include?(:perform)
79
-
80
- define_job_task(@process, job, @args, options)
81
- nil
82
- end
83
-
84
- # defines a sub process task, for the given @definition
85
- # the definition specified must have input compatible arguments
86
- # to the current definition
87
- def sub_process(definition, options={})
88
- raise ArgumentError, 'definition' if definition.nil?
89
- raise ArgumentError, "#{definition.name} does not extend the #{Definition.name} module" unless definition.kind_of?(Definition)
90
-
91
- # TODO: decide whether the sub process to dynamically receive arguments
92
-
93
- sub_process = definition.create_sub_process(*@args, combine_options(options))
94
- task = define_sub_process_task(@process, sub_process, options)
95
- Builder.new(sub_process, definition, *@args)
96
- @process.tasks << task if sub_process.tasks.any?
97
- nil
98
- end
99
-
100
- private
101
-
102
- def define_step_task(process, method, args, options={})
103
- define_task(process) {
104
- Task.define_step_task(process, method, args, combine_options(options))
105
- }
106
- end
107
-
108
- def define_job_task(process, job, args, options={})
109
- define_task(process) {
110
- Task.define_job_task(process, job, args, combine_options(options))
111
- }
112
- end
113
-
114
- def define_sub_process_task(process, sub_process, options={})
115
- Task.define_sub_process_task(process, sub_process, combine_options(options))
116
- end
117
-
118
- def define_task(process)
119
- process.tasks << task = yield
120
- task
121
- end
122
-
123
- def combine_options(options={})
124
- builder_options.merge(options)
125
- end
126
-
127
- end
128
- end
129
- end
@@ -1,49 +0,0 @@
1
- require 'statsd'
2
-
3
- module Taskinator
4
- module LogStats
5
- class << self
6
-
7
- def initialize_client
8
- @client = Statsd.new()
9
- end
10
-
11
- def client
12
- defined?(@client) ? @client : initialize_client
13
- end
14
-
15
- def client=(statsd_client)
16
- @client = (statsd_client ? statsd_client : initialize_client)
17
- end
18
-
19
- def duration(stat, duration)
20
- client.timing(stat, duration * 1000)
21
- end
22
-
23
- def timing(stat, &block)
24
- result = nil
25
- duration = Benchmark.realtime do
26
- result = yield
27
- end
28
- duration(stat, duration)
29
- result
30
- end
31
-
32
- def gauge(stat, count)
33
- client.gauge(stat, count)
34
- end
35
-
36
- def count(stat, count)
37
- client.count(stat, count)
38
- end
39
-
40
- def increment(stat)
41
- client.increment(stat)
42
- end
43
-
44
- def decrement(stat)
45
- client.decrement(stat)
46
- end
47
- end
48
- end
49
- end
@@ -1,109 +0,0 @@
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
@@ -1,5 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe Taskinator::Visitor::XmlVisitor do
4
- pending
5
- end