taskinator 0.3.13 → 0.4.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f306587dcab890524eee15dc606291f27cc63f7d
4
- data.tar.gz: 5b273db15436480b8d74991e55f460bec917064b
2
+ SHA256:
3
+ metadata.gz: 4a5f5f63478b9c1b1419e33baf6039f8e92153638ba2c08dc60cadd1a968d117
4
+ data.tar.gz: e222c79a85490ec2cbced43833b05b40e97dc6863782586505b19b178e44f664
5
5
  SHA512:
6
- metadata.gz: ba3c47d3c4d0d32fd4ec32b09b651c170d805b82b4c0be0bee40ce64524ccbc2b2e0a45dde459ca515059bc3ca1035c9385da85dcda19492a43542a1afcc77e5
7
- data.tar.gz: 907671c46cddb1155005b708d15f9fbb10457723d5219a18184aa0bbe407014005e45abb32dca67262aefa75f99dfcaa512e2116691854d62c25668debfbf7f0
6
+ metadata.gz: ffd5ee0e0b9bfc78e9f44086d2b60986483c4668acf9b9a282ed3110d15d59477fec68b1042876d52899fe2531648ccd793efa9ba92415231ad4e637922b8049
7
+ data.tar.gz: 99fb91f000e510dd98eda1d843e644c563ca71288759e7d23493a605b7f7b342a116cff2d2bd530250fb552cd88d728a67171fafb49419461baa7200355d9e47
data/.ruby-version CHANGED
@@ -1 +1 @@
1
- ruby-2.4.2
1
+ ruby-2.7.2
data/.travis.yml CHANGED
@@ -1,14 +1,16 @@
1
+ os: linux
2
+ dist: xenial
1
3
  language: ruby
2
- sudo: false # See http://docs.travis-ci.com/user/migrating-from-legacy
3
4
  cache: bundler
4
5
 
5
6
  services:
6
- - redis-server
7
+ - redis
7
8
 
8
9
  rvm:
9
- - 2.2.8
10
- - 2.3.5
11
- - 2.4.2
10
+ - 2.5.8
11
+ - 2.6.6
12
+ - 2.7.2
13
+ - 3.0.0
12
14
 
13
15
  script: 'bundle exec rake spec'
14
16
 
data/CHANGELOG.md CHANGED
@@ -1,3 +1,56 @@
1
+ v0.4.1 - 15 Mar 2021
2
+ ---
3
+ Optimisation to exclude sub-processes which don't have any tasks.
4
+ Preparations for upgrade to Ruby 3 and ActiveSupport 6
5
+
6
+ v0.4.0 - 4 Mar 2021
7
+ ---
8
+ Bug fix `job` tasks which have no arguments to the `perform` method.
9
+ Added support for having `perform` method as a class method.
10
+
11
+ v0.3.16 - 17 Feb 2021
12
+ ---
13
+ Bug fix to deincrement pending counts for sequential tasks.
14
+ Bug fix to allow concurrent tasks to be retried (via Resque) and to complete processes.
15
+
16
+ v0.3.15 - 22 Nov 2018
17
+ ---
18
+ Updated dependencies.
19
+
20
+ v0.3.14 - 13 Jul 2018
21
+ ---
22
+ Updated dependencies.
23
+ Removed gemnasium.
24
+
25
+ v0.3.13 - 23 Sep 2017
26
+ ---
27
+ Updated dependencies.
28
+
29
+ v0.3.12 - 23 Sep 2017
30
+ ---
31
+ Spec fixes.
32
+ Updated dependencies.
33
+
34
+ v0.3.11 - 1 Nov 2016
35
+ ---
36
+ Removed `redis-semaphore` gem and use INCRBY to track pending concurrent tasks instead.
37
+ Added instrumentation using statsd.
38
+ Bug fixes to key expiry logic.
39
+ Refactored process and task state transistions.
40
+
41
+ v0.3.10 - 1 Nov 2016
42
+ ---
43
+ Added support for serializing to XML.
44
+ Improvements to process and task states.
45
+
46
+ v0.3.9 - 12 Sep 2016
47
+ ---
48
+ Added benchmark for redis-mutex.
49
+
50
+ v0.3.7 - 18 Aug 2016
51
+ ---
52
+ Bug fix to `option?` method.
53
+
1
54
  v0.3.6 - 11 Nov 2015
2
55
  ---
3
56
  Added visitor for performing clean up of completed processes/tasks.
data/Gemfile CHANGED
@@ -15,10 +15,10 @@ gem 'resque_spec' , '>= 0.16.0'
15
15
  # other
