rocketjob 5.4.1 → 6.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +175 -5
  3. data/bin/rocketjob_batch_perf +1 -1
  4. data/bin/rocketjob_perf +1 -1
  5. data/lib/rocket_job/batch/categories.rb +345 -0
  6. data/lib/rocket_job/batch/io.rb +174 -106
  7. data/lib/rocket_job/batch/model.rb +20 -68
  8. data/lib/rocket_job/batch/performance.rb +19 -7
  9. data/lib/rocket_job/batch/statistics.rb +34 -12
  10. data/lib/rocket_job/batch/throttle_running_workers.rb +2 -6
  11. data/lib/rocket_job/batch/worker.rb +31 -26
  12. data/lib/rocket_job/batch.rb +3 -1
  13. data/lib/rocket_job/category/base.rb +81 -0
  14. data/lib/rocket_job/category/input.rb +170 -0
  15. data/lib/rocket_job/category/output.rb +34 -0
  16. data/lib/rocket_job/cli.rb +25 -17
  17. data/lib/rocket_job/dirmon_entry.rb +23 -13
  18. data/lib/rocket_job/event.rb +1 -1
  19. data/lib/rocket_job/extensions/iostreams/path.rb +32 -0
  20. data/lib/rocket_job/extensions/mongoid/contextual/mongo.rb +2 -2
  21. data/lib/rocket_job/extensions/mongoid/factory.rb +4 -12
  22. data/lib/rocket_job/extensions/mongoid/stringified_symbol.rb +50 -0
  23. data/lib/rocket_job/extensions/psych/yaml_tree.rb +8 -0
  24. data/lib/rocket_job/extensions/rocket_job_adapter.rb +2 -2
  25. data/lib/rocket_job/jobs/conversion_job.rb +43 -0
  26. data/lib/rocket_job/jobs/dirmon_job.rb +25 -36
  27. data/lib/rocket_job/jobs/housekeeping_job.rb +11 -12
  28. data/lib/rocket_job/jobs/on_demand_batch_job.rb +24 -11
  29. data/lib/rocket_job/jobs/on_demand_job.rb +3 -4
  30. data/lib/rocket_job/jobs/performance_job.rb +3 -1
  31. data/lib/rocket_job/jobs/re_encrypt/relational_job.rb +103 -96
  32. data/lib/rocket_job/jobs/upload_file_job.rb +48 -8
  33. data/lib/rocket_job/lookup_collection.rb +69 -0
  34. data/lib/rocket_job/plugins/cron.rb +60 -20
  35. data/lib/rocket_job/plugins/job/model.rb +25 -50
  36. data/lib/rocket_job/plugins/job/persistence.rb +36 -0
  37. data/lib/rocket_job/plugins/job/throttle.rb +2 -2
  38. data/lib/rocket_job/plugins/job/throttle_running_jobs.rb +1 -1
  39. data/lib/rocket_job/plugins/job/worker.rb +2 -7
  40. data/lib/rocket_job/plugins/restart.rb +3 -103
  41. data/lib/rocket_job/plugins/state_machine.rb +4 -3
  42. data/lib/rocket_job/plugins/throttle_dependent_jobs.rb +37 -0
  43. data/lib/rocket_job/ractor_worker.rb +42 -0
  44. data/lib/rocket_job/server/model.rb +1 -1
  45. data/lib/rocket_job/sliced/bzip2_output_slice.rb +18 -19
  46. data/lib/rocket_job/sliced/compressed_slice.rb +3 -6
  47. data/lib/rocket_job/sliced/encrypted_bzip2_output_slice.rb +49 -0
  48. data/lib/rocket_job/sliced/encrypted_slice.rb +4 -6
  49. data/lib/rocket_job/sliced/input.rb +42 -54
  50. data/lib/rocket_job/sliced/slice.rb +12 -16
  51. data/lib/rocket_job/sliced/slices.rb +26 -11
  52. data/lib/rocket_job/sliced/writer/input.rb +46 -18
  53. data/lib/rocket_job/sliced/writer/output.rb +33 -45
  54. data/lib/rocket_job/sliced.rb +1 -74
  55. data/lib/rocket_job/subscribers/server.rb +1 -1
  56. data/lib/rocket_job/thread_worker.rb +46 -0
  57. data/lib/rocket_job/throttle_definitions.rb +7 -1
  58. data/lib/rocket_job/version.rb +1 -1
  59. data/lib/rocket_job/worker.rb +21 -55
  60. data/lib/rocket_job/worker_pool.rb +5 -7
  61. data/lib/rocketjob.rb +53 -43
  62. metadata +36 -28
  63. data/lib/rocket_job/batch/tabular/input.rb +0 -131
  64. data/lib/rocket_job/batch/tabular/output.rb +0 -65
  65. data/lib/rocket_job/batch/tabular.rb +0 -56
  66. data/lib/rocket_job/extensions/mongoid/remove_warnings.rb +0 -12
  67. data/lib/rocket_job/jobs/on_demand_batch_tabular_job.rb +0 -28
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 97910a2bbdcc67b808677e01f9ef9650935deac9f47ee415ad7665227e06e2a3
4
- data.tar.gz: 9c560b2a1deea93eb4c8e9cdf6fb28f2308e8f9a85457660c292ad38a2b76137
3
+ metadata.gz: e313f192b854d066258a614ceac1131851c8df94c7e08f7cea6681fff6946d69
4
+ data.tar.gz: 10804682bee08715671696db4610ce4f93679398398bc385e93619f5a3aca715
5
5
  SHA512:
