taskinator 0.5.0 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,36 +1,35 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- taskinator (0.5.0)
4
+ taskinator (0.5.2)
5
5
  builder (>= 3.2.2)
6
6
  connection_pool (>= 2.2.0)
7
- globalid (~> 0.3)
7
+ globalid (>= 0.3)
8
8
  json (>= 1.8.2)
9
9
  redis (>= 3.2.1)
10
10
  redis-namespace (>= 1.5.2)
11
- statsd-ruby (~> 1.4.0)
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.6.2)
18
- actionview (= 5.2.6.2)
19
- activesupport (= 5.2.6.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.6.2)
25
- activesupport (= 5.2.6.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.6.2)
31
- activesupport (= 5.2.6.2)
29
+ activejob (5.2.8.1)
30
+ activesupport (= 5.2.8.1)
32
31
  globalid (>= 0.3.6)
33
- activesupport (5.2.6.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.9)
42
- connection_pool (2.2.5)
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.10)
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.10.0)
54
+ erubi (1.12.0)
56
55
  fakeredis (0.7.0)
57
56
  redis (>= 3.2, < 5.0)
58
- globalid (0.6.0)
57
+ globalid (1.0.0)
59
58
  activesupport (>= 5.0)
60
- i18n (1.9.1)
59
+ i18n (1.12.0)
61
60
  concurrent-ruby (~> 1.0)
62
- json (2.6.1)
63
- loofah (2.14.0)
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.7.1)
68
- minitest (5.15.0)
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 (1.1.1)
70
+ mustermann (3.0.0)
72
71
  ruby2_keywords (~> 0.0.1)
73
- nokogiri (1.13.1)
74
- mini_portile2 (~> 2.7.0)
72
+ nokogiri (1.13.10)
73
+ mini_portile2 (~> 2.8.0)
75
74
  racc (~> 1.4)
76
- pry (0.13.1)
75
+ pry (0.14.1)
77
76
  coderay (~> 1.1)
78
77
  method_source (~> 1.0)
79
- pry-byebug (3.9.0)
78
+ pry-byebug (3.10.1)
80
79
  byebug (~> 11.0)
81
- pry (~> 0.13.0)
82
- racc (1.6.0)
83
- rack (2.2.3)
84
- rack-protection (2.1.0)
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 (1.1.0)
87
- rack (>= 1.0, < 3)
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.2)
92
- loofah (~> 2.3)
93
- railties (5.2.6.2)
94
- actionpack (= 5.2.6.2)
95
- activesupport (= 5.2.6.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.6.0)
101
- redis-namespace (1.8.1)
102
- redis (>= 3.0.4)
103
- resque (2.2.0)
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.11.0)
115
- rspec-core (~> 3.11.0)
116
- rspec-expectations (~> 3.11.0)
117
- rspec-mocks (~> 3.11.0)
118
- rspec-core (3.11.0)
119
- rspec-support (~> 3.11.0)
120
- rspec-expectations (3.11.0)
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.11.0)
123
- rspec-mocks (3.11.0)
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.11.0)
126
- rspec-rails (5.1.0)
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.11.0)
135
+ rspec-support (3.12.0)
138
136
  ruby2_keywords (0.0.5)
139
- sidekiq (6.4.1)
140
- connection_pool (>= 2.2.2)
137
+ sidekiq (6.5.8)
138
+ connection_pool (>= 2.2.5, < 3)
141
139
  rack (~> 2.0)
142
- redis (>= 4.2.0)
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 (2.1.0)
149
- mustermann (~> 1.0)
150
- rack (~> 2.2)
151
- rack-protection (= 2.1.0)
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.10)
162
- tins (1.31.0)
158
+ tilt (2.0.11)
159
+ tins (1.32.1)
163
160
  sync
164
- tzinfo (1.2.9)
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://img.shields.io/github/workflow/status/virtualstaticvoid/taskinator/Taskinator?style=flat-square)](https://github.com/virtualstaticvoid/taskinator/actions)
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, :redis or :sidekiq
57
- config.queue_adapter = :redis
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 executed by calling the generated `create_process` method on your "process" module.
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, to start immediately, call the `start!` method.
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
- * `:redis`
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, :redis or :sidekiq
721
- config.queue_adapter = :redis
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 'taskinator/builder'
2
+
1
3
  module Taskinator
2
4
  module Definition
3
5
 
@@ -120,5 +122,3 @@ module Taskinator
120
122
 
121
123
  end
122
124
  end
123
-
124
- require 'taskinator/definition/builder'
@@ -58,6 +58,7 @@ module Taskinator
58
58
  return OpenStruct.new(
59
59
  {
60
60
  :type => self.class.name,
61
+ :definition => self.definition.name,
61
62
  :process_uuid => process_uuid,
62
63
  :process_options => process_options.dup,
63
64
  :uuid => uuid,
@@ -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
- visit_attribute(attribute)
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
- (defined?(type::Default) ? type::Default : nil)
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