taskinator 0.3.16 → 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +4 -3
- data/CHANGELOG.md +63 -0
- data/Gemfile.lock +24 -27
- data/README.md +169 -68
- data/lib/taskinator/api.rb +14 -5
- data/lib/taskinator/definition/builder.rb +16 -7
- data/lib/taskinator/persistence.rb +3 -0
- data/lib/taskinator/task.rb +3 -3
- data/lib/taskinator/version.rb +1 -1
- data/lib/taskinator.rb +0 -1
- data/spec/support/test_flows.rb +38 -0
- data/spec/taskinator/api_spec.rb +16 -0
- data/spec/taskinator/definition/builder_spec.rb +48 -0
- data/spec/taskinator/persistence_spec.rb +21 -17
- data/spec/taskinator/task_spec.rb +32 -8
- data/taskinator.gemspec +0 -1
- metadata +3 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f1a746ba56e5b6a2f57736990eef9e83eafe8701fcbebaaddea258a010f3ee14
|
4
|
+
data.tar.gz: 5e17bc8e6a61b93d6df0691dc463fb178aca72957ab79eccaea7b9cd6c1af642
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f8cd10cc4c8f5d45d5cc781b379927bae7c0ed5c280e2470450a818a6a3132af75bc11a4c16eef6312056f984e912f3fa758bd45e493988cee37fd01ad92d87
|
7
|
+
data.tar.gz: f8b330d3f6e6766f6ddbc33becd3bb6d16826a021dd0ba16c3b2ec8308cdfe31caad40621fcbc542d6662c8aababaa946e369d251c82b24a213f955a942192a3
|
data/.travis.yml
CHANGED
@@ -1,15 +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
|
7
|
+
- redis
|
7
8
|
|
8
9
|
rvm:
|
9
|
-
- 2.4.10
|
10
10
|
- 2.5.8
|
11
11
|
- 2.6.6
|
12
12
|
- 2.7.2
|
13
|
+
- 3.0.0
|
13
14
|
|
14
15
|
script: 'bundle exec rake spec'
|
15
16
|
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,66 @@
|
|
1
|
+
v0.4.3 - 14 Jan 2022
|
2
|
+
---
|
3
|
+
Add `#find_process` and `#find_task` methods to `Taskinator::Api`.
|
4
|
+
Bug fix to API when enumerating processes.
|
5
|
+
Updated dependencies.
|
6
|
+
|
7
|
+
v0.4.2 - 16 Mar 2021
|
8
|
+
---
|
9
|
+
Bug fix for process/task keys not expired upon completion.
|
10
|
+
|
11
|
+
v0.4.1 - 15 Mar 2021
|
12
|
+
---
|
13
|
+
Optimisation to exclude sub-processes which don't have any tasks.
|
14
|
+
Preparations for upgrade to Ruby 3 and ActiveSupport 6
|
15
|
+
|
16
|
+
v0.4.0 - 4 Mar 2021
|
17
|
+
---
|
18
|
+
Bug fix `job` tasks which have no arguments to the `perform` method.
|
19
|
+
Added support for having `perform` method as a class method.
|
20
|
+
|
21
|
+
v0.3.16 - 17 Feb 2021
|
22
|
+
---
|
23
|
+
Bug fix to deincrement pending counts for sequential tasks.
|
24
|
+
Bug fix to allow concurrent tasks to be retried (via Resque) and to complete processes.
|
25
|
+
|
26
|
+
v0.3.15 - 22 Nov 2018
|
27
|
+
---
|
28
|
+
Updated dependencies.
|
29
|
+
|
30
|
+
v0.3.14 - 13 Jul 2018
|
31
|
+
---
|
32
|
+
Updated dependencies.
|
33
|
+
Removed gemnasium.
|
34
|
+
|
35
|
+
v0.3.13 - 23 Sep 2017
|
36
|
+
---
|
37
|
+
Updated dependencies.
|
38
|
+
|
39
|
+
v0.3.12 - 23 Sep 2017
|
40
|
+
---
|
41
|
+
Spec fixes.
|
42
|
+
Updated dependencies.
|
43
|
+
|
44
|
+
v0.3.11 - 1 Nov 2016
|
45
|
+
---
|
46
|
+
Removed `redis-semaphore` gem and use INCRBY to track pending concurrent tasks instead.
|
47
|
+
Added instrumentation using statsd.
|
48
|
+
Bug fixes to key expiry logic.
|
49
|
+
Refactored process and task state transistions.
|
50
|
+
|
51
|
+
v0.3.10 - 1 Nov 2016
|
52
|
+
---
|
53
|
+
Added support for serializing to XML.
|
54
|
+
Improvements to process and task states.
|
55
|
+
|
56
|
+
v0.3.9 - 12 Sep 2016
|
57
|
+
---
|
58
|
+
Added benchmark for redis-mutex.
|
59
|
+
|
60
|
+
v0.3.7 - 18 Aug 2016
|
61
|
+
---
|
62
|
+
Bug fix to `option?` method.
|
63
|
+
|
1
64
|
v0.3.6 - 11 Nov 2015
|
2
65
|
---
|
3
66
|
Added visitor for performing clean up of completed processes/tasks.
|
data/Gemfile.lock
CHANGED
@@ -1,21 +1,20 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
taskinator (0.3
|
4
|
+
taskinator (0.4.3)
|
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
11
|
statsd-ruby (~> 1.4.0)
|
13
12
|
thwait (~> 0.2)
|
14
13
|
|
15
14
|
GEM
|
16
15
|
remote: https://rubygems.org/
|
17
16
|
specs:
|
18
|
-
activesupport (5.2.
|
17
|
+
activesupport (5.2.6)
|
19
18
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
20
19
|
i18n (>= 0.7, < 2)
|
21
20
|
minitest (~> 5.1)
|
@@ -23,8 +22,8 @@ GEM
|
|
23
22
|
builder (3.2.4)
|
24
23
|
byebug (11.1.3)
|
25
24
|
coderay (1.1.3)
|
26
|
-
concurrent-ruby (1.1.
|
27
|
-
connection_pool (2.2.
|
25
|
+
concurrent-ruby (1.1.9)
|
26
|
+
connection_pool (2.2.5)
|
28
27
|
coveralls (0.8.23)
|
29
28
|
json (>= 1.8, < 3)
|
30
29
|
simplecov (~> 0.16.1)
|
@@ -33,38 +32,36 @@ GEM
|
|
33
32
|
tins (~> 1.6)
|
34
33
|
delayed_job (4.1.9)
|
35
34
|
activesupport (>= 3.0, < 6.2)
|
36
|
-
diff-lcs (1.
|
37
|
-
docile (1.
|
35
|
+
diff-lcs (1.5.0)
|
36
|
+
docile (1.4.0)
|
38
37
|
e2mmap (0.1.0)
|
39
38
|
fakeredis (0.7.0)
|
40
39
|
redis (>= 3.2, < 5.0)
|
41
|
-
globalid (0.
|
42
|
-
activesupport (>=
|
43
|
-
i18n (1.8.
|
40
|
+
globalid (0.6.0)
|
41
|
+
activesupport (>= 5.0)
|
42
|
+
i18n (1.8.11)
|
44
43
|
concurrent-ruby (~> 1.0)
|
45
|
-
json (2.
|
44
|
+
json (2.6.1)
|
46
45
|
method_source (1.0.0)
|
47
|
-
minitest (5.
|
48
|
-
mono_logger (1.1.
|
46
|
+
minitest (5.15.0)
|
47
|
+
mono_logger (1.1.1)
|
49
48
|
multi_json (1.15.0)
|
50
49
|
mustermann (1.1.1)
|
51
50
|
ruby2_keywords (~> 0.0.1)
|
52
|
-
pry (0.
|
51
|
+
pry (0.14.1)
|
53
52
|
coderay (~> 1.1)
|
54
53
|
method_source (~> 1.0)
|
55
|
-
pry-byebug (3.
|
54
|
+
pry-byebug (3.8.0)
|
56
55
|
byebug (~> 11.0)
|
57
|
-
pry (~> 0.
|
56
|
+
pry (~> 0.10)
|
58
57
|
rack (2.2.3)
|
59
58
|
rack-protection (2.1.0)
|
60
59
|
rack
|
61
|
-
rake (13.0.
|
62
|
-
redis (4.
|
60
|
+
rake (13.0.6)
|
61
|
+
redis (4.5.1)
|
63
62
|
redis-namespace (1.8.1)
|
64
63
|
redis (>= 3.0.4)
|
65
|
-
|
66
|
-
redis
|
67
|
-
resque (2.0.0)
|
64
|
+
resque (2.2.0)
|
68
65
|
mono_logger (~> 1.0)
|
69
66
|
multi_json (~> 1.0)
|
70
67
|
redis-namespace (~> 1.6)
|
@@ -90,9 +87,9 @@ GEM
|
|
90
87
|
rspec-sidekiq (3.1.0)
|
91
88
|
rspec-core (~> 3.0, >= 3.0.0)
|
92
89
|
sidekiq (>= 2.4.0)
|
93
|
-
rspec-support (3.10.
|
94
|
-
ruby2_keywords (0.0.
|
95
|
-
sidekiq (6.1
|
90
|
+
rspec-support (3.10.3)
|
91
|
+
ruby2_keywords (0.0.5)
|
92
|
+
sidekiq (6.3.1)
|
96
93
|
connection_pool (>= 2.2.2)
|
97
94
|
rack (~> 2.0)
|
98
95
|
redis (>= 4.2.0)
|
@@ -110,12 +107,12 @@ GEM
|
|
110
107
|
sync (0.5.0)
|
111
108
|
term-ansicolor (1.7.1)
|
112
109
|
tins (~> 1.0)
|
113
|
-
thor (1.1
|
110
|
+
thor (1.2.1)
|
114
111
|
thread_safe (0.3.6)
|
115
112
|
thwait (0.2.0)
|
116
113
|
e2mmap
|
117
114
|
tilt (2.0.10)
|
118
|
-
tins (1.
|
115
|
+
tins (1.31.0)
|
119
116
|
sync
|
120
117
|
tzinfo (1.2.9)
|
121
118
|
thread_safe (~> 0.1)
|
@@ -142,4 +139,4 @@ DEPENDENCIES
|
|
142
139
|
taskinator!
|
143
140
|
|
144
141
|
BUNDLED WITH
|
145
|
-
2.2.
|
142
|
+
2.2.14
|
data/README.md
CHANGED
@@ -5,21 +5,25 @@
|
|
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
7
|
|
8
|
-
A simple orchestration library for running complex processes or workflows in Ruby.
|
9
|
-
|
10
|
-
for
|
8
|
+
A simple orchestration library for running complex processes or workflows in Ruby.
|
9
|
+
Processes are defined using a simple DSL, where the sequences and tasks are defined.
|
10
|
+
Processes can then be queued for execution. Sequences can be synchronous or asynchronous,
|
11
|
+
and the overall process can be monitored for completion or failure.
|
11
12
|
|
12
|
-
Processes and tasks are executed by background workers and you can use any one of the
|
13
|
+
Processes and tasks are executed by background workers and you can use any one of the
|
14
|
+
following gems:
|
13
15
|
|
14
16
|
* [resque](https://github.com/resque/resque)
|
15
17
|
* [sidekiq](https://github.com/mperham/sidekiq)
|
16
18
|
* [delayed_job](https://github.com/collectiveidea/delayed_job)
|
17
19
|
|
18
|
-
The configuration and state of each process and their respective tasks is stored using
|
20
|
+
The configuration and state of each process and their respective tasks is stored using
|
21
|
+
Redis key/values.
|
19
22
|
|
20
23
|
## Requirements
|
21
24
|
|
22
|
-
The latest MRI (2.1, 2.0) version. Other versions/VMs are untested but might work fine.
|
25
|
+
The latest MRI (2.1, 2.0) version. Other versions/VMs are untested but might work fine.
|
26
|
+
MRI 1.9 is not supported.
|
23
27
|
|
24
28
|
Redis 2.4 or greater is required.
|
25
29
|
|
@@ -68,7 +72,8 @@ module MyProcess
|
|
68
72
|
end
|
69
73
|
```
|
70
74
|
|
71
|
-
The `define_process` method optionally takes the list of expected arguments which are used
|
75
|
+
The `define_process` method optionally takes the list of expected arguments which are used
|
76
|
+
to validate the arguments supplied when creating a new process.
|
72
77
|
These should be specified with symbols.
|
73
78
|
|
74
79
|
```ruby
|
@@ -87,7 +92,8 @@ process = MyProcess.create_process Date.today, :option_1 => true
|
|
87
92
|
|
88
93
|
_NOTE:_ The current implementation performs a naive check on the count of arguments.
|
89
94
|
|
90
|
-
Next, specify the tasks with their corresponding implementation methods, that make up the
|
95
|
+
Next, specify the tasks with their corresponding implementation methods, that make up the
|
96
|
+
process, using the `task` method and providing the `method` to execute for the task.
|
91
97
|
|
92
98
|
```ruby
|
93
99
|
module MyProcess
|
@@ -108,7 +114,8 @@ module MyProcess
|
|
108
114
|
end
|
109
115
|
```
|
110
116
|
|
111
|
-
More complex processes may define sequential or concurrent steps, using the `sequential`
|
117
|
+
More complex processes may define sequential or concurrent steps, using the `sequential`
|
118
|
+
and `concurrent` methods respectively.
|
112
119
|
|
113
120
|
```ruby
|
114
121
|
module MyProcess
|
@@ -141,10 +148,15 @@ module MyProcess
|
|
141
148
|
end
|
142
149
|
```
|
143
150
|
|
144
|
-
It is likely that you already have worker classes for one of the queueing libraries,
|
151
|
+
It is likely that you already have worker classes for one of the queueing libraries,
|
152
|
+
such as resque or delayed_job, and wish to reuse them for executing them in the sequence
|
153
|
+
defined by the process definition.
|
145
154
|
|
146
|
-
Define a `job` step, providing the class of the worker, and then taskinator will execute
|
147
|
-
|
155
|
+
Define a `job` step, providing the class of the worker, and then taskinator will execute
|
156
|
+
that worker as part of the process definition.
|
157
|
+
|
158
|
+
The `job` step will be queued and executed on same queue as configured by `delayed_job`, or
|
159
|
+
that of the worker for `resque` and `sidekiq`.
|
148
160
|
|
149
161
|
```ruby
|
150
162
|
# E.g. A resque worker
|
@@ -168,8 +180,11 @@ module MyProcess
|
|
168
180
|
end
|
169
181
|
```
|
170
182
|
|
171
|
-
You can also define data driven tasks using the `for_each` method, which takes an iterator method
|
172
|
-
|
183
|
+
You can also define data driven tasks using the `for_each` method, which takes an iterator method
|
184
|
+
name as an argument.
|
185
|
+
|
186
|
+
The iterator method yields the parameters necessary for the task or job. Notice that the task
|
187
|
+
method takes a parameter in this case, which will be the return values provided by the iterator.
|
173
188
|
|
174
189
|
```ruby
|
175
190
|
module MyProcess
|
@@ -192,8 +207,9 @@ module MyProcess
|
|
192
207
|
end
|
193
208
|
```
|
194
209
|
|
195
|
-
It is possible to branch the process logic based on the options hash passed in when creating
|
196
|
-
The `options?` method takes the options key as an argument and calls the supplied
|
210
|
+
It is possible to branch the process logic based on the options hash passed in when creating
|
211
|
+
a process. The `options?` method takes the options key as an argument and calls the supplied
|
212
|
+
block if the option is present and it's value is _truthy_.
|
197
213
|
|
198
214
|
```ruby
|
199
215
|
module MyProcess
|
@@ -227,8 +243,11 @@ process2 = MyProcess.create_process
|
|
227
243
|
process2.tasks.count #=> 1
|
228
244
|
```
|
229
245
|
|
230
|
-
In addition, it is possible to transform the arguments used by a task or job, by including
|
231
|
-
|
246
|
+
In addition, it is possible to transform the arguments used by a task or job, by including
|
247
|
+
a `transform` step in the definition.
|
248
|
+
|
249
|
+
Similarly for the `for_each` method, `transform` takes a method name as an argument.
|
250
|
+
The transformer method must yield the new arguments as required.
|
232
251
|
|
233
252
|
```ruby
|
234
253
|
module MyProcess
|
@@ -273,7 +292,8 @@ module MyProcess
|
|
273
292
|
end
|
274
293
|
```
|
275
294
|
|
276
|
-
Any combination or nesting of `task`, `sequential`, `concurrent` and `for_each` steps are
|
295
|
+
Any combination or nesting of `task`, `sequential`, `concurrent` and `for_each` steps are
|
296
|
+
possible. E.g.
|
277
297
|
|
278
298
|
```ruby
|
279
299
|
module MyProcess
|
@@ -306,13 +326,17 @@ module MyProcess
|
|
306
326
|
end
|
307
327
|
```
|
308
328
|
|
309
|
-
In this example, the `work_step_begin` is executed, followed by the `work_step_all_at_once`
|
310
|
-
the sub process `MySubProcess` is created and
|
329
|
+
In this example, the `work_step_begin` is executed, followed by the `work_step_all_at_once`
|
330
|
+
steps which are executed concurrently, then the sub process `MySubProcess` is created and
|
331
|
+
executed, followed by the `work_step_one_by_one` tasks which are executed sequentially and
|
311
332
|
finally the `work_step_end` is executed.
|
312
333
|
|
313
|
-
It is also possible to embed conditional logic within the process definition stages in
|
314
|
-
|
315
|
-
|
334
|
+
It is also possible to embed conditional logic within the process definition stages in
|
335
|
+
order to produce steps based on the required logic.
|
336
|
+
|
337
|
+
All builder methods are available within the scope of the `define_process` block. These
|
338
|
+
methods include `args` and `options` which are passed into the `create_process` method
|
339
|
+
of the definition.
|
316
340
|
|
317
341
|
E.g.
|
318
342
|
|
@@ -332,7 +356,8 @@ module MyProcess
|
|
332
356
|
end
|
333
357
|
|
334
358
|
# when creating this proces, you supply to option when calling `create_process`
|
335
|
-
# in this example, 'args' will be an array [1,2,3]
|
359
|
+
# in this example, 'args' will be an array [1,2,3]
|
360
|
+
# and options will be a Hash {:send_notification => true}
|
336
361
|
MyProcess.create_process(1, 2, 3, :send_notification => true)
|
337
362
|
|
338
363
|
```
|
@@ -364,8 +389,12 @@ To best understand how arguments are handled, you need to break it down into 3 p
|
|
364
389
|
* Creation and
|
365
390
|
* Execution
|
366
391
|
|
367
|
-
Firstly, a process definition is declarative in that the `define_process` and a mix of
|
368
|
-
|
392
|
+
Firstly, a process definition is declarative in that the `define_process` and a mix of
|
393
|
+
`sequential`, `concurrent`, `for_each`, `task` and `job` directives provide the way to
|
394
|
+
specify the sequencing of the steps for the process.
|
395
|
+
|
396
|
+
Taskinator will interprete this definition and execute each step in the desired sequence
|
397
|
+
or concurrency.
|
369
398
|
|
370
399
|
Consider the following process definition:
|
371
400
|
|
@@ -411,11 +440,17 @@ end
|
|
411
440
|
|
412
441
|
There are three tasks; namely `:work_step_1`, `:work_step_2` and `:work_step_3`.
|
413
442
|
|
414
|
-
The third task, `:work_step_3`, is built up using the `for_each` iterator, which means that
|
443
|
+
The third task, `:work_step_3`, is built up using the `for_each` iterator, which means that
|
444
|
+
the number of `:work_step_3` tasks will depend on how many times the `additional_step`
|
445
|
+
iterator method yields to the definition.
|
446
|
+
|
447
|
+
This brings us to the creation part. When `create_process` is called on the given module,
|
448
|
+
you provide arguments to it, which will get passed onto the respective `task` and
|
449
|
+
`for_each` iterator methods.
|
415
450
|
|
416
|
-
|
451
|
+
So, considering the `MySimpleProcess` module shown above, `work_step_1`, `work_step_2`
|
452
|
+
and `work_step_3` methods each expect arguments.
|
417
453
|
|
418
|
-
So, considering the `MySimpleProcess` module shown above, `work_step_1`, `work_step_2` and `work_step_3` methods each expect arguments.
|
419
454
|
These will ultimately come from the arguments passed into the `create_process` method.
|
420
455
|
|
421
456
|
E.g.
|
@@ -438,7 +473,8 @@ process = MySimpleProcess.create_process(options)
|
|
438
473
|
|
439
474
|
```
|
440
475
|
|
441
|
-
To best understand how the process is created, consider the following "procedural" code
|
476
|
+
To best understand how the process is created, consider the following "procedural" code
|
477
|
+
for how it could work.
|
442
478
|
|
443
479
|
```ruby
|
444
480
|
# A process, which maps the target and a list of steps
|
@@ -535,11 +571,17 @@ process.execute
|
|
535
571
|
|
536
572
|
```
|
537
573
|
|
538
|
-
In reality, each task is executed by a worker process, possibly on another host, so the
|
574
|
+
In reality, each task is executed by a worker process, possibly on another host, so the
|
575
|
+
execution process isn't as simple, but this example should help you to understand
|
576
|
+
conceptually how the process is executed, and how the arguments are propagated through.
|
539
577
|
|
540
578
|
### Monitoring
|
541
579
|
|
542
|
-
|
580
|
+
NOTE: This aspect of the library is still a work in progress.
|
581
|
+
|
582
|
+
#### Processes
|
583
|
+
|
584
|
+
To monitor the state of the processes, use the `Taskinator::Api::Processes` class.
|
543
585
|
|
544
586
|
```ruby
|
545
587
|
processes = Taskinator::Api::Processes.new
|
@@ -549,11 +591,57 @@ processes.each do |process|
|
|
549
591
|
end
|
550
592
|
```
|
551
593
|
|
594
|
+
#### Debugging
|
595
|
+
|
596
|
+
To aid debugging specific processes and tasks, where the process or task identifier is
|
597
|
+
known, it is possible to retrieve the specific task or process using `Taskinator::Api`.
|
598
|
+
|
599
|
+
To retrieve a specific process, given the process identifier:
|
600
|
+
|
601
|
+
```ruby
|
602
|
+
process_id = "SUPPLY-PROCESS-IDENTIFIER"
|
603
|
+
process = Taskinator::Api.find_process(process_id)
|
604
|
+
|
605
|
+
puts process.inspect
|
606
|
+
puts process.current_state
|
607
|
+
puts process.tasks
|
608
|
+
# etc...
|
609
|
+
```
|
610
|
+
|
611
|
+
The type of process may be one of the following:
|
612
|
+
|
613
|
+
* `Taskinator::Process::Sequential`
|
614
|
+
* `Taskinator::Process::Concurrent`
|
615
|
+
|
616
|
+
Then, to retrieve a specific task, given the task identifier:
|
617
|
+
|
618
|
+
```ruby
|
619
|
+
task_id = "SUPPLY-TASK-IDENTIFIER"
|
620
|
+
task = Taskinator::Api.find_task(task_id)
|
621
|
+
|
622
|
+
puts task.inspect
|
623
|
+
puts task.class
|
624
|
+
puts task.args # for Step and Job types
|
625
|
+
puts task.sub_process.tasks # for SubProcess type
|
626
|
+
# etc...
|
627
|
+
```
|
628
|
+
|
629
|
+
Depending on the type of task, different attributes will be available for inspection.
|
630
|
+
|
631
|
+
The types include:
|
632
|
+
|
633
|
+
* `Taskinator::Task::Step`
|
634
|
+
* `Taskinator::Task::Job`
|
635
|
+
* `Taskinator::Task::SubProcess`
|
636
|
+
|
552
637
|
## Configuration
|
553
638
|
|
554
639
|
### Redis
|
555
640
|
|
556
|
-
By default Taskinator assumes Redis is located at `localhost:6397`. This is fine for development,
|
641
|
+
By default Taskinator assumes Redis is located at `localhost:6397`. This is fine for development,
|
642
|
+
but for many production environments you will need to point to an external Redis server.
|
643
|
+
You may also what to use a namespace for the Redis keys.
|
644
|
+
|
557
645
|
_NOTE:_ The configuration hash _must_ have symbolized keys.
|
558
646
|
|
559
647
|
```ruby
|
@@ -568,15 +656,19 @@ end
|
|
568
656
|
Or, alternatively, via an `ENV` variable
|
569
657
|
|
570
658
|
Set the `REDIS_PROVIDER` environment variable to the Redis server url.
|
571
|
-
E.g. On Heroku, with RedisGreen: set REDIS_PROVIDER=REDISGREEN_URL and Taskinator will use the
|
659
|
+
E.g. On Heroku, with RedisGreen: set REDIS_PROVIDER=REDISGREEN_URL and Taskinator will use the
|
660
|
+
value of the `REDISGREEN_URL` environment variable when connecting to Redis.
|
572
661
|
|
573
662
|
You may also use the generic `REDIS_URL` which may be set to your own private Redis server.
|
574
663
|
|
575
|
-
The Redis configuration leverages the same setup as `sidekiq`. For advanced options, checkout the
|
664
|
+
The Redis configuration leverages the same setup as `sidekiq`. For advanced options, checkout the
|
665
|
+
[Sidekiq Advanced Options](https://github.com/mperham/sidekiq/wiki/Advanced-Options#complete-control)
|
666
|
+
wiki page for more information.
|
576
667
|
|
577
668
|
### Queues
|
578
669
|
|
579
|
-
By default the queue names for process and task workers is `default`, however, you can specify
|
670
|
+
By default the queue names for process and task workers is `default`, however, you can specify
|
671
|
+
the queue names as follows:
|
580
672
|
|
581
673
|
```ruby
|
582
674
|
Taskinator.configure do |config|
|
@@ -589,7 +681,8 @@ end
|
|
589
681
|
|
590
682
|
### Instrumentation
|
591
683
|
|
592
|
-
It is possible to instrument processes, tasks and jobs by providing an instrumeter such
|
684
|
+
It is possible to instrument processes, tasks and jobs by providing an instrumeter such
|
685
|
+
as `ActiveSupport::Notifications`.
|
593
686
|
|
594
687
|
```ruby
|
595
688
|
Taskinator.configure do |config|
|
@@ -607,40 +700,41 @@ end
|
|
607
700
|
|
608
701
|
The following instrumentation events are issued:
|
609
702
|
|
610
|
-
| Event
|
611
|
-
|
612
|
-
| `taskinator.process.created`
|
613
|
-
| `taskinator.process.saved`
|
614
|
-
| `taskinator.process.enqueued`
|
615
|
-
| `taskinator.process.processing`
|
616
|
-
| `taskinator.process.paused`
|
617
|
-
| `taskinator.process.resumed`
|
618
|
-
| `taskinator.process.completed`
|
619
|
-
| `taskinator.process.cancelled`
|
620
|
-
| `taskinator.process.failed`
|
621
|
-
| `taskinator.task.enqueued`
|
622
|
-
| `taskinator.task.processing`
|
623
|
-
| `taskinator.task.completed`
|
624
|
-
| `taskinator.task.cancelled`
|
625
|
-
| `taskinator.task.failed`
|
703
|
+
| Event | When |
|
704
|
+
|---------------------------------|----------------------------------------------------------|
|
705
|
+
| `taskinator.process.created` | After a root process gets created |
|
706
|
+
| `taskinator.process.saved` | After a root process has been persisted to Redis |
|
707
|
+
| `taskinator.process.enqueued` | After a process or subprocess is enqueued for processing |
|
708
|
+
| `taskinator.process.processing` | When a process or subprocess is processing |
|
709
|
+
| `taskinator.process.paused` | When a process or subprocess is paused |
|
710
|
+
| `taskinator.process.resumed` | When a process or subprocess is resumed |
|
711
|
+
| `taskinator.process.completed` | After a process or subprocess has completed processing |
|
712
|
+
| `taskinator.process.cancelled` | After a process or subprocess has been cancelled |
|
713
|
+
| `taskinator.process.failed` | After a process or subprocess has failed |
|
714
|
+
| `taskinator.task.enqueued` | After a task has been enqueued |
|
715
|
+
| `taskinator.task.processing` | When a task is processing |
|
716
|
+
| `taskinator.task.completed` | After a task has completed |
|
717
|
+
| `taskinator.task.cancelled` | After a task has been cancelled |
|
718
|
+
| `taskinator.task.failed` | After a task has failed |
|
626
719
|
|
627
720
|
For all events, the data included contains the following information:
|
628
721
|
|
629
|
-
| Key
|
630
|
-
|
631
|
-
| `:type`
|
632
|
-
| `:process_uuid`
|
633
|
-
| `:process_options`
|
634
|
-
| `:uuid`
|
635
|
-
| `:options`
|
636
|
-
| `:state`
|
637
|
-
| `:percentage_completed`
|
638
|
-
| `:percentage_failed`
|
639
|
-
| `:percentage_cancelled`
|
722
|
+
| Key | Value |
|
723
|
+
|---------------------------------|----------------------------------------------------------|
|
724
|
+
| `:type` | The type name of the component reporting the event |
|
725
|
+
| `:process_uuid` | The UUID of the root process |
|
726
|
+
| `:process_options` | Options hash of the root process |
|
727
|
+
| `:uuid` | The UUID of the respective task, job or sub process |
|
728
|
+
| `:options` | Options hash of the component |
|
729
|
+
| `:state` | State of the component |
|
730
|
+
| `:percentage_completed` | The percentage of completed tasks |
|
731
|
+
| `:percentage_failed` | The percentage of failed tasks |
|
732
|
+
| `:percentage_cancelled` | The percentage of cancelled tasks |
|
640
733
|
|
641
734
|
## Notes
|
642
735
|
|
643
|
-
The persistence logic is decoupled from the implementation, so it is possible to implement
|
736
|
+
The persistence logic is decoupled from the implementation, so it is possible to implement
|
737
|
+
another backing store if required.
|
644
738
|
|
645
739
|
## Contributing
|
646
740
|
|
@@ -651,12 +745,19 @@ The persistence logic is decoupled from the implementation, so it is possible to
|
|
651
745
|
5. Create new Pull Request
|
652
746
|
|
653
747
|
## License
|
748
|
+
|
654
749
|
MIT Copyright (c) 2014 Chris Stefano
|
655
750
|
|
656
751
|
Portions of code are from the Sidekiq project, Copyright (c) Contributed Systems LLC.
|
657
752
|
|
658
753
|
## Inspiration
|
659
754
|
|
660
|
-
Inspired by the [sidekiq](https://github.com/mperham/sidekiq) and
|
755
|
+
Inspired by the [sidekiq](https://github.com/mperham/sidekiq) and
|
756
|
+
[workflow](https://github.com/geekq/workflow) gems.
|
757
|
+
|
758
|
+
For other workflow solutions, checkout [Stonepath](https://github.com/bokmann/stonepath),
|
759
|
+
the now deprecated [ruote](https://github.com/jmettraux/ruote) gem and
|
760
|
+
[workflow](https://github.com/geekq/workflow).
|
661
761
|
|
662
|
-
|
762
|
+
Alternatively, for a robust enterprise ready solution checkout the
|
763
|
+
[AWS Flow Framework for Ruby](http://docs.aws.amazon.com/amazonswf/latest/awsrbflowguide/welcome.html).
|
data/lib/taskinator/api.rb
CHANGED
@@ -13,12 +13,13 @@ module Taskinator
|
|
13
13
|
def each(&block)
|
14
14
|
return to_enum(__method__) unless block_given?
|
15
15
|
|
16
|
+
identifiers = Taskinator.redis do |conn|
|
17
|
+
conn.smembers(@processes_list_key)
|
18
|
+
end
|
19
|
+
|
16
20
|
instance_cache = {}
|
17
|
-
|
18
|
-
|
19
|
-
uuids.each do |uuid|
|
20
|
-
yield Process.fetch(uuid, instance_cache)
|
21
|
-
end
|
21
|
+
identifiers.each do |identifier|
|
22
|
+
yield Process.fetch(identifier, instance_cache)
|
22
23
|
end
|
23
24
|
end
|
24
25
|
|
@@ -28,5 +29,13 @@ module Taskinator
|
|
28
29
|
end
|
29
30
|
end
|
30
31
|
end
|
32
|
+
|
33
|
+
def self.find_process(identifier)
|
34
|
+
Process.fetch(identifier)
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.find_task(identifier)
|
38
|
+
Task.fetch(identifier)
|
39
|
+
end
|
31
40
|
end
|
32
41
|
end
|
@@ -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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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)
|
@@ -604,6 +604,9 @@ module Taskinator
|
|
604
604
|
end
|
605
605
|
|
606
606
|
def visit_tasks(tasks)
|
607
|
+
@conn.expire "#{@key}:tasks", expire_in
|
608
|
+
@conn.expire "#{@key}.count", expire_in
|
609
|
+
@conn.expire "#{@key}.pending", expire_in
|
607
610
|
tasks.each do |task|
|
608
611
|
RedisCleanupVisitor.new(@conn, task, expire_in).visit
|
609
612
|
end
|
data/lib/taskinator/task.rb
CHANGED
@@ -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.
|
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
|
data/lib/taskinator/version.rb
CHANGED
data/lib/taskinator.rb
CHANGED
data/spec/support/test_flows.rb
CHANGED
@@ -132,4 +132,42 @@ module TestFlows
|
|
132
132
|
end
|
133
133
|
end
|
134
134
|
|
135
|
+
module NestedTask
|
136
|
+
extend Taskinator::Definition
|
137
|
+
include Support
|
138
|
+
|
139
|
+
define_process :task_count do
|
140
|
+
task :task_1
|
141
|
+
|
142
|
+
concurrent do
|
143
|
+
task :task_2
|
144
|
+
task :task_3
|
145
|
+
|
146
|
+
sequential do
|
147
|
+
task :task_4
|
148
|
+
task :task_5
|
149
|
+
|
150
|
+
concurrent do
|
151
|
+
task :task_6
|
152
|
+
task :task_7
|
153
|
+
|
154
|
+
sequential do
|
155
|
+
task :task_8
|
156
|
+
task :task_9
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
task :task_10
|
161
|
+
end
|
162
|
+
|
163
|
+
task :task_11
|
164
|
+
end
|
165
|
+
|
166
|
+
task :task_12
|
167
|
+
end
|
168
|
+
|
169
|
+
task :task_13
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
135
173
|
end
|
data/spec/taskinator/api_spec.rb
CHANGED
@@ -44,4 +44,20 @@ describe Taskinator::Api, :redis => true do
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
+
describe "#find_process" do
|
48
|
+
it {
|
49
|
+
# fetch method is covered by persistence spec
|
50
|
+
expect(Taskinator::Process).to receive(:fetch) {}
|
51
|
+
subject.find_process 'foo:bar:process'
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#find_task" do
|
56
|
+
it {
|
57
|
+
# fetch method is covered by persistence spec
|
58
|
+
expect(Taskinator::Task).to receive(:fetch) {}
|
59
|
+
subject.find_task 'foo:bar:process:baz:task'
|
60
|
+
}
|
61
|
+
end
|
62
|
+
|
47
63
|
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
|
@@ -370,24 +370,27 @@ describe Taskinator::Persistence, :redis => true do
|
|
370
370
|
TestFlows::Job,
|
371
371
|
TestFlows::SubProcess,
|
372
372
|
TestFlows::Sequential,
|
373
|
-
TestFlows::Concurrent
|
373
|
+
TestFlows::Concurrent,
|
374
|
+
TestFlows::EmptySequentialProcessTest,
|
375
|
+
TestFlows::EmptyConcurrentProcessTest,
|
376
|
+
TestFlows::NestedTask,
|
374
377
|
].each do |definition|
|
375
378
|
|
376
379
|
describe "#{definition.name} expire immediately" do
|
377
380
|
it {
|
378
|
-
process = definition.create_process(1)
|
379
|
-
|
380
381
|
Taskinator.redis do |conn|
|
381
|
-
|
382
|
+
# sanity check
|
383
|
+
expect(conn.keys).to be_empty
|
382
384
|
|
383
|
-
process.
|
385
|
+
process = definition.create_process(1)
|
384
386
|
|
385
|
-
|
387
|
+
# sanity check
|
388
|
+
expect(conn.hget(process.key, :uuid)).to eq(process.uuid)
|
386
389
|
|
387
|
-
|
388
|
-
expect(conn.hget(task.key, :uuid)).to be_nil
|
389
|
-
end
|
390
|
+
process.cleanup(0) # immediately
|
390
391
|
|
392
|
+
# ensure nothing left behind
|
393
|
+
expect(conn.keys).to be_empty
|
391
394
|
end
|
392
395
|
}
|
393
396
|
end
|
@@ -396,9 +399,14 @@ describe Taskinator::Persistence, :redis => true do
|
|
396
399
|
|
397
400
|
describe "expires in future" do
|
398
401
|
it {
|
399
|
-
process = TestFlows::Task.create_process(1)
|
400
|
-
|
401
402
|
Taskinator.redis do |conn|
|
403
|
+
|
404
|
+
# sanity check
|
405
|
+
expect(conn.keys).to be_empty
|
406
|
+
|
407
|
+
process = TestFlows::Task.create_process(1)
|
408
|
+
|
409
|
+
# sanity check
|
402
410
|
expect(conn.hget(process.key, :uuid)).to eq(process.uuid)
|
403
411
|
|
404
412
|
process.cleanup(2)
|
@@ -411,12 +419,8 @@ describe Taskinator::Persistence, :redis => true do
|
|
411
419
|
|
412
420
|
sleep 3
|
413
421
|
|
414
|
-
#
|
415
|
-
expect(conn.
|
416
|
-
recursively_enumerate_tasks(process.tasks) do |task|
|
417
|
-
expect(conn.hget(task.key, :uuid)).to be_nil
|
418
|
-
end
|
419
|
-
|
422
|
+
# ensure nothing left behind
|
423
|
+
expect(conn.keys).to be_empty
|
420
424
|
end
|
421
425
|
}
|
422
426
|
end
|
@@ -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
|
-
|
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(
|
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,
|
401
|
+
task = Taskinator::Task.define_job_task(process, TestJobModuleNoArgs, nil)
|
378
402
|
expect(process).to receive(:task_completed).with(task)
|
379
|
-
expect(
|
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,7 +23,6 @@ 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'
|
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
|
4
|
+
version: 0.4.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Chris Stefano
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-01-14 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
|
@@ -246,7 +232,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
246
232
|
- !ruby/object:Gem::Version
|
247
233
|
version: '0'
|
248
234
|
requirements: []
|
249
|
-
rubygems_version: 3.
|
235
|
+
rubygems_version: 3.2.3
|
250
236
|
signing_key:
|
251
237
|
specification_version: 4
|
252
238
|
summary: A simple orchestration library for running complex processes or workflows
|