6
- metadata.gz: 768538825ba34f58d915205fb1c61066ef2d86d9c3743f487db381074e30bd75c420f99f017adbb338c0dd4f202a69cf699b0b4e2a9ebc0581423fb102c85284
7
- data.tar.gz: 17d494818bf341a6b3340f4c0c5d5b77ec68c05eeb26d10eccb43c81221c14acd64ffe73ea1f610b514f233c84d5cc7cf3c2a10f4d1410b9b427308d22f5ad53
6
+ metadata.gz: 158675e5ddec87a8b277708887b037746e3f1573569edd5a8959eabdf5668b144553cbe6164844f2cf69bc603798b3b5b052697506dad8a29e8477afc62cc45f
7
+ data.tar.gz: 680efe5603de3649b7e09340a545d2e1df1e02697d34af9451869310e9f2a87bbd05423686bb7d40d60f104553f7311721870e52ac2171bb1491b3a8decaf439
data/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  # Rocket Job
2
- [![Gem Version](https://img.shields.io/gem/v/rocketjob.svg)](https://rubygems.org/gems/rocketjob) [![Build Status](https://travis-ci.org/rocketjob/rocketjob.svg?branch=master)](https://travis-ci.org/rocketjob/rocketjob) [![Downloads](https://img.shields.io/gem/dt/rocketjob.svg)](https://rubygems.org/gems/semantic_logger) [![License](https://img.shields.io/badge/license-Apache%202.0-brightgreen.svg)](http://opensource.org/licenses/Apache-2.0) ![](https://img.shields.io/badge/status-Production%20Ready-blue.svg) [![Support](https://img.shields.io/badge/IRC%20(gitter)-Support-brightgreen.svg)](https://gitter.im/rocketjob/support)
2
+ [![Gem Version](https://img.shields.io/gem/v/rocketjob.svg)](https://rubygems.org/gems/rocketjob) [![Downloads](https://img.shields.io/gem/dt/rocketjob.svg)](https://rubygems.org/gems/rocketjob) [![License](https://img.shields.io/badge/license-Apache%202.0-brightgreen.svg)](http://opensource.org/licenses/Apache-2.0) ![](https://img.shields.io/badge/status-Production%20Ready-blue.svg) [![Support](https://img.shields.io/badge/IRC%20(gitter)-Support-brightgreen.svg)](https://gitter.im/rocketjob/support)
3
3
 
4
4
  Ruby's missing batch system
5
5
 
@@ -17,11 +17,179 @@ Checkout https://rocketjob.io/
17
17
  * Questions? Join the chat room on Gitter for [rocketjob support](https://gitter.im/rocketjob/support)
18
18
  * [Report bugs](https://github.com/rocketjob/rocketjob/issues)
19
19
 
20
- ## Rocket Job 4
20
+ ## Rocket Job v6
21
21
 
22
- Rocket Job Pro is now open sourced and included within Rocket Job.
22
+ - Support for Ruby v3 and Rails 6.
23
+ - Major enhancements in Batch job support:
24
+ - Direct built-in Tabular support for all input and output categories.
25
+ - Multiple output file support, each with its own settings for:
26
+ - Compression
27
+ - GZip, Zip, BZip2 (Chunked for much faster loading into Apache Spark).
28
+ - Encryption
29
+ - PGP, Symmetric Encryption.
30
+ - File format
31
+ - CSV, PSV, JSON, Fixed Format, xlsx.
32
+ - Significant error handling improvements, especially around throttle failures
33
+ that used to result in "hanging" jobs.
34
+ - Support AWS DocumentDB in addition to MongoDB as the data store.
35
+ - Removed use of Symbols to meet Symbol deprecation in MongoDB and Mongoid.
23
36
 
24
- The `RocketJob::Batch` plugin now adds batch processing capabilites to break up a single task into many
37
+ ### Upgrading to Rocket Job v6
38
+
39
+ The following plugins have been deprecated and are no longer loaded by default.
40
+ - `RocketJob::Batch::Tabular::Input`
41
+ - `RocketJob::Batch::Tabular::Output`
42
+
43
+ If your code relies on these plugins and you still want to upgrade to Rocket Job v6,
44
+ add the following require statement to any jobs that still use them:
45
+
46
+ ~~~ruby
47
+ require "rocket_job/batch/tabular"
48
+ ~~~
49
+
50
+ It is important to migrate away from these plugins, since they will be removed in a future release.
51
+
52
+ #### Scheduled Jobs
53
+
54
+ For any scheduled jobs that include the `RocketJob::Plugins::Cron` plugin, the default behavior has changed
55
+ so that the scheduled job instance is created immediately after the currently scheduled instance starts.
56
+
57
+ To maintain the old behavior of creating the job when it fails, aborts, or completes, add the following line
58
+ to each of the applicable jobs:
59
+
60
+ ~~~ruby
61
+ self.cron_after_start = false
62
+ ~~~
63
+
64
+ Additionally, scheduled jobs will now prevent a new one from being created when another scheduled instance
65
+ of the same job is already queued, or running with the _same_ `cron_schedule`.
66
+
67
+ To maintain the old behavior of allowing multiple instances with the same cron schedule, add the following
68
+ line to each of the applicable jobs:
69
+
70
+ ~~~ruby
71
+ self.cron_singleton = false
72
+ ~~~
73
+
74
+ ##### Singleton
75
+
76
+ Since Scheduled jobs now implement their own singleton logic, remove the singleton plugin from any scheduled jobs.
77
+
78
+ #### Upgrading Batch Jobs to Rocket Job v6
79
+
80
+ Rocket Job v6 replaces the array of symbol type for `input_categories` and `output_categories`
81
+ with an array of `RocketJob::Category::Input` and `RocketJob::Category::Output`.
82
+
83
+ Jobs that added or modified the input or output categories need to be upgraded. For example:
84
+ ~~~ruby
85
+ class MyJob < RocketJob::Job
86
+ include RocketJob::Batch
87
+
88
+ self.output_categories = [:main, :errors, :ignored]
89
+ end
90
+ ~~~
91
+
92
+ Needs to be changed to:
93
+ ~~~ruby
94
+ class MyJob < RocketJob::Job
95
+ include RocketJob::Batch
96
+
97
+ output_category name: :main
98
+ output_category name: :errors
99
+ output_category name: :ignored
100
+ end
101
+ ~~~
102
+
103
+ ##### slice_size, encrypt, compress
104
+
105
+ These fields have been removed from the job itself:
106
+ ~~~ruby
107
+ class MyJob < RocketJob::Job
108
+ include RocketJob::Batch
109
+
110
+ self.slice_sice = 1_000
111
+ self.encrypt = true
112
+ self.compress = true
113
+ end
114
+ ~~~
115
+
116
+ They are now specified on the `input_category` as follows:
117
+ - `slice_size` just moves under `input_category`.
118
+ - `encrypt` becomes an option to `serializer`.
119
+ - `compress` is now the default for all batch jobs so is not needed.
120
+
121
+ If the serializer is set to `encrypt` then it is automatically compressed.
122
+
123
+ ~~~ruby
124
+ class MyJob < RocketJob::Job
125
+ include RocketJob::Batch
126
+
127
+ input_category slice_sice: 1_000, serializer: :encrypt
128
+ end
129
+ ~~~
130
+
131
+ ##### collect_output, collect_nil_output
132
+
133
+ The following fields have been moved from the job itself:
134
+ ~~~ruby
135
+ class MyJob < RocketJob::Job
136
+ include RocketJob::Batch
137
+
138
+ self.collect_output = true
139
+ self.collect_nil_output = true
140
+ end
141
+ ~~~
142
+
143
+ Into the corresponding `output_category`:
144
+ - `collect_output` no longer has any meaning. Output is collected anytime an `output_category` is defined.
145
+ - `collect_nil_output` is now the option `nils` on the `output_category.
146
+ It defaults to `false` so that by default any `nil` output from the `perform` method is not collected.
147
+ ~~~ruby
148
+ class MyJob < RocketJob::Job
149
+ include RocketJob::Batch
150
+
151
+ output_category nils: true
152
+ end
153
+ ~~~
154
+
155
+ ##### name
156
+
157
+ For both `input_category` and `output_category`, when the `name` argument is not supplied
158
+ it defaults to `:main`.
159
+
160
+ For Example:
161
+ ~~~ruby
162
+ class MyJob < RocketJob::Job
163
+ include RocketJob::Batch
164
+
165
+ input_category name: :main, serializer: :encrypt
166
+ output_category name: :main
167
+ end
168
+ ~~~
169
+
170
+ Is the same as:
171
+ ~~~ruby
172
+ class MyJob < RocketJob::Job
173
+ include RocketJob::Batch
174
+
175
+ input_category serializer: :encrypt
176
+ output_category
177
+ end
178
+ ~~~
179
+
180
+ ##### Existing and inflight jobs
181
+
182
+ When migrating to Rocket Job 6, it is recommended to load every job and then save it back again as part of the
183
+ deployment. When the job loads it will automatically convert itself from the old schema to the new v6 schema.
184
+
185
+ In flight jobs should not be affected, other than it is important to shutdown all running batch
186
+ servers _before_ running any new instances.
187
+
188
+ ## Rocket Job v4
189
+
190
+ Rocket Job Pro is now fully open source and included in Rocket Job under the Apache License.
191
+
192
+ The `RocketJob::Batch` plugin now adds batch processing capabilities to break up a single task into many
25
193
  concurrent workers processing slices of the entire job at the same time.
26
194
 
27
195
 
@@ -33,7 +201,9 @@ class MyJob < RocketJob::Job
33
201
 
34
202
  self.description = "Reverse names"
35
203
  self.destroy_on_complete = false
36
- self.collect_output = true
204
+
205
+ # Collect the output for this job in the default output category: `:main`
206
+ output_category
37
207
 
38
208
  # Method to call by all available workers at the same time.
39
209
  # Reverse the characters for each line:
@@ -2,7 +2,7 @@
2
2
  require "rocketjob_batch"
3
3
 
4
4
  # Log to console
5
- SemanticLogger.add_appender(io: STDOUT, formatter: :color)
5
+ SemanticLogger.add_appender(io: $stdout, formatter: :color)
6
6
 
7
7
  perf = RocketJob::Batch::Performance.new
8
8
  perf.parse(ARGV)
data/bin/rocketjob_perf CHANGED
@@ -2,7 +2,7 @@
2
2
  require "rocketjob"
3
3
 
4
4
  # Log to console
5
- SemanticLogger.add_appender(io: STDOUT, formatter: :color)
5
+ SemanticLogger.add_appender(io: $stdout, formatter: :color)
6
6
 
7
7
  perf = RocketJob::Performance.new
8
8
  perf.parse(ARGV)
@@ -0,0 +1,345 @@
1
+ require "active_support/concern"
2
+
3
+ module RocketJob
4
+ module Batch
5
+ module Categories
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ after_initialize :rocketjob_categories_assign, if: :new_record?
10
+ after_initialize :rocketjob_categories_migrate, unless: :new_record?
11
+ before_perform :rocketjob_categories_input_render
12
+ after_perform :rocketjob_categories_output_render
13
+
14
+ # List of categories that this job can load input data into
15
+ embeds_many :input_categories, class_name: "RocketJob::Category::Input"
16
+
17
+ # List of categories that this job can save output data into
18
+ embeds_many :output_categories, class_name: "RocketJob::Category::Output"
19
+
20
+ # Internal attributes
21
+ class_attribute :defined_input_categories, instance_accessor: false, instance_predicate: false
22
+ class_attribute :defined_output_categories, instance_accessor: false, instance_predicate: false
23
+
24
+ # For RJMC to be able to edit jobs
25
+ accepts_nested_attributes_for :input_categories, :output_categories
26
+ end
27
+
28
+ module ClassMethods
29
+ # Define a new input category
30
+ # @see RocketJob::Category::Input
31
+ def input_category(**args)
32
+ category = RocketJob::Category::Input.new(**args)
33
+ if defined_input_categories.nil?
34
+ self.defined_input_categories = [category]
35
+ else
36
+ rocketjob_categories_set(category, defined_input_categories)
37
+ end
38
+ end
39
+
40
+ # Define a new output category
41
+ # @see RocketJob::Category::Output
42
+ def output_category(**args)
43
+ category = RocketJob::Category::Output.new(**args)
44
+ if defined_output_categories.nil?
45
+ self.defined_output_categories = [category]
46
+ else
47
+ rocketjob_categories_set(category, defined_output_categories)
48
+ end
49
+ end
50
+
51
+ # Builds this job instance from the supplied properties hash that may contain input and output categories.
52
+ # Keeps the defaults and merges in settings without replacing existing categories.
53
+ def from_properties(properties)
54
+ return super(properties) unless properties.key?("input_categories") || properties.key?("output_categories")
55
+
56
+ properties = properties.dup
57
+ input_categories = properties.delete("input_categories")
58
+ output_categories = properties.delete("output_categories")
59
+ job = super(properties)
60
+ job.merge_input_categories(input_categories)
61
+ job.merge_output_categories(output_categories)
62
+ job
63
+ end
64
+
65
+ private
66
+
67
+ def rocketjob_categories_set(category, categories)
68
+ index = categories.find_index { |cat| cat.name == category.name }
69
+ index ? categories[index] = category : categories << category
70
+ category
71
+ end
72
+ end
73
+
74
+ def input_category(category_name = :main)
75
+ return category_name if category_name.is_a?(Category::Input)
76
+ raise(ArgumentError, "Cannot supply Output Category to input category") if category_name.is_a?(Category::Output)
77
+
78
+ category_name = category_name.to_sym
79
+ # find does not work against this association
80
+ input_categories.each { |category| return category if category.name == category_name }
81
+
82
+ unless category_name == :main
83
+ raise(
84
+ ArgumentError,
85
+ "Unknown Input Category: #{category_name.inspect}. Registered categories: #{input_categories.collect(&:name).join(',')}"
86
+ )
87
+ end
88
+
89
+ # Auto-register main input category when not defined
90
+ category = Category::Input.new(job: self)
91
+ self.input_categories << category
92
+ category
93
+ end
94
+
95
+ def output_category(category_name = :main)
96
+ return category_name if category_name.is_a?(Category::Output)
97
+ raise(ArgumentError, "Cannot supply Input Category to output category") if category_name.is_a?(Category::Input)
98
+
99
+ category_name = category_name.to_sym
100
+ # .find does not work against this association
101
+ output_categories.each { |category| return category if category.name == category_name }
102
+
103
+ raise(
104
+ ArgumentError,
105
+ "Unknown Output Category: #{category_name.inspect}. Registered categories: #{output_categories.collect(&:name).join(',')}"
106
+ )
107
+ end
108
+
109
+ # Returns [true|false] whether the named category has already been defined
110
+ def input_category?(category_name)
111
+ category_name = category_name.to_sym
112
+ # .find does not work against this association
113
+ input_categories.each { |catg| return true if catg.name == category_name }
114
+ false
115
+ end
116
+
117
+ def output_category?(category_name)
118
+ category_name = category_name.to_sym
119
+ # .find does not work against this association
120
+ output_categories.each { |catg| return true if catg.name == category_name }
121
+ false
122
+ end
123
+
124
+ def merge_input_categories(categories)
125
+ return if categories.blank?
126
+
127
+ categories.each do |properties|
128
+ category_name = (properties["name"] || properties[:name] || :main).to_sym
129
+ category = input_category(category_name)
130
+ properties.each { |key, value| category.public_send("#{key}=".to_sym, value) }
131
+ end
132
+ end
133
+
134
+ def merge_output_categories(categories)
135
+ return if categories.blank?
136
+
137
+ categories.each do |properties|
138
+ category_name = (properties["name"] || properties[:name] || :main).to_sym
139
+ category = output_category(category_name)
140
+ properties.each { |key, value| category.public_send("#{key}=".to_sym, value) }
141
+ end
142
+ end
143
+
144
+ private
145
+
146
+ def rocketjob_categories_assign
147
+ # Input categories defaults to :main if none was set in the class
148
+ if input_categories.empty?
149
+ self.input_categories =
150
+ if self.class.defined_input_categories
151
+ self.class.defined_input_categories.deep_dup
152
+ else
153
+ [RocketJob::Category::Input.new]
154
+ end
155
+ end
156
+
157
+ return if !self.class.defined_output_categories || !output_categories.empty?
158
+
159
+ # Input categories defaults to nil if none was set in the class
160
+ self.output_categories = self.class.defined_output_categories.deep_dup
161
+ end
162
+
163
+ # Render the output from the perform.
164
+ def rocketjob_categories_output_render
165
+ return if @rocket_job_output.nil?
166
+
167
+ # TODO: ..
168
+ return unless output_categories
169
+ return if output_categories.empty?
170
+
171
+ @rocket_job_output = rocketjob_categories_output_render_row(@rocket_job_output)
172
+ end
173
+
174
+ # Parse the input data before passing to the perform method
175
+ def rocketjob_categories_input_render
176
+ return if @rocket_job_input.nil?
177
+
178
+ @rocket_job_input = rocketjob_categories_input_render_row(@rocket_job_input)
179
+ end
180
+
181
+ def rocketjob_categories_input_render_row(row)
182
+ return if row.nil?
183
+
184
+ category = input_category
185
+ return row if category.nil? || !category.tabular?
186
+ return nil if row.blank?
187
+
188
+ tabular = category.tabular
189
+
190
+ # Return the row as-is if the required header has not yet been set.
191
+ if tabular.header?
192
+ raise(ArgumentError,
193
+ "The tabular header columns _must_ be set before attempting to parse data that requires it.")
194
+ end
195
+
196
+ tabular.record_parse(row)
197
+ end
198
+
199
+ def rocketjob_categories_output_render_row(row)
200
+ return if row.nil?
201
+
202
+ if row.is_a?(Batch::Result)
203
+ category = output_category(row.category)
204
+ row.value = category.tabular.render(row.value) if category.tabular?
205
+ return row
206
+ end
207
+
208
+ if row.is_a?(Batch::Results)
209
+ results = Batch::Results.new
210
+ row.each { |result| results << rocketjob_categories_output_render_row(result) }
211
+ return results
212
+ end
213
+
214
+ category = output_category
215
+ return row unless category.tabular?
216
+ return nil if row.blank?
217
+
218
+ category.tabular.render(row)
219
+ end
220
+
221
+ # Migrate existing v5 batch jobs to v6
222
+ def rocketjob_categories_migrate
223
+ return unless attribute_present?(:input_categories) && self[:input_categories]&.first.is_a?(Symbol)
224
+
225
+ serializer = :none
226
+ if attribute_present?(:compress)
227
+ serializer = :compress if self[:compress]
228
+ remove_attribute(:compress)
229
+ end
230
+
231
+ if attribute_present?(:encrypt)
232
+ serializer = :encrypt if self[:encrypt]
233
+ remove_attribute(:encrypt)
234
+ end
235
+
236
+ slice_size = 100
237
+ if attribute_present?(:slice_size)
238
+ slice_size = self[:slice_size].to_i
239
+ remove_attribute(:slice_size)
240
+ end
241
+
242
+ main_input_format = nil
243
+ main_input_mode = :line
244
+ main_input_columns = nil
245
+ # Only migrate tabular attributes if the job also removed the tabular plugin.
246
+ unless respond_to?(:tabular_input_render)
247
+ if attribute_present?(:tabular_input_format)
248
+ main_input_format = self[:tabular_input_format]
249
+ remove_attribute(:tabular_input_format)
250
+ end
251
+
252
+ if attribute_present?(:tabular_input_mode)
253
+ main_input_mode = self[:tabular_input_mode]
254
+ remove_attribute(:tabular_input_mode)
255
+ end
256
+
257
+ if attribute_present?(:tabular_input_header)
258
+ main_input_columns = self[:tabular_input_header]
259
+ remove_attribute(:tabular_input_header)
260
+ end
261
+ end
262
+
263
+ file_name = nil
264
+ if attribute_present?(:upload_file_name)
265
+ file_name = self[:upload_file_name]
266
+ remove_attribute(:upload_file_name)
267
+ end
268
+
269
+ existing = self[:input_categories]
270
+ self[:input_categories] = []
271
+ self[:input_categories] = existing.collect do |category_name|
272
+ RocketJob::Category::Input.new(
273
+ name: category_name,
274
+ file_name: file_name,
275
+ serializer: serializer,
276
+ slice_size: slice_size,
277
+ format: [:main, "main"].include?(category_name) ? main_input_format : nil,
278
+ columns: [:main, "main"].include?(category_name) ? main_input_columns : nil,
279
+ mode: [:main, "main"].include?(category_name) ? main_input_mode : nil
280
+ ).as_document
281
+ end
282
+
283
+ collect_output = false
284
+ if attribute_present?(:collect_output)
285
+ collect_output = self[:collect_output]
286
+ remove_attribute(:collect_output)
287
+ end
288
+
289
+ collect_nil_output = true
290
+ if attribute_present?(:collect_nil_output)
291
+ collect_nil_output = self[:collect_nil_output]
292
+ remove_attribute(:collect_nil_output)
293
+ end
294
+
295
+ main_output_format = nil
296
+ main_output_columns = nil
297
+ main_output_options = nil
298
+
299
+ # Only migrate tabular attributes if the job also removed the tabular plugin.
300
+ unless respond_to?(:tabular_output_render)
301
+ if attribute_present?(:tabular_output_format)
302
+ main_output_format = self[:tabular_output_format]
303
+ remove_attribute(:tabular_output_format)
304
+ end
305
+
306
+ if attribute_present?(:tabular_output_header)
307
+ main_output_columns = self[:tabular_output_header]
308
+ remove_attribute(:tabular_output_header)
309
+ end
310
+
311
+ if attribute_present?(:tabular_output_options)
312
+ main_output_options = self[:tabular_output_options]
313
+ remove_attribute(:tabular_output_options)
314
+ end
315
+ end
316
+
317
+ existing = self[:output_categories]
318
+ self[:output_categories] = []
319
+ if collect_output
320
+ if existing.blank?
321
+ self[:output_categories] = [
322
+ RocketJob::Category::Output.new(
323
+ nils: collect_nil_output,
324
+ format: main_output_format,
325
+ columns: main_output_columns,
326
+ format_options: main_output_options
327
+ ).as_document
328
+ ]
329
+ elsif existing.first.is_a?(Symbol)
330
+ self[:output_categories] = existing.collect do |category_name|
331
+ RocketJob::Category::Output.new(
332
+ name: category_name,
333
+ serializer: serializer,
334
+ nils: collect_nil_output,
335
+ format: [:main, "main"].include?(category_name) ? main_output_format : nil,
336
+ columns: [:main, "main"].include?(category_name) ? main_output_columns : nil,
337
+ format_options: [:main, "main"].include?(category_name) ? main_output_options : nil
338
+ ).as_document
339
+ end
340
+ end
341
+ end
342
+ end
343
+ end
344
+ end
345
+ end