taskinator 0.5.0 → 0.5.2
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 +4 -4
- data/.github/workflows/{taskinator.yml → build.yml} +2 -15
- data/.github/workflows/release.yml +18 -0
- data/CHANGELOG.md +126 -108
- data/Gemfile +1 -1
- data/Gemfile.lock +64 -69
- data/README.md +50 -42
- data/lib/taskinator/builder.rb +129 -0
- data/lib/taskinator/definition.rb +2 -2
- data/lib/taskinator/instrumentation.rb +1 -0
- data/lib/taskinator/persistence.rb +57 -10
- data/lib/taskinator/process.rb +14 -9
- data/lib/taskinator/task.rb +9 -11
- data/lib/taskinator/version.rb +1 -1
- data/lib/taskinator.rb +0 -13
- data/spec/support/test_job.rb +30 -0
- data/spec/support/test_job_task.rb +2 -0
- data/spec/support/test_step_task.rb +2 -0
- data/spec/support/test_subprocess_task.rb +2 -0
- data/spec/taskinator/{definition/builder_spec.rb → builder_spec.rb} +2 -2
- data/spec/taskinator/definition_spec.rb +31 -0
- data/spec/taskinator/instrumentation_spec.rb +3 -2
- data/spec/taskinator/persistence_spec.rb +131 -5
- data/spec/taskinator/process_spec.rb +11 -3
- data/spec/taskinator/task_spec.rb +43 -25
- data/spec/taskinator/taskinator_spec.rb +16 -0
- data/taskinator.gemspec +2 -3
- metadata +20 -29
- data/lib/taskinator/definition/builder.rb +0 -129
- data/lib/taskinator/log_stats.rb +0 -49
- data/lib/taskinator/xml_visitor.rb +0 -109
- data/spec/taskinator/xml_visitor_spec.rb +0 -5
data/Gemfile.lock
CHANGED
|
@@ -1,36 +1,35 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
taskinator (0.5.
|
|
4
|
+
taskinator (0.5.2)
|
|
5
5
|
builder (>= 3.2.2)
|
|
6
6
|
connection_pool (>= 2.2.0)
|
|
7
|
-
globalid (
|
|
7
|
+
globalid (>= 0.3)
|
|
8
8
|
json (>= 1.8.2)
|
|
9
9
|
redis (>= 3.2.1)
|
|
10
10
|
redis-namespace (>= 1.5.2)
|
|
11
|
-
|
|
12
|
-
thwait (~> 0.2)
|
|
11
|
+
thwait (>= 0.2)
|
|
13
12
|
|
|
14
13
|
GEM
|
|
15
14
|
remote: https://rubygems.org/
|
|
16
15
|
specs:
|
|
17
|
-
actionpack (5.2.
|
|
18
|
-
actionview (= 5.2.
|
|
19
|
-
activesupport (= 5.2.
|
|
16
|
+
actionpack (5.2.8.1)
|
|
17
|
+
actionview (= 5.2.8.1)
|
|
18
|
+
activesupport (= 5.2.8.1)
|
|
20
19
|
rack (~> 2.0, >= 2.0.8)
|
|
21
20
|
rack-test (>= 0.6.3)
|
|
22
21
|
rails-dom-testing (~> 2.0)
|
|
23
22
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
|
24
|
-
actionview (5.2.
|
|
25
|
-
activesupport (= 5.2.
|
|
23
|
+
actionview (5.2.8.1)
|
|
24
|
+
activesupport (= 5.2.8.1)
|
|
26
25
|
builder (~> 3.1)
|
|
27
26
|
erubi (~> 1.4)
|
|
28
27
|
rails-dom-testing (~> 2.0)
|
|
29
28
|
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
|
30
|
-
activejob (5.2.
|
|
31
|
-
activesupport (= 5.2.
|
|
29
|
+
activejob (5.2.8.1)
|
|
30
|
+
activesupport (= 5.2.8.1)
|
|
32
31
|
globalid (>= 0.3.6)
|
|
33
|
-
activesupport (5.2.
|
|
32
|
+
activesupport (5.2.8.1)
|
|
34
33
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
35
34
|
i18n (>= 0.7, < 2)
|
|
36
35
|
minitest (~> 5.1)
|
|
@@ -38,8 +37,8 @@ GEM
|
|
|
38
37
|
builder (3.2.4)
|
|
39
38
|
byebug (11.1.3)
|
|
40
39
|
coderay (1.1.3)
|
|
41
|
-
concurrent-ruby (1.1.
|
|
42
|
-
connection_pool (2.
|
|
40
|
+
concurrent-ruby (1.1.10)
|
|
41
|
+
connection_pool (2.3.0)
|
|
43
42
|
coveralls (0.8.23)
|
|
44
43
|
json (>= 1.8, < 3)
|
|
45
44
|
simplecov (~> 0.16.1)
|
|
@@ -47,83 +46,82 @@ GEM
|
|
|
47
46
|
thor (>= 0.19.4, < 2.0)
|
|
48
47
|
tins (~> 1.6)
|
|
49
48
|
crass (1.0.6)
|
|
50
|
-
delayed_job (4.1.
|
|
49
|
+
delayed_job (4.1.11)
|
|
51
50
|
activesupport (>= 3.0, < 8.0)
|
|
52
51
|
diff-lcs (1.5.0)
|
|
53
52
|
docile (1.4.0)
|
|
54
53
|
e2mmap (0.1.0)
|
|
55
|
-
erubi (1.
|
|
54
|
+
erubi (1.12.0)
|
|
56
55
|
fakeredis (0.7.0)
|
|
57
56
|
redis (>= 3.2, < 5.0)
|
|
58
|
-
globalid (0.
|
|
57
|
+
globalid (1.0.0)
|
|
59
58
|
activesupport (>= 5.0)
|
|
60
|
-
i18n (1.
|
|
59
|
+
i18n (1.12.0)
|
|
61
60
|
concurrent-ruby (~> 1.0)
|
|
62
|
-
json (2.6.
|
|
63
|
-
loofah (2.
|
|
61
|
+
json (2.6.3)
|
|
62
|
+
loofah (2.19.1)
|
|
64
63
|
crass (~> 1.0.2)
|
|
65
64
|
nokogiri (>= 1.5.9)
|
|
66
65
|
method_source (1.0.0)
|
|
67
|
-
mini_portile2 (2.
|
|
68
|
-
minitest (5.
|
|
66
|
+
mini_portile2 (2.8.1)
|
|
67
|
+
minitest (5.17.0)
|
|
69
68
|
mono_logger (1.1.1)
|
|
70
69
|
multi_json (1.15.0)
|
|
71
|
-
mustermann (
|
|
70
|
+
mustermann (3.0.0)
|
|
72
71
|
ruby2_keywords (~> 0.0.1)
|
|
73
|
-
nokogiri (1.13.
|
|
74
|
-
mini_portile2 (~> 2.
|
|
72
|
+
nokogiri (1.13.10)
|
|
73
|
+
mini_portile2 (~> 2.8.0)
|
|
75
74
|
racc (~> 1.4)
|
|
76
|
-
pry (0.
|
|
75
|
+
pry (0.14.1)
|
|
77
76
|
coderay (~> 1.1)
|
|
78
77
|
method_source (~> 1.0)
|
|
79
|
-
pry-byebug (3.
|
|
78
|
+
pry-byebug (3.10.1)
|
|
80
79
|
byebug (~> 11.0)
|
|
81
|
-
pry (
|
|
82
|
-
racc (1.6.
|
|
83
|
-
rack (2.2.
|
|
84
|
-
rack-protection (
|
|
80
|
+
pry (>= 0.13, < 0.15)
|
|
81
|
+
racc (1.6.2)
|
|
82
|
+
rack (2.2.5)
|
|
83
|
+
rack-protection (3.0.5)
|
|
85
84
|
rack
|
|
86
|
-
rack-test (
|
|
87
|
-
rack (>= 1.
|
|
85
|
+
rack-test (2.0.2)
|
|
86
|
+
rack (>= 1.3)
|
|
88
87
|
rails-dom-testing (2.0.3)
|
|
89
88
|
activesupport (>= 4.2.0)
|
|
90
89
|
nokogiri (>= 1.6)
|
|
91
|
-
rails-html-sanitizer (1.4.
|
|
92
|
-
loofah (~> 2.
|
|
93
|
-
railties (5.2.
|
|
94
|
-
actionpack (= 5.2.
|
|
95
|
-
activesupport (= 5.2.
|
|
90
|
+
rails-html-sanitizer (1.4.4)
|
|
91
|
+
loofah (~> 2.19, >= 2.19.1)
|
|
92
|
+
railties (5.2.8.1)
|
|
93
|
+
actionpack (= 5.2.8.1)
|
|
94
|
+
activesupport (= 5.2.8.1)
|
|
96
95
|
method_source
|
|
97
96
|
rake (>= 0.8.7)
|
|
98
97
|
thor (>= 0.19.0, < 2.0)
|
|
99
98
|
rake (13.0.6)
|
|
100
|
-
redis (4.
|
|
101
|
-
redis-namespace (1.
|
|
102
|
-
redis (>=
|
|
103
|
-
resque (2.
|
|
99
|
+
redis (4.8.0)
|
|
100
|
+
redis-namespace (1.10.0)
|
|
101
|
+
redis (>= 4)
|
|
102
|
+
resque (2.4.0)
|
|
104
103
|
mono_logger (~> 1.0)
|
|
105
104
|
multi_json (~> 1.0)
|
|
106
105
|
redis-namespace (~> 1.6)
|
|
107
106
|
sinatra (>= 0.9.2)
|
|
108
|
-
vegas (~> 0.1.2)
|
|
109
107
|
resque_spec (0.18.1)
|
|
110
108
|
resque (>= 1.26.0)
|
|
111
109
|
rspec-core (>= 3.0.0)
|
|
112
110
|
rspec-expectations (>= 3.0.0)
|
|
113
111
|
rspec-mocks (>= 3.0.0)
|
|
114
|
-
rspec (3.
|
|
115
|
-
rspec-core (~> 3.
|
|
116
|
-
rspec-expectations (~> 3.
|
|
117
|
-
rspec-mocks (~> 3.
|
|
118
|
-
rspec-core (3.
|
|
119
|
-
rspec-support (~> 3.
|
|
120
|
-
rspec-expectations (3.
|
|
112
|
+
rspec (3.12.0)
|
|
113
|
+
rspec-core (~> 3.12.0)
|
|
114
|
+
rspec-expectations (~> 3.12.0)
|
|
115
|
+
rspec-mocks (~> 3.12.0)
|
|
116
|
+
rspec-core (3.12.0)
|
|
117
|
+
rspec-support (~> 3.12.0)
|
|
118
|
+
rspec-expectations (3.12.1)
|
|
121
119
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
122
|
-
rspec-support (~> 3.
|
|
123
|
-
rspec-mocks (3.
|
|
120
|
+
rspec-support (~> 3.12.0)
|
|
121
|
+
rspec-mocks (3.12.1)
|
|
124
122
|
diff-lcs (>= 1.2.0, < 2.0)
|
|
125
|
-
rspec-support (~> 3.
|
|
126
|
-
rspec-rails (5.1.
|
|
123
|
+
rspec-support (~> 3.12.0)
|
|
124
|
+
rspec-rails (5.1.2)
|
|
127
125
|
actionpack (>= 5.2)
|
|
128
126
|
activesupport (>= 5.2)
|
|
129
127
|
railties (>= 5.2)
|
|
@@ -134,23 +132,22 @@ GEM
|
|
|
134
132
|
rspec-sidekiq (3.1.0)
|
|
135
133
|
rspec-core (~> 3.0, >= 3.0.0)
|
|
136
134
|
sidekiq (>= 2.4.0)
|
|
137
|
-
rspec-support (3.
|
|
135
|
+
rspec-support (3.12.0)
|
|
138
136
|
ruby2_keywords (0.0.5)
|
|
139
|
-
sidekiq (6.
|
|
140
|
-
connection_pool (>= 2.2.
|
|
137
|
+
sidekiq (6.5.8)
|
|
138
|
+
connection_pool (>= 2.2.5, < 3)
|
|
141
139
|
rack (~> 2.0)
|
|
142
|
-
redis (>= 4.
|
|
140
|
+
redis (>= 4.5.0, < 5)
|
|
143
141
|
simplecov (0.16.1)
|
|
144
142
|
docile (~> 1.1)
|
|
145
143
|
json (>= 1.8, < 3)
|
|
146
144
|
simplecov-html (~> 0.10.0)
|
|
147
145
|
simplecov-html (0.10.2)
|
|
148
|
-
sinatra (
|
|
149
|
-
mustermann (~>
|
|
150
|
-
rack (~> 2.2)
|
|
151
|
-
rack-protection (=
|
|
146
|
+
sinatra (3.0.5)
|
|
147
|
+
mustermann (~> 3.0)
|
|
148
|
+
rack (~> 2.2, >= 2.2.4)
|
|
149
|
+
rack-protection (= 3.0.5)
|
|
152
150
|
tilt (~> 2.0)
|
|
153
|
-
statsd-ruby (1.4.0)
|
|
154
151
|
sync (0.5.0)
|
|
155
152
|
term-ansicolor (1.7.1)
|
|
156
153
|
tins (~> 1.0)
|
|
@@ -158,13 +155,11 @@ GEM
|
|
|
158
155
|
thread_safe (0.3.6)
|
|
159
156
|
thwait (0.2.0)
|
|
160
157
|
e2mmap
|
|
161
|
-
tilt (2.0.
|
|
162
|
-
tins (1.
|
|
158
|
+
tilt (2.0.11)
|
|
159
|
+
tins (1.32.1)
|
|
163
160
|
sync
|
|
164
|
-
tzinfo (1.2.
|
|
161
|
+
tzinfo (1.2.10)
|
|
165
162
|
thread_safe (~> 0.1)
|
|
166
|
-
vegas (0.1.11)
|
|
167
|
-
rack (>= 1.0.0)
|
|
168
163
|
|
|
169
164
|
PLATFORMS
|
|
170
165
|
ruby
|
|
@@ -184,7 +179,7 @@ DEPENDENCIES
|
|
|
184
179
|
rspec
|
|
185
180
|
rspec-rails (>= 2.0)
|
|
186
181
|
rspec-sidekiq (>= 2.1.0)
|
|
187
|
-
sidekiq (>= 3.5.0)
|
|
182
|
+
sidekiq (>= 3.5.0, < 7.0.0)
|
|
188
183
|
taskinator!
|
|
189
184
|
|
|
190
185
|
BUNDLED WITH
|
data/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Taskinator
|
|
2
2
|
|
|
3
3
|
[](http://badge.fury.io/rb/taskinator)
|
|
4
|
-
[](https://github.com/virtualstaticvoid/taskinator/actions/workflows/build.yml)
|
|
5
5
|
[](https://codeclimate.com/github/virtualstaticvoid/taskinator)
|
|
6
6
|
|
|
7
7
|
A simple orchestration library for running complex processes or workflows in Ruby.
|
|
@@ -53,8 +53,8 @@ If you are using Taskinator within a Rails application, then add an initializer,
|
|
|
53
53
|
Taskinator.configure do |config|
|
|
54
54
|
|
|
55
55
|
# configure the queue adapter to use
|
|
56
|
-
# can be :active_job, :delayed_job, :
|
|
57
|
-
config.queue_adapter = :
|
|
56
|
+
# can be :active_job, :delayed_job, :resque or :sidekiq
|
|
57
|
+
config.queue_adapter = :resque
|
|
58
58
|
|
|
59
59
|
# configure redis
|
|
60
60
|
config.redis = {
|
|
@@ -171,39 +171,6 @@ module MyProcess
|
|
|
171
171
|
end
|
|
172
172
|
```
|
|
173
173
|
|
|
174
|
-
#### Reusing ActiveJob jobs
|
|
175
|
-
|
|
176
|
-
It is likely that you already have one or more [jobs](https://guides.rubyonrails.org/active_job_basics.html)
|
|
177
|
-
and want to reuse them within the process definition.
|
|
178
|
-
|
|
179
|
-
Define a `job` step, providing the class of the Active Job to run and then taskinator will
|
|
180
|
-
invoke that job as part of the process.
|
|
181
|
-
|
|
182
|
-
The `job` step will be queued and executed on same queue as
|
|
183
|
-
[configured by the job](https://guides.rubyonrails.org/active_job_basics.html#queues).
|
|
184
|
-
|
|
185
|
-
```ruby
|
|
186
|
-
# E.g. A resque worker
|
|
187
|
-
class DoSomeWork
|
|
188
|
-
queue :high_priority
|
|
189
|
-
|
|
190
|
-
def self.perform(arg1, arg2)
|
|
191
|
-
# code to do the work
|
|
192
|
-
end
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
module MyProcess
|
|
196
|
-
extend Taskinator::Definition
|
|
197
|
-
|
|
198
|
-
# when creating the process, supply the same arguments
|
|
199
|
-
# that the DoSomeWork worker expects
|
|
200
|
-
|
|
201
|
-
define_process do
|
|
202
|
-
job DoSomeWork
|
|
203
|
-
end
|
|
204
|
-
end
|
|
205
|
-
```
|
|
206
|
-
|
|
207
174
|
#### Data Driven Process Definitions
|
|
208
175
|
|
|
209
176
|
You can also define data driven tasks using the `for_each` method, which takes an iterator method
|
|
@@ -271,7 +238,7 @@ process2 = MyProcess.create_process
|
|
|
271
238
|
process2.tasks.count #=> 1
|
|
272
239
|
```
|
|
273
240
|
|
|
274
|
-
#### Transformations
|
|
241
|
+
#### Argument Transformations
|
|
275
242
|
|
|
276
243
|
In addition, it is possible to transform the arguments used by a task or job, by including
|
|
277
244
|
a `transform` step in the definition.
|
|
@@ -396,16 +363,54 @@ MyProcess.create_process(1, 2, 3, :send_notification => true)
|
|
|
396
363
|
|
|
397
364
|
```
|
|
398
365
|
|
|
366
|
+
#### Reusing ActiveJob jobs
|
|
367
|
+
|
|
368
|
+
It is likely that you already have one or more [jobs](https://guides.rubyonrails.org/active_job_basics.html)
|
|
369
|
+
and want to reuse them within the process definition.
|
|
370
|
+
|
|
371
|
+
Define a `job` step, providing the class of the Active Job to run and then taskinator will
|
|
372
|
+
invoke that job as part of the process.
|
|
373
|
+
|
|
374
|
+
The `job` step will be queued and executed on same queue as
|
|
375
|
+
[configured by the job](https://guides.rubyonrails.org/active_job_basics.html#queues).
|
|
376
|
+
|
|
377
|
+
```ruby
|
|
378
|
+
# E.g. A resque worker
|
|
379
|
+
class DoSomeWork
|
|
380
|
+
queue :high_priority
|
|
381
|
+
|
|
382
|
+
def self.perform(arg1, arg2)
|
|
383
|
+
# code to do the work
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
module MyProcess
|
|
388
|
+
extend Taskinator::Definition
|
|
389
|
+
|
|
390
|
+
# when creating the process, supply the same arguments
|
|
391
|
+
# that the DoSomeWork worker expects
|
|
392
|
+
|
|
393
|
+
define_process do
|
|
394
|
+
job DoSomeWork
|
|
395
|
+
end
|
|
396
|
+
end
|
|
397
|
+
```
|
|
398
|
+
|
|
399
399
|
### Execution
|
|
400
400
|
|
|
401
|
-
A process is
|
|
401
|
+
A process is created by calling the generated `create_process` method on your "process" module.
|
|
402
402
|
|
|
403
403
|
```ruby
|
|
404
404
|
process = MyProcess.create_process
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
And then enqueued for execution by calling the `enqueue!` method of the process.
|
|
408
|
+
|
|
409
|
+
```ruby
|
|
405
410
|
process.enqueue!
|
|
406
411
|
```
|
|
407
412
|
|
|
408
|
-
Or,
|
|
413
|
+
Or, started immediately by calling the `start!` method of the process.
|
|
409
414
|
|
|
410
415
|
```ruby
|
|
411
416
|
process = MyProcess.create_process
|
|
@@ -637,6 +642,7 @@ process_id = "SUPPLY-PROCESS-IDENTIFIER"
|
|
|
637
642
|
process = Taskinator::Api.find_process(process_id)
|
|
638
643
|
|
|
639
644
|
puts process.inspect
|
|
645
|
+
puts process.definition
|
|
640
646
|
puts process.current_state
|
|
641
647
|
puts process.tasks
|
|
642
648
|
# etc...
|
|
@@ -655,6 +661,7 @@ task = Taskinator::Api.find_task(task_id)
|
|
|
655
661
|
|
|
656
662
|
puts task.inspect
|
|
657
663
|
puts task.class
|
|
664
|
+
puts task.definition
|
|
658
665
|
puts task.args # for Step and Job types
|
|
659
666
|
puts task.sub_process.tasks # for SubProcess type
|
|
660
667
|
# etc...
|
|
@@ -708,7 +715,7 @@ To configure the queue adapter to use, set `config.queue_adapter` to one of the
|
|
|
708
715
|
|
|
709
716
|
* `:active_job`
|
|
710
717
|
* `:delayed_job`
|
|
711
|
-
* `:
|
|
718
|
+
* `:resque`
|
|
712
719
|
* `:sidekiq`
|
|
713
720
|
|
|
714
721
|
As follows:
|
|
@@ -717,8 +724,8 @@ As follows:
|
|
|
717
724
|
Taskinator.configure do |config|
|
|
718
725
|
|
|
719
726
|
# configure the queue adapter to use
|
|
720
|
-
# can be :active_job, :delayed_job, :
|
|
721
|
-
config.queue_adapter = :
|
|
727
|
+
# can be :active_job, :delayed_job, :resque or :sidekiq
|
|
728
|
+
config.queue_adapter = :resque
|
|
722
729
|
|
|
723
730
|
end
|
|
724
731
|
```
|
|
@@ -787,6 +794,7 @@ For all events, the data included contains the following information:
|
|
|
787
794
|
| Key | Value |
|
|
788
795
|
|---------------------------------|----------------------------------------------------------|
|
|
789
796
|
| `:type` | The type name of the component reporting the event |
|
|
797
|
+
| `:definition` | The type name of the process definition |
|
|
790
798
|
| `:process_uuid` | The UUID of the root process |
|
|
791
799
|
| `:process_options` | Options hash of the root process |
|
|
792
800
|
| `:uuid` | The UUID of the respective task, job or sub process |
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
module Taskinator
|
|
2
|
+
class Builder
|
|
3
|
+
|
|
4
|
+
attr_reader :process
|
|
5
|
+
attr_reader :definition
|
|
6
|
+
attr_reader :args
|
|
7
|
+
attr_reader :builder_options
|
|
8
|
+
|
|
9
|
+
def initialize(process, definition, *args)
|
|
10
|
+
@process = process
|
|
11
|
+
@definition = definition
|
|
12
|
+
@builder_options = args.last.is_a?(Hash) ? args.pop : {}
|
|
13
|
+
@args = args
|
|
14
|
+
@executor = Taskinator::Executor.new(@definition)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def option?(key, &block)
|
|
18
|
+
yield if builder_options[key]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# defines a sub process of tasks which are executed sequentially
|
|
22
|
+
def sequential(options={}, &block)
|
|
23
|
+
raise ArgumentError, 'block' unless block_given?
|
|
24
|
+
|
|
25
|
+
sub_process = Process.define_sequential_process_for(@definition, options)
|
|
26
|
+
task = define_sub_process_task(@process, sub_process, options)
|
|
27
|
+
Builder.new(sub_process, @definition, *@args).instance_eval(&block)
|
|
28
|
+
@process.tasks << task if sub_process.tasks.any?
|
|
29
|
+
nil
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# defines a sub process of tasks which are executed concurrently
|
|
33
|
+
def concurrent(complete_on=CompleteOn::Default, options={}, &block)
|
|
34
|
+
raise ArgumentError, 'block' unless block_given?
|
|
35
|
+
|
|
36
|
+
sub_process = Process.define_concurrent_process_for(@definition, complete_on, options)
|
|
37
|
+
task = define_sub_process_task(@process, sub_process, options)
|
|
38
|
+
Builder.new(sub_process, @definition, *@args).instance_eval(&block)
|
|
39
|
+
@process.tasks << task if sub_process.tasks.any?
|
|
40
|
+
nil
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# dynamically defines tasks, using the given @iterator method
|
|
44
|
+
# the definition will be evaluated for each yielded item
|
|
45
|
+
def for_each(method, options={}, &block)
|
|
46
|
+
raise ArgumentError, 'method' if method.nil?
|
|
47
|
+
raise NoMethodError, method unless @executor.respond_to?(method)
|
|
48
|
+
raise ArgumentError, 'block' unless block_given?
|
|
49
|
+
|
|
50
|
+
#
|
|
51
|
+
# `for_each` is an exception, since it invokes the definition
|
|
52
|
+
# in order to yield elements to the builder, and any options passed
|
|
53
|
+
# are included with the builder options
|
|
54
|
+
#
|
|
55
|
+
method_args = options.any? ? [*@args, options] : @args
|
|
56
|
+
@executor.send(method, *method_args) do |*args|
|
|
57
|
+
Builder.new(@process, @definition, *args).instance_eval(&block)
|
|
58
|
+
end
|
|
59
|
+
nil
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
alias_method :transform, :for_each
|
|
63
|
+
|
|
64
|
+
# defines a task which executes the given @method
|
|
65
|
+
def task(method, options={})
|
|
66
|
+
raise ArgumentError, 'method' if method.nil?
|
|
67
|
+
raise NoMethodError, method unless @executor.respond_to?(method)
|
|
68
|
+
|
|
69
|
+
define_step_task(@process, method, @args, options)
|
|
70
|
+
nil
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# defines a task which executes the given @job
|
|
74
|
+
# which is expected to implement a perform method either as a class or instance method
|
|
75
|
+
def job(job, options={})
|
|
76
|
+
raise ArgumentError, 'job' if job.nil?
|
|
77
|
+
raise ArgumentError, 'job' unless job.methods.include?(:perform) || job.instance_methods.include?(:perform)
|
|
78
|
+
|
|
79
|
+
define_job_task(@process, job, @args, options)
|
|
80
|
+
nil
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# TODO: add mailer
|
|
84
|
+
# TODO: add complete!
|
|
85
|
+
# TODO: add fail!
|
|
86
|
+
|
|
87
|
+
# defines a sub process task, for the given @definition
|
|
88
|
+
# the definition specified must have input compatible arguments
|
|
89
|
+
# to the current definition
|
|
90
|
+
def sub_process(definition, options={})
|
|
91
|
+
raise ArgumentError, 'definition' if definition.nil?
|
|
92
|
+
raise ArgumentError, "#{definition.name} does not extend the #{Definition.name} module" unless definition.kind_of?(Definition)
|
|
93
|
+
|
|
94
|
+
sub_process = definition.create_sub_process(*@args, combine_options(options))
|
|
95
|
+
task = define_sub_process_task(@process, sub_process, options)
|
|
96
|
+
Builder.new(sub_process, definition, *@args)
|
|
97
|
+
@process.tasks << task if sub_process.tasks.any?
|
|
98
|
+
nil
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
private
|
|
102
|
+
|
|
103
|
+
def define_step_task(process, method, args, options={})
|
|
104
|
+
define_task(process) {
|
|
105
|
+
Task.define_step_task(process, method, args, combine_options(options))
|
|
106
|
+
}
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def define_job_task(process, job, args, options={})
|
|
110
|
+
define_task(process) {
|
|
111
|
+
Task.define_job_task(process, job, args, combine_options(options))
|
|
112
|
+
}
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def define_sub_process_task(process, sub_process, options={})
|
|
116
|
+
Task.define_sub_process_task(process, sub_process, combine_options(options))
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def define_task(process)
|
|
120
|
+
process.tasks << task = yield
|
|
121
|
+
task
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def combine_options(options={})
|
|
125
|
+
builder_options.merge(options)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
end
|
|
129
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'builder'
|
|
2
|
+
|
|
1
3
|
module Taskinator
|
|
2
4
|
module Persistence
|
|
3
5
|
|
|
@@ -113,13 +115,13 @@ module Taskinator
|
|
|
113
115
|
transaction.hmset(
|
|
114
116
|
self.key,
|
|
115
117
|
:state, new_state,
|
|
116
|
-
:updated_at, @updated_at
|
|
118
|
+
:updated_at, @updated_at.iso8601(3)
|
|
117
119
|
)
|
|
118
120
|
|
|
119
121
|
# also update the "root" process
|
|
120
122
|
transaction.hset(
|
|
121
123
|
process_key,
|
|
122
|
-
:updated_at, @updated_at
|
|
124
|
+
:updated_at, @updated_at.iso8601(3)
|
|
123
125
|
)
|
|
124
126
|
end
|
|
125
127
|
end
|
|
@@ -136,7 +138,7 @@ module Taskinator
|
|
|
136
138
|
:error_type, error.class.name,
|
|
137
139
|
:error_message, error.message,
|
|
138
140
|
:error_backtrace, JSON.generate(error.backtrace || []),
|
|
139
|
-
:updated_at, Time.now.utc
|
|
141
|
+
:updated_at, Time.now.utc.iso8601(3)
|
|
140
142
|
)
|
|
141
143
|
end
|
|
142
144
|
end
|
|
@@ -180,7 +182,7 @@ module Taskinator
|
|
|
180
182
|
process_key = self.process_key
|
|
181
183
|
conn.multi do |transaction|
|
|
182
184
|
transaction.hincrby process_key, "tasks_#{status}", 1
|
|
183
|
-
transaction.hset process_key, :updated_at, Time.now.utc
|
|
185
|
+
transaction.hset process_key, :updated_at, Time.now.utc.iso8601(3)
|
|
184
186
|
end
|
|
185
187
|
end
|
|
186
188
|
end
|
|
@@ -292,7 +294,8 @@ module Taskinator
|
|
|
292
294
|
end
|
|
293
295
|
|
|
294
296
|
def visit_attribute_time(attribute)
|
|
295
|
-
|
|
297
|
+
value = @instance.send(attribute)
|
|
298
|
+
@hmset += [attribute, value.iso8601(3)] if value
|
|
296
299
|
end
|
|
297
300
|
|
|
298
301
|
def visit_attribute_enum(attribute, type)
|
|
@@ -447,6 +450,46 @@ module Taskinator
|
|
|
447
450
|
end
|
|
448
451
|
end
|
|
449
452
|
|
|
453
|
+
class UnknownTypeError < StandardError
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
module UnknownType
|
|
457
|
+
|
|
458
|
+
@unknown_types = {}
|
|
459
|
+
|
|
460
|
+
def self.new(type)
|
|
461
|
+
Taskinator.logger.error("Unknown type '#{type}' while deserializing.")
|
|
462
|
+
@unknown_types[type] ||= Module.new do
|
|
463
|
+
extend UnknownType
|
|
464
|
+
@type = type
|
|
465
|
+
|
|
466
|
+
# for unknown definitions
|
|
467
|
+
def method_missing(_, *_)
|
|
468
|
+
raise UnknownTypeError.new(to_s)
|
|
469
|
+
end
|
|
470
|
+
end
|
|
471
|
+
end
|
|
472
|
+
|
|
473
|
+
attr_reader :type
|
|
474
|
+
|
|
475
|
+
def to_s
|
|
476
|
+
"Unknown type '#{type}'."
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
def allocate
|
|
480
|
+
self
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
def accept(*_)
|
|
484
|
+
# nothing doing
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
# for unknown job types
|
|
488
|
+
def perform(*_)
|
|
489
|
+
raise UnknownTypeError.new(to_s)
|
|
490
|
+
end
|
|
491
|
+
end
|
|
492
|
+
|
|
450
493
|
class RedisDeserializationVisitor < Taskinator::Visitor::Base
|
|
451
494
|
|
|
452
495
|
#
|
|
@@ -480,7 +523,7 @@ module Taskinator
|
|
|
480
523
|
return unless @attribute_values.key?(:type)
|
|
481
524
|
|
|
482
525
|
type = @attribute_values[:type]
|
|
483
|
-
klass = Kernel.const_get(type)
|
|
526
|
+
klass = Kernel.const_get(type) rescue UnknownType.new(type)
|
|
484
527
|
|
|
485
528
|
#
|
|
486
529
|
# NOTE:
|
|
@@ -539,16 +582,18 @@ module Taskinator
|
|
|
539
582
|
def visit_attribute_enum(attribute, type)
|
|
540
583
|
visit_attribute(attribute) do |value|
|
|
541
584
|
const_value = type.constants.select {|c| type.const_get(c) == value.to_i }.first
|
|
542
|
-
const_value
|
|
543
|
-
type.const_get(const_value)
|
|
544
|
-
|
|
585
|
+
if const_value
|
|
586
|
+
type.const_get(const_value)
|
|
587
|
+
else
|
|
588
|
+
defined?(type::Default) ? type::Default : nil
|
|
589
|
+
end
|
|
545
590
|
end
|
|
546
591
|
end
|
|
547
592
|
|
|
548
593
|
def visit_type(attribute)
|
|
549
594
|
value = @attribute_values[attribute]
|
|
550
595
|
if value
|
|
551
|
-
type = Kernel.const_get(value)
|
|
596
|
+
type = Kernel.const_get(value) rescue UnknownType.new(value)
|
|
552
597
|
@instance.instance_variable_set("@#{attribute}", type)
|
|
553
598
|
end
|
|
554
599
|
end
|
|
@@ -576,6 +621,8 @@ module Taskinator
|
|
|
576
621
|
type = Taskinator.redis do |conn|
|
|
577
622
|
conn.hget(base.key_for(uuid), :type)
|
|
578
623
|
end
|
|
624
|
+
# these types are always from taskinator
|
|
625
|
+
# so no need to rescue for unknown types!
|
|
579
626
|
klass = Kernel.const_get(type)
|
|
580
627
|
LazyLoader.new(klass, uuid, @instance_cache)
|
|
581
628
|
end
|