worker_tools 0.1.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: f8781038bd01108da06e6c7449cb29faaa45fdd8
4
- data.tar.gz: eed78607ccda5d5b0ed7ad5030a42b8dc286fcea
2
+ SHA256:
3
+ metadata.gz: 2d73219c29b55493f0be76ce047dbe7f72259f4efaab15d1528230bdfc1920b5
4
+ data.tar.gz: a8ef47af79183aa6e6b465a0cbc6c7396ccccf3cb92574c386b956ebd3415056
5
5
  SHA512:
6
- metadata.gz: cc5633debc66182adff1af028ae0034937be1d369abbaabdbc731da5e792480db6074b602e951efaf4ec1889d0586fd175b44df4945eff8819d637cdfec171c8
7
- data.tar.gz: d6dcb02514729dcf459df1eaee5f40df1a792731336aea56a8b92541418163227b17144174285075e5365f98546c0a2607534c5b62845bee1e0982c94dd9bb39
6
+ metadata.gz: 30287518cdce7bb9536d802fffdaa54b893bfcc811b03f1c226ea59b45787c0f5b4090b82796984e5c77741679bd035620cd3e38aecb4ebb0f1dcac9de50cb16
7
+ data.tar.gz: 18c1c3ff22097f710cea838c6cf4a8058c35343d7f76312e063771ef2dc02c6ad0fbdace866c1b3dc9e21ab3545ea91f041ff16f4d540402be601c44c834d0dc
data/.travis.yml CHANGED
@@ -2,4 +2,5 @@ sudo: false
2
2
  language: ruby
3
3
  rvm:
4
4
  - 2.3.1
5
- before_install: gem install bundler -v 1.14.6
5
+ - 2.7.0
6
+ before_install: gem install bundler -v 2.1.4
data/CHANGELOG.md ADDED
@@ -0,0 +1,48 @@
1
+ # Changelog
2
+
3
+ ## [1.0.0] - 2022-05-20
4
+
5
+ Compared to 0.2.1
6
+
7
+ ### New
8
+
9
+ - Namespaced errors and non failure logic
10
+ - Support for status running and complete_with_warnings
11
+ - Benchmark wrapper
12
+ - Counters wrapper
13
+ - Notes instead of information field
14
+ - Filter for slack errors (`slack_error_notifiable`)
15
+ - Model attachments convention
16
+ - Complete specification of csv open arguments
17
+
18
+ ### BREAKING CHANGES
19
+
20
+ Instead of writing the final csv or xlsx to a folder, the gem assumes that the model provides an add_attachment method.
21
+
22
+ Both csv and xlsx output modules use entry hashes for content (`csv_output_entries`, `xlsx_output_entries`). The mapper methods `csv_output_row_values` and `xlsx_output_row_values` do not need (in most cases) to be defined, there is a default now. See the complete examples in the README.
23
+
24
+ These methods were renamed
25
+
26
+ - `xlsx_output_values` => `xlsx_output_row_values`
27
+ - `xlsx_insert_headers` => `xlsx_output_insert_headers`
28
+ - `xlsx_insert_rows` => ` xlsx_output_insert_rows`
29
+ - `xlsx_iterators` => `xlsx_output_iterators`
30
+ - `xlsx_style_columns` => `xlsx_output_style_columns`
31
+ - `xlsx_write_sheet` => `xlsx_output_write_sheet`
32
+
33
+ These methods were removed
34
+
35
+ - `add_info` in favor of `add_note`
36
+ - `create_model_if_not_available`, a model is always created.
37
+ - `format_log_message`, `format_info_message` in favor of `format_message`
38
+ - `csv_output_target`
39
+ - `cvs_output_target_folder`
40
+ - `csv_output_target_file_name`
41
+ - `csv_ouput_ensure_target_folder`
42
+ - `csv_output_write_target`
43
+ - `xlsx_output_target`
44
+ - `xlsx_output_target_folder`
45
+ - `xlsx_ensure_output_target_folder`
46
+ - `xlsx_write_output_target`
47
+
48
+ [1.0.0]: https://github.com/i22-digitalagentur/worker-tools/compare/0.2.1...1.0.0
data/README.md CHANGED
@@ -1,13 +1,53 @@
1
1
  # WorkerTools