16
16
  gem 'bundler' , '>= 1.6.0'
17
17
  gem 'rake' , '>= 10.3.0'
18
- gem 'activesupport' , '~> 4.2.0'
18
+ gem 'activesupport' , '~> 5.2.0'
19
19
  gem 'rspec'
20
- gem 'coveralls' , '>= 0.7.0'
20
+ gem 'coveralls' , '>= 0.8.22'
21
21
  gem 'pry' , '>= 0.9.0'
22
22
  gem 'pry-byebug' , '>= 1.3.0'
23
23
 
24
- gem 'fakeredis' , '~> 0.6.0'
24
+ gem 'fakeredis' , '~> 0.7.0'
data/Gemfile.lock CHANGED
@@ -1,115 +1,120 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- taskinator (0.3.13)
4
+ taskinator (0.4.1)
5
5
  builder (>= 3.2.2)
6
6
  connection_pool (>= 2.2.0)
7
7
  globalid (~> 0.3)
8
8
  json (>= 1.8.2)
9
9
  redis (>= 3.2.1)
10
10
  redis-namespace (>= 1.5.2)
11
- redis-semaphore (>= 0.2.4)
12
- statsd-ruby (~> 1.2.0)
11
+ statsd-ruby (~> 1.4.0)
12
+ thwait (~> 0.2)
13
13
 
14
14
  GEM
15
15
  remote: https://rubygems.org/
16
16
  specs:
17
- activesupport (4.2.9)
18
- i18n (~> 0.7)
17
+ activesupport (5.2.4.5)
18
+ concurrent-ruby (~> 1.0, >= 1.0.2)
19
+ i18n (>= 0.7, < 2)
19
20
  minitest (~> 5.1)
20
- thread_safe (~> 0.3, >= 0.3.4)
21
21
  tzinfo (~> 1.1)
22
- builder (3.2.3)
23
- byebug (9.1.0)
24
- coderay (1.1.2)
25
- concurrent-ruby (1.0.5)
26
- connection_pool (2.2.1)
27
- coveralls (0.8.21)
22
+ builder (3.2.4)
23
+ byebug (11.1.3)
24
+ coderay (1.1.3)
25
+ concurrent-ruby (1.1.8)
26
+ connection_pool (2.2.3)
27
+ coveralls (0.8.23)
28
28
  json (>= 1.8, < 3)
29
- simplecov (~> 0.14.1)
29
+ simplecov (~> 0.16.1)
30
30
  term-ansicolor (~> 1.3)
31
- thor (~> 0.19.4)
31
+ thor (>= 0.19.4, < 2.0)
32
32
  tins (~> 1.6)
33
- delayed_job (4.1.3)
34
- activesupport (>= 3.0, < 5.2)
35
- diff-lcs (1.3)
36
- docile (1.1.5)
37
- fakeredis (0.6.0)
38
- redis (~> 3.2)
39
- globalid (0.4.0)
33
+ delayed_job (4.1.9)
34
+ activesupport (>= 3.0, < 6.2)
35
+ diff-lcs (1.4.4)
36
+ docile (1.3.5)
37
+ e2mmap (0.1.0)
38
+ fakeredis (0.7.0)
39
+ redis (>= 3.2, < 5.0)
40
+ globalid (0.4.2)
40
41
  activesupport (>= 4.2.0)
41
- i18n (0.8.6)
42
- json (2.1.0)
43
- method_source (0.8.2)
44
- minitest (5.10.3)
42
+ i18n (1.8.9)
43
+ concurrent-ruby (~> 1.0)
44
+ json (2.5.1)
45
+ method_source (1.0.0)
46
+ minitest (5.14.4)
45
47
  mono_logger (1.1.0)
46
- multi_json (1.12.2)
47
- mustermann (1.0.1)
48
- pry (0.11.0)
49
- coderay (~> 1.1.0)
50
- method_source (~> 0.8.1)
51
- pry-byebug (3.5.0)
52
- byebug (~> 9.1)
53
- pry (~> 0.10)
54
- rack (2.0.3)
55
- rack-protection (2.0.0)
48
+ multi_json (1.15.0)
49
+ mustermann (1.1.1)
50
+ ruby2_keywords (~> 0.0.1)
51
+ pry (0.13.1)
52
+ coderay (~> 1.1)
53
+ method_source (~> 1.0)
54
+ pry-byebug (3.9.0)
55
+ byebug (~> 11.0)
56
+ pry (~> 0.13.0)
57
+ rack (2.2.3)
58
+ rack-protection (2.1.0)
56
59
  rack
