taskinator 0.5.0 → 0.5.1

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