taskinator 0.5.0 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
[![Gem Version](https://badge.fury.io/rb/taskinator.svg)](http://badge.fury.io/rb/taskinator)
|
4
|
-
[![Build Status](https://
|
4
|
+
[![Build Status](https://github.com/virtualstaticvoid/taskinator/actions/workflows/build.yml/badge.svg)](https://github.com/virtualstaticvoid/taskinator/actions/workflows/build.yml)
|
5
5
|
[![Code Climate](https://codeclimate.com/github/virtualstaticvoid/taskinator.png)](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
|