57
- rake (12.1.0)
58
- redis (3.3.3)
59
- redis-namespace (1.5.3)
60
- redis (~> 3.0, >= 3.0.4)
61
- redis-semaphore (0.3.1)
62
- redis
63
- resque (1.27.4)
60
+ rake (13.0.3)
61
+ redis (4.2.5)
62
+ redis-namespace (1.8.1)
63
+ redis (>= 3.0.4)
64
+ resque (2.0.0)
64
65
  mono_logger (~> 1.0)
65
66
  multi_json (~> 1.0)
66
- redis-namespace (~> 1.3)
67
+ redis-namespace (~> 1.6)
67
68
  sinatra (>= 0.9.2)
68
69
  vegas (~> 0.1.2)
69
- resque_spec (0.17.0)
70
- resque (>= 1.19.0)
70
+ resque_spec (0.18.1)
71
+ resque (>= 1.26.0)
71
72
  rspec-core (>= 3.0.0)
72
73
  rspec-expectations (>= 3.0.0)
73
74
  rspec-mocks (>= 3.0.0)
74
- rspec (3.6.0)
75
- rspec-core (~> 3.6.0)
76
- rspec-expectations (~> 3.6.0)
77
- rspec-mocks (~> 3.6.0)
78
- rspec-core (3.6.0)
79
- rspec-support (~> 3.6.0)
80
- rspec-expectations (3.6.0)
75
+ rspec (3.10.0)
76
+ rspec-core (~> 3.10.0)
77
+ rspec-expectations (~> 3.10.0)
78
+ rspec-mocks (~> 3.10.0)
79
+ rspec-core (3.10.1)
80
+ rspec-support (~> 3.10.0)
81
+ rspec-expectations (3.10.1)
81
82
  diff-lcs (>= 1.2.0, < 2.0)
82
- rspec-support (~> 3.6.0)
83
- rspec-mocks (3.6.0)
83
+ rspec-support (~> 3.10.0)
84
+ rspec-mocks (3.10.2)
84
85
  diff-lcs (>= 1.2.0, < 2.0)
85
- rspec-support (~> 3.6.0)
86
- rspec-sidekiq (3.0.3)
86
+ rspec-support (~> 3.10.0)
87
+ rspec-sidekiq (3.1.0)
87
88
  rspec-core (~> 3.0, >= 3.0.0)
88
89
  sidekiq (>= 2.4.0)
89
- rspec-support (3.6.0)
90
- sidekiq (5.0.4)
91
- concurrent-ruby (~> 1.0)
92
- connection_pool (~> 2.2, >= 2.2.0)
93
- rack-protection (>= 1.5.0)
94
- redis (~> 3.3, >= 3.3.3)
95
- simplecov (0.14.1)
96
- docile (~> 1.1.0)
90
+ rspec-support (3.10.2)
91
+ ruby2_keywords (0.0.4)
92
+ sidekiq (6.1.3)
93
+ connection_pool (>= 2.2.2)
94
+ rack (~> 2.0)
95
+ redis (>= 4.2.0)
96
+ simplecov (0.16.1)
97
+ docile (~> 1.1)
97
98
  json (>= 1.8, < 3)
98
99
  simplecov-html (~> 0.10.0)
99
100
  simplecov-html (0.10.2)
100
- sinatra (2.0.0)
101
+ sinatra (2.1.0)
101
102
  mustermann (~> 1.0)
102
- rack (~> 2.0)
103
- rack-protection (= 2.0.0)
103
+ rack (~> 2.2)
104
+ rack-protection (= 2.1.0)
104
105
  tilt (~> 2.0)
105
- statsd-ruby (1.2.1)
106
- term-ansicolor (1.6.0)
106
+ statsd-ruby (1.4.0)
107
+ sync (0.5.0)
108
+ term-ansicolor (1.7.1)
107
109
  tins (~> 1.0)
108
- thor (0.19.4)
110
+ thor (1.1.0)
109
111
  thread_safe (0.3.6)
110
- tilt (2.0.8)
111
- tins (1.15.0)
112
- tzinfo (1.2.3)
112
+ thwait (0.2.0)
113
+ e2mmap
114
+ tilt (2.0.10)
115
+ tins (1.28.0)
116
+ sync
117
+ tzinfo (1.2.9)
113
118
  thread_safe (~> 0.1)
114
119
  vegas (0.1.11)
115
120
  rack (>= 1.0.0)
@@ -118,11 +123,11 @@ PLATFORMS
118
123
  ruby
