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 +5 -5
- data/.travis.yml +2 -1
- data/CHANGELOG.md +48 -0
- data/README.md +358 -20
- data/lib/worker_tools/basics.rb +27 -11
- data/lib/worker_tools/benchmark.rb +15 -0
- data/lib/worker_tools/counters.rb +40 -0
- data/lib/worker_tools/csv_input.rb +13 -4
- data/lib/worker_tools/csv_output.rb +19 -33
- data/lib/worker_tools/errors.rb +10 -0
- data/lib/worker_tools/recorder.rb +20 -12
- data/lib/worker_tools/slack_error_notifier.rb +6 -2
- data/lib/worker_tools/utils/hash_with_indifferent_access_type.rb +9 -0
- data/lib/worker_tools/utils/serialized_array_type.rb +19 -0
- data/lib/worker_tools/version.rb +1 -1
- data/lib/worker_tools/xlsx_input.rb +14 -4
- data/lib/worker_tools/xlsx_output.rb +51 -43
- data/lib/worker_tools.rb +1 -1
- data/worker_tools.gemspec +3 -3
- metadata +15 -25
- data/lib/worker_tools/rocketchat_error_notifier.rb +0 -68
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2d73219c29b55493f0be76ce047dbe7f72259f4efaab15d1528230bdfc1920b5
|
4
|
+
data.tar.gz: a8ef47af79183aa6e6b465a0cbc6c7396ccccf3cb92574c386b956ebd3415056
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 30287518cdce7bb9536d802fffdaa54b893bfcc811b03f1c226ea59b45787c0f5b4090b82796984e5c77741679bd035620cd3e38aecb4ebb0f1dcac9de50cb16
|
7
|
+
data.tar.gz: 18c1c3ff22097f710cea838c6cf4a8058c35343d7f76312e063771ef2dc02c6ad0fbdace866c1b3dc9e21ab3545ea91f041ff16f4d540402be601c44c834d0dc
|
data/.travis.yml
CHANGED
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]
|
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
|
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,
|
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:
|
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.
|
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.
|
60
|
-
t.
|
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 `
|
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
|
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
|
178
|
+
## Module SlackErrorNotifier
|
128
179
|
|
129
|
-
|
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
|
-
|
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`.
|
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
|
-
|
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
|
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
|
data/lib/worker_tools/basics.rb
CHANGED
@@ -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
|
-
|
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.
|
64
|
-
|
65
|
-
|
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
|
70
|
-
|
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
|
-
|
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
|