2
2
 
3
- [![Build Status](https://travis-ci.org/i22-digitalagentur/worker-tools.svg?branch=master)](https://travis-ci.org/i22-digitalagentur/worker-tools)
3
+ [![Build Status][build-badge]][build-url]
4
+ [![MIT License][license-shield]][license-url]
5
+ [![Release][release-shield]][release-url]
6
+ ![Maintenance][maintained-shield]
7
+
8
+ <br>
9
+
10
+ <details open="open">
11
+ <summary>Table of Contents</summary>
12
+ <ol>
13
+ <li>
14
+ <a href="#about-the-project">About The Project</a>
15
+ </li>
16
+ <li>
17
+ <a href="#installation">Installation</a>
18
+ </li>
19
+ <li><a href="#conventions">Conventions</a></li>
20
+ <li><a href="#module-basics">Module 'Basics'</a></li>
21
+ <li><a href="#module-recorder">Module 'Recorder'</a></li>
22
+ <li><a href="#module-slackerrornotifier">Module 'SlackErrorNotifier'</a></li>
23
+ <li><a href="#wrappers">Wrappers</a></li>
24
+ <li><a href="#module-notes">Module 'Notes'</a></li>
25
+ <li><a href="#attachments">Attachments</a></li>
26
+ <li>
27
+ <a href="#complete-examples">Complete Examples</a>
28
+ <ul>
29
+ <li><a href="#xlsx-input-example">XLSX Input Example</a></li>
30
+ <li><a href="#csv-input-example">CSV Input Example</a></li>
31
+ <li><a href="#csv-output-example">CSV Output Example</a></li>
32
+ <li><a href="#xlsx-output-example">XLSX Output Example</a></li>
33
+ </ul>
34
+ </li>
35
+ <li><a href="#changelog">Changelog</a></li>
36
+ <li><a href="#requirements">Requirements</a></li>
37
+ <li><a href="#contributing">Contributing</a></li>
38
+ <li><a href="#license">License</a></li>
39
+ <li><a href="#acknowledgement">Acknowledgement</a></li>
40
+ </ol>
41
+ </details>
42
+
43
+ ## About The Project
4
44
 
5
45
  WorkerTools is a collection of modules meant to speed up how we write background tasks following a few basic patterns. The structure of plain independent modules with limited abstraction allows to define and override a few methods according to your needs without requiring a deep investment in the library.
6
46
 
7
47
  These modules provide some features and conventions to address the following points with little configuration on your part:
8
48
 
9
49
  - How to save the state the task.
10
- - How to save information relevant to the admins / customers.
50
+ - How to save notes relevant to the admins / customers.
11
51
  - How to log the details
12
52
  - How to handle exceptions and send notifications
13
53
  - How to process CSV files (as input and output)
@@ -32,13 +72,20 @@ Or install it yourself as:
32
72
 
33
73
  ## Conventions
34
74
 
35
- Most of the modules require an ActiveRecord model to keep track of the state, information, and files related to the job. The class of this model is typically an Import, Export, Report.. or something more generic like a JobEntry.
75
+ Most of the modules require an ActiveRecord model to keep track of the state, notes, and files related to the job. The class of this model is typically an Import, Export, Report.. or something more generic like a JobEntry.
36
76
 
37
77
  An example of this model for an Import using Paperclip would be something like this:
38
78
 
39
79
  ```ruby
40
80
  class Import < ApplicationRecord
41
- enum state: { waiting: 0, complete: 1, failed: 2 }
81
+ enum state: %w[
82
+ waiting
83
+ complete
84
+ complete_with_warnings
85
+ failed
86
+ running
87
+ ].map { |e| [e, e] }.to_h
88
+
42
89
  enum kind: { foo: 0, bar: 1 }
43
90
 
44
91
  has_attached_file :attachment
@@ -48,7 +95,9 @@ class Import < ApplicationRecord
48
95
  end
49
96
  ```
50
97
 
51
- The state `complete` and `failed` are used by the modules. Both `state` and `kind` could be an enum or just a string field. Whether you have one, none or many attachments, and which library you use to handle it's up to you.
98
+ The state `complete` and `failed` are used by the modules. Both `state` and `kind` could be an enum or just a string field. Whether you have one, none or many attachments, and which library you use to handle it's up to you.
99
+
100
+ The state `complete_with_warnings` indicates that the model contains notes that did not lead to a failure but should get some attention. By default those levels are `warning` and `errors` and can be customized.
52
101
 
53
102
  In this case the migration would be something like this:
54
103
 
@@ -56,9 +105,10 @@ In this case the migration would be something like this:
56
105
  def change
57
106
  create_table :imports do |t|
58
107
  t.integer :kind, null: false
59
- t.integer :state, default: 0, null: false
60
- t.text :information
108
+ t.string :state, default: 'waiting', null: false
109
+ t.json :notes, default: []
61
110
  t.json :options, default: {}
111
+ t.json :meta, default: {}
62
112
 
63
113
  t.string :attachment_file_name
64
114
  t.integer :attachment_file_size
@@ -66,7 +116,7 @@ In this case the migration would be something like this:
66
116
 
67
117
  t.timestamps
68
118
  end
69
- ```
119
+ ```
70
120
 
71
121
  ## Module 'Basics'
72
122
 
@@ -95,12 +145,13 @@ end
95
145
 
96
146
  The basics module contains a `perform` method, which is the usual entry point for ApplicationJob and Sidekiq. It can receive the id of the model, the model instance, or nothing, in which case it will attempt to create this model on its own.
97
147
 
148
+ By default errors subclassed from WorkerTools::Errors::Invalid (such as those related to wrong headers in the input modules) will not raise and mark the model as failed. The method `non_failure_error?` lets you modifiy this behaviour.
98
149
 
99
150
  ## Module 'Recorder'
100
151
 
101
- Provides some methods to manage a log and the `information` field of the model. The main methods are `add_info`, `add_log`, and `record` (which both logs and appends the message to the information field). See all methods in [recorder](/lib/worker_tools/recorder.rb)
152
+ Provides some methods to manage a log and the `notes` field of the model. The main methods are `add_info`, `add_log`, and `record` (which both logs and appends the message to the notes field). See all methods in [recorder](/lib/worker_tools/recorder.rb)
102
153
 
103
- This module has a _recoder_ wrapper that will register the exception details into the log and information field in case of error:
154
+ This module has a _recoder_ wrapper that will register the exception details into the log and notes field in case of error:
104
155
 
105
156
  ```ruby
106
157
  class MyImporter
@@ -124,30 +175,45 @@ If you only want the logger functions, without worrying about persisting a model
124
175
  end
125
176
  ```
126
177
 
127
- ## Module RocketchatErrorNotifier
178
+ ## Module SlackErrorNotifier
128
179
 
129
- [rocketchat_error_notifier](/lib/worker_tools/rocketchat_error_notifier.rb)
180
+ Provides a Slack error notifier wrapper. To do this, you need to define SLACK_NOTIFIER_WEBHOOK as well as SLACK_NOTIFIER_CHANNEL. Then you need to include the SlackErrorNotifier module in your class and append slack_error_notifier to your wrappers. Below you can see an example.
130
181
 
131
- ## Module SlackErrorNotifier
182
+ ```ruby
183
+ class MyImporter
184
+ include WorkerTools::SlackErrorNotifier
185
+
186
+ wrappers :slack_error_notifier
187
+
188
+ def perform
189
+ with_wrapper_logger do
190
+ # do stuff
191
+ end
192
+ end
193
+ end
194
+ ```
132
195
 
133
- [slack_error_notifier](/lib/worker_tools/slack_error_notifier.rb)
196
+ See all methods in [slack_error_notifier](/lib/worker_tools/slack_error_notifier.rb)
134
197
 
135
198
  ## Module CSV Input
136
199
 
137
- [csv_input](/lib/worker_tools/csv_input.rb)
200
+ See all methods in [csv_input](/lib/worker_tools/csv_input.rb)
138
201
 
139
202
  ## Module CSV Output
140
203
 
141
- [csv_output](/lib/worker_tools/csv_output.rb)
204
+ See all methods in [csv_output](/lib/worker_tools/csv_output.rb)
142
205
 
143
206
  ## Module XLSX Input
144
207
 
145
- [xlsx_input](/lib/worker_tools/xlsx_input.rb)
208
+ See all methods in [xlsx_input](/lib/worker_tools/xlsx_input.rb)
146
209
 
210
+ ## Module XLSX Output
211
+
212
+ See all methods in [xlsx_output](/lib/worker_tools/xlsx_output.rb)
147
213
 
148
214
  ## Wrappers
149
215
 
150
- In the [basics module](/lib/worker_tools/basics.rb), `perform` calls your custom method `run` to do the actual work of the task, and wraps it around any methods expecting a block that you might have had defined using `wrappers`. That gives us a systematic way to add logic depending on the output of `run` and any exceptions that might arise, such as logging the error and context, sending a chat notification, retrying under some circumstances, etc.
216
+ In the [basics module](/lib/worker_tools/basics.rb), `perform` calls your custom method `run` to do the actual work of the task, and wraps it around any methods expecting a block that you might have had defined using `wrappers`. That gives us a systematic way to add logic depending on the output of `run` and any exceptions that might arise, such as logging the error and context, sending a chat notification, retrying under some circumstances, etc.
151
217
 
152
218
  The following code
153
219
 
@@ -203,13 +269,285 @@ def perform(model_id)
203
269
  end
204
270
  ```
205
271
 
272
+ ## Counter
273
+
274
+ There is a counter wrapper that you can use to add custom counters to the meta attribute. To do this, you need to complete the following tasks:
275
+
276
+ - include WorkerTools::Counters to your class
277
+ - add :counters to the wrappers method props
278
+ - call counters method with your custom counters
279
+ You can see an example below. After that, you can access your custom counters via the meta attribute.
280
+
281
+ ```ruby
282
+ class MyImporter
283
+ include WorkerTools::Counters
284
+ wrappers :counters
285
+ counters :foo, :bar
286
+
287
+ def run
288
+ example_foo_counter_methods
289
+ end
290
+
291
+ def example_foo_counter_methods
292
+ # you can use the increment helper
293
+ 10.times { increment_foo }
294
+
295
+ # the counter works like a regular accessor, you can read it and modify it
296
+ # directly
297
+ self.bar = 100
298
+ puts bar # => 100
299
+ end
300
+
301
+ # ..
302
+ end
303
+ ```
304
+
305
+ ## Benchmark
306
+
307
+ There is a benchmark wrapper that you can use to record the benchmark. The only thing you need to do is to include the benchmark module and append the name to the wrapper array. Below you can see an example of the integration.
308
+
309
+ ```ruby
310
+ class MyImporter
311
+ include WorkerTools::Benchmark
312
+ wrappers :benchmark
313
+
314
+ def run
315
+ # do stuff
316
+ end
317
+
318
+ # ..
319
+ end
320
+ ```
321
+
322
+ ## Module 'Notes'
323
+
324
+ If you use ActiveRecord you may need to modify the serializer as well as deserializer from the note attribute. After that you can easily serialize hashes and array of hashes with indifferent access. For that purpose the gem provides two utility methods. (HashWithIndifferentAccessType, SerializedArrayType). There is an example of how you can use it.
325
+
326
+ ```ruby
327
+ class ServiceTask < ApplicationRecord
328
+
329
+ attribute :notes, SerializedArrayType.new(type: HashWithIndifferentAccessType.new)
330
+ end
331
+ ```
332
+
333
+ See all methods in [utils](/lib/worker_tools/utils)
334
+
335
+ ## Attachments
336
+
337
+ The modules that generate a file expect the model to provide an `add_attachment` method with following signature:
338
+
339
+ ```ruby
340
+ def add_attachment(file, file_name: nil, content_type: nil)
341
+ # your logic
342
+ end
343
+ ```
344
+
345
+ You can skip this convention by overwriting the module related method, for example after including `CsvOutput`
346
+
347
+ ```ruby
348
+ def csv_output_add_attachment
349
+ # default implementation
350
+ # model.add_attachment(csv_output_tmp_file, file_name: csv_output_file_name, content_type: 'text/csv')
351
+
352
+ # your method
353
+ ftp_upload(csv_output_tmp_file)
354
+ end
355
+ ```
356
+
357
+ ## Complete Examples
358
+
359
+ ### XLSX Input Example
360
+
361
+ ```ruby
362
+ class XlsxInputExample
363
+ include Sidekiq::Worker
364
+ include WorkerTools::Basics
365
+ include WorkerTools::Recorder
366
+ include WorkerTools::XlsxInput
367
+
368
+ wrappers %i[basics recorder]
369
+
370
+ def model_class
371
+ Import
372
+ end
373
+
374
+ def model_kind
375
+ 'xlsx_input_example'
376
+ end
377
+
378
+ def run
379
+ xlsx_input_foreach.each { |row| SomeModel.create!(row) }
380
+ end
381
+
382
+ def xlsx_input_columns
383
+ {
384
+ foo: 'Your Foo',
385
+ bar: 'Your Bar'
386
+ }
387
+ end
388
+ end
389
+ ```
390
+
391
+ ### CSV Input Example
392
+
393
+ ```ruby
394
+ class CsvInputExample
395
+ include Sidekiq::Worker
396
+ include WorkerTools::Basics
397
+ include WorkerTools::Recorder
398
+ include WorkerTools::CsvInput
399
+
400
+ wrappers %i[basics recorder]
401
+
402
+ def model_class
403
+ Import
404
+ end
405
+
406
+ def model_kind
407
+ 'csv_input_example'
408
+ end
409
+
410
+ def csv_input_columns
411
+ {
412
+ flavour: 'Flavour',
413
+ number: 'Number'
414
+ }
415
+ end
416
+
417
+ def run
418
+ csv_input_foreach.map { |row| do_something row_to_attributes(row) }
419
+ end
420
+
421
+ def row_to_attributes(row)
422
+ {
423
+ flavour: row['flavour'].downcase,
424
+ number: row['number'].to_i * 10
425
+ }
426
+ end
427
+ end
428
+ ```
429
+
430
+ ### CSV Output Example
431
+
432
+ ```ruby
433
+ # More complex example with CsvOutput
434
+ class CsvOutputExample
435
+ include Sidekiq::Worker
436
+ include WorkerTools::Basics
437
+ include WorkerTools::CsvOutput
438
+ include WorkerTools::Recorder
439
+
440
+ wrappers %i[basics recorder]
441
+
442
+ def model_class
443
+ Report
444
+ end
445
+
446
+ def model_kind
447
+ 'csv_out_example'
448
+ end
449
+
450
+ def model_file_name
451
+ "#{model_kind}-#{Date.current}.csv"
452
+ end
453
+
454
+ def run
455
+ csv_output_write_file
456
+ end
457
+
458
+ def csv_output_column_headers
459
+ @csv_output_column_headers ||= {
460
+ foo: 'Foo',
461
+ bar: 'Bar'
462
+ }
463
+ end
464
+
465
+ def csv_output_entries
466
+ @csv_output_entries ||= User.includes(...).lazy.map do |user|
467
+ {
468
+ foo: user.foo,
469
+ bar: user.bar
470
+ }
471
+ end
472
+ end
473
+
474
+ end
475
+ ```
476
+
477
+ ### XLSX Output Example
478
+
479
+ ```ruby
480
+ # ExampleXlsxOutput
481
+ class XlsxOutputExample
482
+ include Sidekiq::Worker
483
+ include WorkerTools::Basics
484
+ include WorkerTools::Recorder
485
+ include WorkerTools::XlsxOutput
486
+
487
+ wrappers %i[basics recorder]
488
+
489
+ def model_class
490
+ Export
491
+ end
492
+
493
+ def model_kind
494
+ 'xlsx_output_example'
495
+ end
496
+
497
+ def run
498
+ xlsx_output_write_file
499
+ end
500
+
501
+ def xlsx_output_column_headers
502
+ @xlsx_output_column_headers ||= {
503
+ foo: 'Foo',
504
+ bar: 'Bar'
505
+ }
506
+ end
507
+
508
+ def xlsx_output_entries
509
+ @xlsx_output_entries ||= SomeArray.map do |entry|
510
+ {
511
+ foo: user.foo,
512
+ bar: user.bar
513
+ }
514
+ end
515
+ end
516
+ end
517
+ ```
518
+
519
+ ## Changelog
520
+
521
+ See [CHANGELOG](CHANGELOG.md)
522
+
523
+ ## Requirements
524
+
525
+ - ruby > 2.3.1
206
526
 
207
527
  ## Contributing
208
528
 
209
- Bug reports and pull requests are welcome on GitHub at https://github.com/i22-digitalagentur/worker_tools.
529
+ Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are **greatly appreciated**.
210
530
 
531
+ 1. Fork the Project
532
+ 2. Create your Feature Branch (`git checkout -b feature/new_feature`)
533
+ 3. Commit your Changes (`git commit -m 'feat: Add new feature'`)
534
+ 4. Push to the Branch (`git push origin feature/new_feature`)
535
+ 5. Open a Pull Request
211
536
 
212
537
  ## License
213
538
 
214
- The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
539
+ The gem is available under the MIT License. See `LICENSE` for more information.
540
+
541
+ ## Acknowledgement
542
+
543
+ - [Img Shields](https://shields.io)
544
+
545
+ <!--shield-styles-->
215
546
 
547
+ [build-badge]: https://travis-ci.org/i22-digitalagentur/worker-tools.svg?branch=master
548
+ [build-url]: https://travis-ci.org/i22-digitalagentur/worker-tools
549
+ [maintained-shield]: https://img.shields.io/badge/Maintained%3F-yes-green.svg?style=flat
550
+ [release-shield]: https://img.shields.io/github/release/i22-digitalagentur/coverage-badge-creator.svg?style=flat
551
+ [release-url]: https://github.com/i22-digitalagentur/worker-tools/releases/
552
+ [license-shield]: https://img.shields.io/badge/License-MIT-yellow.svg?style=flat
553
+ [license-url]: https://github.com/i22-digitalagentur/worker-tools/blob/master/LICENSE
@@ -6,7 +6,6 @@ module WorkerTools
6
6
 
7
7
  included do
8
8
  attr_writer :model
9
- attr_accessor :information
10
9
 
11
10
  def self.wrappers(*args)
12
11
  @wrappers ||= args.flatten
@@ -33,6 +32,7 @@ module WorkerTools
33
32
 
34
33
  def perform(model_id = nil)
35
34
  @model_id = model_id
35
+
36
36
  with_wrappers(wrapper_methods) do
37
37
  run
38
38
  end
@@ -42,32 +42,36 @@ module WorkerTools
42
42
  self.class.read_wrappers.map do |wrapper|
43
43
  symbolized_method = "with_wrapper_#{wrapper}".to_sym
44
44
  raise "Missing wrapper #{wrapper}" unless respond_to?(symbolized_method)
45
+
45
46
  symbolized_method
46
47
  end
47
48
  end
48
49
 
49
50
  def with_wrapper_basics(&block)
51
+ save_state_without_validate('running')
50
52
  block.yield
51
53
  finalize
52
54
  # this time we do want to catch Exception to attempt to handle some of the
53
55
  # critical errors.
54
56
  # rubocop:disable Lint/RescueException
55
- rescue Exception
57
+ rescue Exception => e
58
+ return finalize if non_failure_error?(e)
59
+
56
60
  # rubocop:enable Lint/RescueException
57
- model.state = 'failed'
58
- model.save!(validate: false)
61
+ save_state_without_validate('failed')
59
62
  raise
60
63
  end
61
64
 
62
65
  def finalize
63
- model.update_attributes!(
64
- state: 'complete',
65
- information: information
66
- )
66
+ mark_with_warnings = model.notes.any? do |note|
67
+ complete_with_warnings_note_levels.include?(note.with_indifferent_access[:level].to_s)
68
+ end
69
+
70
+ model.update!(state: mark_with_warnings ? :complete_with_warnings : :complete)
67
71
  end
68
72
 
69
- def create_model_if_not_available
70
- false
73
+ def complete_with_warnings_note_levels
74
+ %w[error warning]
71
75
  end
72
76
 
73
77
  def model
@@ -76,17 +80,29 @@ module WorkerTools
76
80
 
77
81
  def with_wrappers(wrapper_symbols, &block)
78
82
  return yield if wrapper_symbols.blank?
83
+
79
84
  current_wrapper_symbol = wrapper_symbols.shift
80
85
  send(current_wrapper_symbol) { with_wrappers(wrapper_symbols, &block) }
81
86
  end
82
87
 
88
+ def non_failure_error?(error)
89
+ error.is_a?(WorkerTools::Errors::Invalid)
90
+ # or add your list
91
+ # [WorkerTools::Errors::Invalid, SomeOtherError].any? { |k| e.is_a?(k) }
92
+ end
93
+
83
94
  private
84
95
 
96
+ def save_state_without_validate(state)
97
+ model.state = state
98
+ model.save!(validate: false)
99
+ end
100
+
85
101
  def find_model
86
102
  @model_id ||= nil
87
103
  return @model_id if @model_id.is_a?(model_class)
88
104
  return model_class.find(@model_id) if @model_id
89
- raise 'Model not available' unless create_model_if_not_available
105
+
90
106
  t = model_class.new
91
107
  t.kind = model_kind if t.respond_to?(:kind=)
92
108
  t.save!(validate: false)
@@ -0,0 +1,15 @@
1
+ module WorkerTools
2
+ module Benchmark
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ attr_accessor :benchmark
7
+
8
+ def with_wrapper_benchmark(&block)
9
+ @benchmark = ::Benchmark.measure(&block)
10
+
11
+ model.meta['duration'] = @benchmark.real.round if model.respond_to?(:meta)
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,40 @@
1
+ module WorkerTools
2
+ module Counters
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ def self.counters(*args)
7
+ @counters ||= args.flatten
8
+ add_counter_methods
9
+ end
10
+
11
+ def self.read_counters
12
+ @counters || []
13
+ end
14
+
15
+ def self.add_counter_methods
16
+ @counters.each do |name|
17
+ # ex `inserts`
18
+ define_method(name) { model.meta[name] }
19
+
20
+ # ex `inserts=`
21
+ define_method("#{name}=") { |value| model.meta[name] = value }
22
+
23
+ # ex `increment_inserts`
24
+ define_method("increment_#{name}") { model.meta[name] += 1 }
25
+ end
26
+ end
27
+
28
+ def with_wrapper_counters(&block)
29
+ reset_counters
30
+ block.call
31
+ end
32
+
33
+ def reset_counters
34
+ self.class.read_counters.each do |name|
35
+ model.meta[name] = 0
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end