119
124
 
120
125
  DEPENDENCIES
121
- activesupport (~> 4.2.0)
126
+ activesupport (~> 5.2.0)
122
127
  bundler (>= 1.6.0)
123
- coveralls (>= 0.7.0)
128
+ coveralls (>= 0.8.22)
124
129
  delayed_job (~> 4.1.0)
125
- fakeredis (~> 0.6.0)
130
+ fakeredis (~> 0.7.0)
126
131
  pry (>= 0.9.0)
127
132
  pry-byebug (>= 1.3.0)
128
133
  rake (>= 10.3.0)
@@ -134,4 +139,4 @@ DEPENDENCIES
134
139
  taskinator!
135
140
 
136
141
  BUNDLED WITH
137
- 1.15.4
142
+ 2.2.14
data/README.md CHANGED
@@ -4,7 +4,6 @@
4
4
  [![Build Status](https://secure.travis-ci.org/virtualstaticvoid/taskinator.png?branch=master)](http://travis-ci.org/virtualstaticvoid/taskinator)
5
5
  [![Code Climate](https://codeclimate.com/github/virtualstaticvoid/taskinator.png)](https://codeclimate.com/github/virtualstaticvoid/taskinator)
6
6
  [![Coverage Status](https://coveralls.io/repos/virtualstaticvoid/taskinator/badge.png)](https://coveralls.io/r/virtualstaticvoid/taskinator)
7
- [![Dependency Status](https://gemnasium.com/virtualstaticvoid/taskinator.svg)](https://gemnasium.com/virtualstaticvoid/taskinator)
8
7
 
9
8
  A simple orchestration library for running complex processes or workflows in Ruby. Processes are defined using a simple DSL, where the sequences and
10
9
  tasks are defined. Processes can then be queued for execution. Sequences can be synchronous or asynchronous, and the overall process can be monitored
data/lib/taskinator.rb CHANGED
@@ -1,7 +1,6 @@
1
1
  require 'json'
2
2
  require 'yaml'
3
3
  require 'securerandom'
4
- require 'redis-semaphore'
5
4
  require 'benchmark'
6
5
  require 'delegate'
7
6
 
@@ -24,7 +24,10 @@ module Taskinator
24
24
  raise ArgumentError, 'block' unless block_given?
25
25
 
26
26
  sub_process = Process.define_sequential_process_for(@definition, options)
27
- Builder.new(define_sub_process_task(@process, sub_process, options), @definition, *@args).instance_eval(&block)
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
28
31
  end
29
32
 
30
33
  # defines a sub process of tasks which are executed concurrently
@@ -32,7 +35,10 @@ module Taskinator
32
35
  raise ArgumentError, 'block' unless block_given?
33
36
 
34
37
  sub_process = Process.define_concurrent_process_for(@definition, complete_on, options)
35
- Builder.new(define_sub_process_task(@process, sub_process, options), @definition, *@args).instance_eval(&block)
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
36
42
  end
37
43
 
38
44
  # dynamically defines tasks, using the given @iterator method
@@ -51,6 +57,7 @@ module Taskinator
51
57
  @executor.send(method, *method_args) do |*args|
52
58
  Builder.new(@process, @definition, *args).instance_eval(&block)
53
59
  end
60
+ nil
54
61
  end
55
62
 
56
63
  alias_method :transform, :for_each
@@ -61,6 +68,7 @@ module Taskinator
61
68
  raise NoMethodError, method unless @executor.respond_to?(method)
62
69
 
63
70
  define_step_task(@process, method, @args, options)
71
+ nil
64
72
  end
65
73
 
66
74
  # defines a task which executes the given @job
@@ -70,6 +78,7 @@ module Taskinator
70
78
  raise ArgumentError, 'job' unless job.methods.include?(:perform) || job.instance_methods.include?(:perform)
71
79
 
72
80
  define_job_task(@process, job, @args, options)
81
+ nil
73
82
  end
74
83
 
75
84
  # defines a sub process task, for the given @definition
@@ -82,7 +91,10 @@ module Taskinator
82
91
  # TODO: decide whether the sub process to dynamically receive arguments
83
92
 
84
93
  sub_process = definition.create_sub_process(*@args, combine_options(options))
85
- Builder.new(define_sub_process_task(@process, sub_process, options), definition, *@args)
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
86
98
  end
87
99
 
88
100
  private
@@ -100,10 +112,7 @@ module Taskinator
100
112
  end
101
113
 
102
114
  def define_sub_process_task(process, sub_process, options={})
103
- define_task(process) {
104
- Task.define_sub_process_task(process, sub_process, combine_options(options))
105
- }
106
- sub_process
115
+ Task.define_sub_process_task(process, sub_process, combine_options(options))
107
116
  end
108
117
 
109
118
  def define_task(process)
@@ -199,6 +199,12 @@ module Taskinator
199
199
  end
200
200
 
201
201
  def task_completed(task)
202
+ # deincrement the count of pending sequential tasks
203
+ pending = deincr_pending_tasks
204
+
205
+ Taskinator.statsd_client.count("taskinator.#{definition.name.underscore.parameterize}.pending", pending)
206
+ Taskinator.logger.info("Completed task for process '#{uuid}'. Pending is #{pending}.")
207
+
202
208
  next_task = task.next
203
209
  if next_task
204
210
  next_task.enqueue!
@@ -258,9 +264,6 @@ module Taskinator
258
264
  end
259
265
 
260
266
  def task_completed(task)
261
- # skip if failed
262
- return if failed?
263
-
264
267
  # deincrement the count of pending concurrent tasks
265
268
  pending = deincr_pending_tasks
266
269
 
@@ -230,12 +230,12 @@ module Taskinator
230
230
  # NNB: if other job types are required, may need to implement how they get invoked here!
231
231
  # FIXME: possible implement using ActiveJob instead, so it doesn't matter how the worker is implemented
232
232
 
233
- if job.instance_of?(Module)
233
+ if job.respond_to?(:perform)
234
234
  # resque
235
- job.perform(args)
235
+ job.perform(*args)
236
236
  else
237
237
  # delayedjob and sidekiq
238
- job.new.perform(args)
238
+ job.new.perform(*args)
239
239
  end
240
240
 
241
241
  # ASSUMPTION: when the job returns, the task is considered to be complete
@@ -1,3 +1,3 @@
1
1
  module Taskinator
2
- VERSION = "0.3.13"
2
+ VERSION = "0.4.1"
3
3
  end
@@ -75,6 +75,22 @@ describe Taskinator::Definition::Builder do
75
75
  expect(Taskinator::Process).to receive(:define_sequential_process_for).with(definition, options).and_call_original
76
76
  subject.sequential(options, &define_block)
77
77
  end
78
+
79
+ it "adds sub-process task" do
80
+ block = Proc.new {|p|
81
+ p.task :task_method
82
+ }
83
+ expect(process.tasks).to be_empty
84
+ subject.sequential(options, &block)
85
+ expect(process.tasks).to_not be_empty
86
+ end
87
+
88
+ it "ignores sub-processes without tasks" do
89
+ allow(block).to receive(:call)
90
+ expect(process.tasks).to be_empty
91
+ subject.sequential(options, &define_block)
92
+ expect(process.tasks).to be_empty
93
+ end
78
94
  end
79
95
 
80
96
  describe "#concurrent" do
@@ -100,6 +116,22 @@ describe Taskinator::Definition::Builder do
100
116
  expect(Taskinator::Process).to receive(:define_concurrent_process_for).with(definition, Taskinator::CompleteOn::First, options).and_call_original
101
117
  subject.concurrent(Taskinator::CompleteOn::First, options, &define_block)
102
118
  end
119
+
120
+ it "adds sub-process task" do
121
+ block = Proc.new {|p|
122
+ p.task :task_method
123
+ }
124
+ expect(process.tasks).to be_empty
125
+ subject.sequential(options, &block)
126
+ expect(process.tasks).to_not be_empty
127
+ end
128
+
129
+ it "ignores sub-processes without tasks" do
130
+ allow(block).to receive(:call)
131
+ expect(process.tasks).to be_empty
132
+ subject.sequential(options, &define_block)
133
+ expect(process.tasks).to be_empty
134
+ end
103
135
  end
104
136
 
105
137
  describe "#for_each" do
@@ -235,6 +267,22 @@ describe Taskinator::Definition::Builder do
235
267
  expect(sub_definition).to receive(:create_sub_process).with(*args, builder_options.merge(options)).and_call_original
236
268
  subject.sub_process(sub_definition, options)
237
269
  end
270
+
271
+ it "adds sub-process task" do
272
+ block = Proc.new {|p|
273
+ p.task :task_method
274
+ }
275
+ expect(process.tasks).to be_empty
276
+ subject.sequential(options, &block)
277
+ expect(process.tasks).to_not be_empty
278
+ end
279
+
280
+ it "ignores sub-processes without tasks" do
281
+ allow(block).to receive(:call)
282
+ expect(process.tasks).to be_empty
283
+ subject.sequential(options, &define_block)
284
+ expect(process.tasks).to be_empty
285
+ end
238
286
  end
239
287
 
240
288
  end
@@ -288,6 +288,23 @@ describe Taskinator::Process do
288
288
  subject.task_completed(task1)
289
289
  end
290
290
 
291
+ it "deincrements the pending task count" do
292
+ tasks.each {|t| subject.tasks << t }
293
+ task1 = tasks[0]
294
+ task2 = tasks[1]
295
+
296
+ allow(task2).to receive(:enqueue!)
297
+
298
+ pending_count = tasks.count
299
+ allow(subject).to receive(:deincr_pending_tasks) { pending_count -= 1 }
300
+
301
+ subject.task_completed(task1)
302
+ expect(pending_count).to eq(tasks.count - 1)
303
+
304
+ subject.task_completed(task2)
305
+ expect(pending_count).to eq(tasks.count - 2)
306
+ end
307
+
291
308
  it "completes if no more tasks" do
292
309
  tasks.each {|t| subject.tasks << t }
293
310
  task2 = tasks[1]
@@ -296,6 +313,30 @@ describe Taskinator::Process do
296
313
 
297
314
  subject.task_completed(task2)
298
315
  end
316
+
317
+ it "completes if failed task gets retried" do
318
+ tasks.each {|t| subject.tasks << t }
319
+ task1 = tasks[0]
320
+ task2 = tasks[1]
321
+
322
+ allow(task2).to receive(:enqueue!)
323
+
324
+ expect(subject).to receive(:fail!).and_call_original
325
+ expect(subject).to receive(:complete!).and_call_original
326
+
327
+ subject.task_completed(task1)
328
+ expect(subject.completed?).to_not be
329
+ expect(subject.failed?).to_not be
330
+
331
+ subject.task_failed(task2, StandardError.new)
332
+ expect(subject.completed?).to_not be
333
+ expect(subject.failed?).to be
334
+
335
+ # "retry" the task
336
+ subject.task_completed(task2)
337
+ expect(subject.completed?).to be
338
+ expect(subject.failed?).to_not be
339
+ end
299
340
  end
300
341
 
301
342
  describe "#tasks_completed?" do
@@ -315,6 +356,28 @@ describe Taskinator::Process do
315
356
  end
316
357
  end
317
358
 
359
+ describe "#task_failed" do
360
+ it "fails when tasks fail" do
361
+ tasks.each {|t| subject.tasks << t }
362
+
363
+ error = StandardError.new
364
+
365
+ expect(subject).to receive(:fail!).with(error)
366
+
367
+ subject.task_failed(tasks.first, error)
368
+ end
369
+
370
+ it "doesn't deincement pending task count" do
371
+ tasks.each {|t| subject.tasks << t }
372
+
373
+ expect(subject).to_not receive(:deincr_pending_tasks)
374
+
375
+ error = StandardError.new
376
+
377
+ subject.task_failed(tasks.first, error)
378
+ end
379
+ end
380
+
318
381
  describe "#accept" do
319
382
  it { expect(subject).to be_a(Taskinator::Persistence) }
320
383
 
@@ -422,7 +485,8 @@ describe Taskinator::Process do
422
485
  process = Taskinator::Process.define_concurrent_process_for(definition, Taskinator::CompleteOn::First)
423
486
  tasks.each {|t| process.tasks << t }
424
487
 
425
- allow(process).to receive(:deincr_pending_tasks) { tasks.count - 1 }
488
+ pending_count = tasks.count
489
+ allow(process).to receive(:deincr_pending_tasks) { pending_count -= 1 }
426
490
 
427
491
  expect(process).to receive(:complete!).once.and_call_original
428
492
 
@@ -450,6 +514,82 @@ describe Taskinator::Process do
450
514
  expect(process.completed?).to be(false) unless pending_count < 1
451
515
  end
452
516
  end
517
+
518
+ it "deincrements the pending task count" do
519
+ tasks.each {|t| subject.tasks << t }
520
+ task1 = tasks[0]
521
+ task2 = tasks[1]
522
+
523
+ pending_count = tasks.count
524
+ allow(subject).to receive(:deincr_pending_tasks) { pending_count -= 1 }
525
+
526
+ subject.task_completed(task1)
527
+ expect(pending_count).to eq(tasks.count - 1)
528
+
529
+ subject.task_completed(task2)
530
+ expect(pending_count).to eq(tasks.count - 2)
531
+ end
532
+
533
+ describe "completes if failed task gets retried" do
534
+ it "after first task succeeds" do
535
+ tasks.each {|t| subject.tasks << t }
536
+ task1 = tasks[0]
537
+ task2 = tasks[1]
538
+
539
+ pending_count = tasks.count
540
+ allow(subject).to receive(:deincr_pending_tasks) { pending_count -= 1 }
541
+ allow(task2).to receive(:enqueue!)
542
+
543
+ expect(subject).to receive(:fail!).and_call_original
544
+ expect(subject).to receive(:complete!).and_call_original
545
+
546
+ # first task succeeds
547
+ subject.task_completed(task1)
548
+ expect(pending_count).to eq(tasks.count - 1)
549
+
550
+ # second task fails
551
+ subject.task_failed(task2, StandardError.new)
552
+
553
+ expect(subject.failed?).to be
554
+ expect(pending_count).to eq(tasks.count - 1)
555
+
556
+ # "retry" the task
557
+ subject.task_completed(task2)
558
+
559
+ expect(pending_count).to eq(tasks.count - 2)
560
+ expect(subject.failed?).to_not be
561
+ expect(subject.completed?).to be
562
+ end
563
+
564
+ it "after first task fails" do
565
+ tasks.each {|t| subject.tasks << t }
566
+ task1 = tasks[0]
567
+ task2 = tasks[1]
568
+
569
+ pending_count = tasks.count
570
+ allow(subject).to receive(:deincr_pending_tasks) { pending_count -= 1 }
571
+ allow(task2).to receive(:enqueue!)
572
+
573
+ expect(subject).to receive(:fail!).and_call_original
574
+ expect(subject).to receive(:complete!).and_call_original
575
+
576
+ # first task fails
577
+ subject.task_failed(task2, StandardError.new)
578
+ expect(subject.failed?).to be
579
+ expect(pending_count).to eq(tasks.count)
580
+
581
+ # second task succeeds
582
+ subject.task_completed(task1)
583
+ expect(pending_count).to eq(tasks.count - 1)
584
+
585
+ # "retry" the task
586
+ subject.task_completed(task2)
587
+
588
+ expect(pending_count).to eq(tasks.count - 2)
589
+ expect(subject.failed?).to_not be
590
+ expect(subject.completed?).to be
591
+ end
592
+ end
453
593
  end
454
594
 
455
595
  describe "#task_failed" do
@@ -462,6 +602,16 @@ describe Taskinator::Process do
462
602
 
463
603
  subject.task_failed(tasks.first, error)
464
604
  end
605
+
606
+ it "doesn't deincement pending task count" do
607
+ tasks.each {|t| subject.tasks << t }
608
+
609
+ expect(subject).to_not receive(:deincr_pending_tasks)
610
+
611
+ error = StandardError.new
612
+
613
+ subject.task_failed(tasks.first, error)
614
+ end
465
615
  end
466
616
 
467
617
  describe "#tasks_completed?" do
@@ -227,7 +227,7 @@ describe Taskinator::Task do
227
227
 
228
228
  method = subject.method
229
229
 
230
- executor.class_eval do
230
+ executor.singleton_class.class_eval do
231
231
  define_method method do |*args|
232
232
  # this method executes in the scope of the executor
233
233
  # store the context in an instance variable
@@ -334,13 +334,23 @@ describe Taskinator::Task do
334
334
  end
335
335
  end
336
336
 
337
- subject { Taskinator::Task.define_job_task(process, TestJob, {:a => 1, :b => 2}) }
337
+ class TestJobClassNoArgs
338
+ def perform
339
+ end
340
+ end
341
+
342
+ module TestJobModuleNoArgs
343
+ def self.perform
344
+ end
345
+ end
346
+
347
+ subject { Taskinator::Task.define_job_task(process, TestJob, [1, {:a => 1, :b => 2}]) }
338
348
 
339
349
  it_should_behave_like "a task", Taskinator::Task::Job
340
350
 
341
351
  describe ".define_job_task" do
342
352
  it "sets the queue to use" do
343
- task = Taskinator::Task.define_job_task(process, TestJob, {:a => 1, :b => 2}, :queue => :foo)
353
+ task = Taskinator::Task.define_job_task(process, TestJob, [1, {:a => 1, :b => 2}], :queue => :foo)
344
354
  expect(task.queue).to eq(:foo)
345
355
  end
346
356
  end
@@ -367,23 +377,37 @@ describe Taskinator::Task do
367
377
 
368
378
  describe "#start" do
369
379
  it {
370
- task = Taskinator::Task.define_job_task(process, TestJobClass, {:a => 1, :b => 2})
380
+ task = Taskinator::Task.define_job_task(process, TestJobClass, [1, {:a => 1, :b => 2}])
381
+ expect(process).to receive(:task_completed).with(task)
382
+ expect_any_instance_of(TestJobClass).to receive(:perform).with(1, {:a => 1, :b => 2})
383
+ task.start!
384
+ }
385
+
386
+ it {
387
+ task = Taskinator::Task.define_job_task(process, TestJobModule, [2, {:a => 1, :b => 2}])
388
+ expect(process).to receive(:task_completed).with(task)
389
+ expect(TestJobModule).to receive(:perform).with(2, {:a => 1, :b => 2})
390
+ task.start!
391
+ }
392
+
393
+ it {
394
+ task = Taskinator::Task.define_job_task(process, TestJobClassNoArgs, nil)
371
395
  expect(process).to receive(:task_completed).with(task)
372
- expect_any_instance_of(TestJobClass).to receive(:perform).with({:a => 1, :b => 2})
396
+ expect_any_instance_of(TestJobClassNoArgs).to receive(:perform).and_call_original
373
397
  task.start!
374
398
  }
375
399
 
376
400
  it {
377
- task = Taskinator::Task.define_job_task(process, TestJobModule, {:a => 1, :b => 2})
401
+ task = Taskinator::Task.define_job_task(process, TestJobModuleNoArgs, nil)
378
402
  expect(process).to receive(:task_completed).with(task)
379
- expect(TestJobModule).to receive(:perform).with({:a => 1, :b => 2})
403
+ expect(TestJobModuleNoArgs).to receive(:perform).and_call_original
380
404
  task.start!
381
405
  }
382
406
 
383
407
  it "is instrumented" do
384
408
  allow(process).to receive(:task_completed).with(subject)
385
409
 
386
- allow(TestJob).to receive(:perform).with({:a => 1, :b => 2})
410
+ allow(TestJob).to receive(:perform).with(1, {:a => 1, :b => 2})
387
411
 
388
412
  instrumentation_block = SpecSupport::Block.new
389
413
 
data/taskinator.gemspec CHANGED
@@ -23,10 +23,11 @@ Gem::Specification.new do |spec|
23
23
  # core
24
24
  spec.add_dependency 'redis' , '>= 3.2.1'
25
25
  spec.add_dependency 'redis-namespace' , '>= 1.5.2'
26
- spec.add_dependency 'redis-semaphore' , '>= 0.2.4'
27
26
  spec.add_dependency 'connection_pool' , '>= 2.2.0'
28
27
  spec.add_dependency 'json' , '>= 1.8.2'
29
28
  spec.add_dependency 'builder' , '>= 3.2.2'
30
29
  spec.add_dependency 'globalid' , '~> 0.3'
31
- spec.add_dependency 'statsd-ruby' , '~> 1.2.0'
30
+ spec.add_dependency 'statsd-ruby' , '~> 1.4.0'
31
+ spec.add_dependency 'thwait' , '~> 0.2'
32
+
32
33
  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.13
4
+ version: 0.4.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: 2017-09-25 00:00:00.000000000 Z
11
+ date: 2021-03-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redis
@@ -38,20 +38,6 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: 1.5.2
41
- - !ruby/object:Gem::Dependency
42
- name: redis-semaphore
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: 0.2.4
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: 0.2.4
55
41
  - !ruby/object:Gem::Dependency
56
42
  name: connection_pool
57
43
  requirement: !ruby/object:Gem::Requirement
@@ -114,14 +100,28 @@ dependencies:
114
100
  requirements:
115
101
  - - "~>"
116
102
  - !ruby/object:Gem::Version
117
- version: 1.2.0
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
+ - !ruby/object:Gem::Dependency
112
+ name: thwait
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '0.2'
118
118
  type: :runtime
119
119
  prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
122
  - - "~>"
123
123
  - !ruby/object:Gem::Version
124
- version: 1.2.0
124
+ version: '0.2'
125
125
  description: Simple process orchestration
126
126
  email:
127
127
  - virtualstaticvoid@gmail.com
@@ -232,8 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
232
232
  - !ruby/object:Gem::Version
233
233
  version: '0'
234
234
  requirements: []
235
- rubyforge_project:
236
- rubygems_version: 2.6.13
235
+ rubygems_version: 3.1.4
237
236
  signing_key:
238
237
  specification_version: 4
239
238
  summary: A simple orchestration library for running complex processes or workflows