checker_jobs 0.1.0.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.circleci/config.yml +48 -0
- data/.gitignore +8 -0
- data/.rspec +1 -0
- data/.rubocop.yml +61 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +207 -0
- data/LICENSE.txt +21 -0
- data/README.md +158 -0
- data/Rakefile +10 -0
- data/bin/console +14 -0
- data/bin/pronto-ci +2 -0
- data/bin/setup +8 -0
- data/checker_jobs.gemspec +35 -0
- data/examples/user_checker.rb +66 -0
- data/lib/checker_jobs/base.rb +8 -0
- data/lib/checker_jobs/checks/base.rb +19 -0
- data/lib/checker_jobs/checks/ensure_fewer.rb +12 -0
- data/lib/checker_jobs/checks/ensure_more.rb +12 -0
- data/lib/checker_jobs/checks/ensure_no.rb +14 -0
- data/lib/checker_jobs/checks.rb +6 -0
- data/lib/checker_jobs/configuration.rb +41 -0
- data/lib/checker_jobs/dsl.rb +73 -0
- data/lib/checker_jobs/emails_backends/action_mailer.rb +37 -0
- data/lib/checker_jobs/emails_backends/default_formatter.rb +54 -0
- data/lib/checker_jobs/emails_backends.rb +4 -0
- data/lib/checker_jobs/errors.rb +37 -0
- data/lib/checker_jobs/jobs_processors/sidekiq.rb +45 -0
- data/lib/checker_jobs/jobs_processors.rb +3 -0
- data/lib/checker_jobs/version.rb +3 -0
- data/lib/checker_jobs.rb +15 -0
- metadata +243 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 569627ae057bbdab3fb84673e85d6ce258eaf9d2
|
|
4
|
+
data.tar.gz: 42a55e7dddba3a9f38e1e30253b8b97fed79b0b6
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 1b6c2a2411701bca8753e401f86a303c9fb386355bd5a109786b0d566ba0822e9f9c67137e87d3aa31b7b3ac2cf27276848198bd3c6634ea9d984be806794cf2
|
|
7
|
+
data.tar.gz: 5260c70caea4009c8b297fccc6859de4616d470c8b0a8322f93e6d22bc1589b9189b25a6efa4df00171ab8cdcec2f57e5c9eb3fe1074db24163c35ce64676559
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
jobs:
|
|
3
|
+
build:
|
|
4
|
+
docker:
|
|
5
|
+
- image: drivy/rails-ci:0.1.2
|
|
6
|
+
steps:
|
|
7
|
+
- checkout
|
|
8
|
+
|
|
9
|
+
- run: git fetch origin --depth=1000
|
|
10
|
+
|
|
11
|
+
# Install CodeClimate test reporter binary, rebuild without cache to update the binary
|
|
12
|
+
- restore_cache:
|
|
13
|
+
key: cc_test_reporter
|
|
14
|
+
- run:
|
|
15
|
+
name: Install Code Climate test reporter
|
|
16
|
+
command: |
|
|
17
|
+
[ -f bin/cc-test-reporter ] || curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./bin/cc-test-reporter
|
|
18
|
+
chmod +x bin/cc-test-reporter
|
|
19
|
+
- restore_cache:
|
|
20
|
+
key: cc_test_reporter
|
|
21
|
+
paths:
|
|
22
|
+
- bin/cc-test-reporter
|
|
23
|
+
|
|
24
|
+
- run:
|
|
25
|
+
name: "Creating a Gemfile.lock without bundler's version"
|
|
26
|
+
command: "head -n -3 Gemfile.lock > Gemfile.lock.no-version"
|
|
27
|
+
|
|
28
|
+
# Bundle install with cache, Ignore bundler version
|
|
29
|
+
- restore_cache:
|
|
30
|
+
key: checker_jobs-{{ checksum "Gemfile.lock.no-version"}}
|
|
31
|
+
- run:
|
|
32
|
+
name: Install Ruby dependencies
|
|
33
|
+
command: bundle install --path vendor/bundle --jobs=`nproc`
|
|
34
|
+
- save_cache:
|
|
35
|
+
key: checker_jobs-{{ checksum "Gemfile.lock.no-version"}}
|
|
36
|
+
paths:
|
|
37
|
+
- vendor/bundle
|
|
38
|
+
|
|
39
|
+
- run:
|
|
40
|
+
name: RSpec
|
|
41
|
+
command: |
|
|
42
|
+
./bin/cc-test-reporter before-build
|
|
43
|
+
bundle exec rspec --format documentation
|
|
44
|
+
./bin/cc-test-reporter after-build --exit-code $?
|
|
45
|
+
|
|
46
|
+
- run:
|
|
47
|
+
name: Pronto
|
|
48
|
+
command: bin/pronto-ci
|
data/.gitignore
ADDED
data/.rspec
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
--require spec_helper
|
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
require: rubocop-rspec
|
|
2
|
+
|
|
3
|
+
AllCops:
|
|
4
|
+
TargetRubyVersion: 2.3
|
|
5
|
+
Exclude:
|
|
6
|
+
- "examples/*.rb"
|
|
7
|
+
|
|
8
|
+
#
|
|
9
|
+
# Opinionated cops
|
|
10
|
+
#
|
|
11
|
+
|
|
12
|
+
Layout/DotPosition:
|
|
13
|
+
EnforcedStyle: trailing
|
|
14
|
+
|
|
15
|
+
Layout/IndentHash:
|
|
16
|
+
EnforcedStyle: consistent
|
|
17
|
+
|
|
18
|
+
Metrics/AbcSize:
|
|
19
|
+
Max: 20
|
|
20
|
+
|
|
21
|
+
Metrics/LineLength:
|
|
22
|
+
Max: 100
|
|
23
|
+
|
|
24
|
+
Style/ClassAndModuleChildren:
|
|
25
|
+
EnforcedStyle: compact
|
|
26
|
+
|
|
27
|
+
Style/StringLiterals:
|
|
28
|
+
EnforcedStyle: double_quotes
|
|
29
|
+
|
|
30
|
+
Style/TrailingCommaInArrayLiteral:
|
|
31
|
+
EnforcedStyleForMultiline: comma
|
|
32
|
+
|
|
33
|
+
Style/TrailingCommaInHashLiteral:
|
|
34
|
+
EnforcedStyleForMultiline: comma
|
|
35
|
+
|
|
36
|
+
#
|
|
37
|
+
# Disabled cops
|
|
38
|
+
#
|
|
39
|
+
|
|
40
|
+
Style/FrozenStringLiteralComment:
|
|
41
|
+
Enabled: false
|
|
42
|
+
|
|
43
|
+
Style/BracesAroundHashParameters:
|
|
44
|
+
Enabled: false
|
|
45
|
+
|
|
46
|
+
Style/FormatString:
|
|
47
|
+
Enabled: false
|
|
48
|
+
|
|
49
|
+
Style/Documentation:
|
|
50
|
+
Enabled: false
|
|
51
|
+
|
|
52
|
+
Style/BlockComments:
|
|
53
|
+
Exclude:
|
|
54
|
+
- "spec/spec_helper.rb"
|
|
55
|
+
|
|
56
|
+
Metrics/BlockLength:
|
|
57
|
+
Exclude:
|
|
58
|
+
- "spec/**/*_spec.rb"
|
|
59
|
+
|
|
60
|
+
Style/Proc:
|
|
61
|
+
Enabled: false
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
checker_jobs (0.1.0)
|
|
5
|
+
|
|
6
|
+
GEM
|
|
7
|
+
remote: https://rubygems.org/
|
|
8
|
+
specs:
|
|
9
|
+
actionmailer (5.1.4)
|
|
10
|
+
actionpack (= 5.1.4)
|
|
11
|
+
actionview (= 5.1.4)
|
|
12
|
+
activejob (= 5.1.4)
|
|
13
|
+
mail (~> 2.5, >= 2.5.4)
|
|
14
|
+
rails-dom-testing (~> 2.0)
|
|
15
|
+
actionpack (5.1.4)
|
|
16
|
+
actionview (= 5.1.4)
|
|
17
|
+
activesupport (= 5.1.4)
|
|
18
|
+
rack (~> 2.0)
|
|
19
|
+
rack-test (>= 0.6.3)
|
|
20
|
+
rails-dom-testing (~> 2.0)
|
|
21
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
|
22
|
+
actionview (5.1.4)
|
|
23
|
+
activesupport (= 5.1.4)
|
|
24
|
+
builder (~> 3.1)
|
|
25
|
+
erubi (~> 1.4)
|
|
26
|
+
rails-dom-testing (~> 2.0)
|
|
27
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
|
28
|
+
activejob (5.1.4)
|
|
29
|
+
activesupport (= 5.1.4)
|
|
30
|
+
globalid (>= 0.3.6)
|
|
31
|
+
activesupport (5.1.4)
|
|
32
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
33
|
+
i18n (~> 0.7)
|
|
34
|
+
minitest (~> 5.1)
|
|
35
|
+
tzinfo (~> 1.1)
|
|
36
|
+
addressable (2.5.2)
|
|
37
|
+
public_suffix (>= 2.0.2, < 4.0)
|
|
38
|
+
ast (2.4.0)
|
|
39
|
+
builder (3.2.3)
|
|
40
|
+
byebug (9.1.0)
|
|
41
|
+
coderay (1.1.2)
|
|
42
|
+
concurrent-ruby (1.0.5)
|
|
43
|
+
connection_pool (2.2.1)
|
|
44
|
+
crass (1.0.3)
|
|
45
|
+
daemons (1.2.6)
|
|
46
|
+
diff-lcs (1.3)
|
|
47
|
+
docile (1.1.5)
|
|
48
|
+
erubi (1.7.0)
|
|
49
|
+
eventmachine (1.2.5)
|
|
50
|
+
faraday (0.14.0)
|
|
51
|
+
multipart-post (>= 1.2, < 3)
|
|
52
|
+
gitlab (4.3.0)
|
|
53
|
+
httparty
|
|
54
|
+
terminal-table
|
|
55
|
+
globalid (0.4.1)
|
|
56
|
+
activesupport (>= 4.2.0)
|
|
57
|
+
haml (5.0.4)
|
|
58
|
+
temple (>= 0.8.0)
|
|
59
|
+
tilt
|
|
60
|
+
httparty (0.15.6)
|
|
61
|
+
multi_xml (>= 0.5.2)
|
|
62
|
+
i18n (0.9.3)
|
|
63
|
+
concurrent-ruby (~> 1.0)
|
|
64
|
+
json (2.1.0)
|
|
65
|
+
loofah (2.2.2)
|
|
66
|
+
crass (~> 1.0.2)
|
|
67
|
+
nokogiri (>= 1.5.9)
|
|
68
|
+
mail (2.7.0)
|
|
69
|
+
mini_mime (>= 0.1.1)
|
|
70
|
+
mailcatcher (0.2.4)
|
|
71
|
+
eventmachine
|
|
72
|
+
haml
|
|
73
|
+
i18n
|
|
74
|
+
json
|
|
75
|
+
mail
|
|
76
|
+
sinatra
|
|
77
|
+
skinny (>= 0.1.2)
|
|
78
|
+
sqlite3-ruby
|
|
79
|
+
thin
|
|
80
|
+
method_source (0.9.0)
|
|
81
|
+
mini_mime (1.0.0)
|
|
82
|
+
mini_portile2 (2.3.0)
|
|
83
|
+
minitest (5.11.3)
|
|
84
|
+
multi_xml (0.6.0)
|
|
85
|
+
multipart-post (2.0.0)
|
|
86
|
+
mustermann (1.0.2)
|
|
87
|
+
nokogiri (1.8.2)
|
|
88
|
+
mini_portile2 (~> 2.3.0)
|
|
89
|
+
octokit (4.8.0)
|
|
90
|
+
sawyer (~> 0.8.0, >= 0.5.3)
|
|
91
|
+
parallel (1.12.1)
|
|
92
|
+
parser (2.5.0.5)
|
|
93
|
+
ast (~> 2.4.0)
|
|
94
|
+
powerpack (0.1.1)
|
|
95
|
+
pronto (0.9.5)
|
|
96
|
+
gitlab (~> 4.0, >= 4.0.0)
|
|
97
|
+
httparty (>= 0.13.7)
|
|
98
|
+
octokit (~> 4.7, >= 4.7.0)
|
|
99
|
+
rainbow (~> 2.1)
|
|
100
|
+
rugged (~> 0.24, >= 0.23.0)
|
|
101
|
+
thor (~> 0.19.0)
|
|
102
|
+
pronto-rubocop (0.9.0)
|
|
103
|
+
pronto (~> 0.9.0)
|
|
104
|
+
rubocop (~> 0.38, >= 0.35.0)
|
|
105
|
+
pry (0.11.3)
|
|
106
|
+
coderay (~> 1.1.0)
|
|
107
|
+
method_source (~> 0.9.0)
|
|
108
|
+
pry-byebug (3.5.1)
|
|
109
|
+
byebug (~> 9.1)
|
|
110
|
+
pry (~> 0.10)
|
|
111
|
+
public_suffix (3.0.1)
|
|
112
|
+
rack (2.0.4)
|
|
113
|
+
rack-protection (2.0.1)
|
|
114
|
+
rack
|
|
115
|
+
rack-test (0.8.2)
|
|
116
|
+
rack (>= 1.0, < 3)
|
|
117
|
+
rails-dom-testing (2.0.3)
|
|
118
|
+
activesupport (>= 4.2.0)
|
|
119
|
+
nokogiri (>= 1.6)
|
|
120
|
+
rails-html-sanitizer (1.0.3)
|
|
121
|
+
loofah (~> 2.0)
|
|
122
|
+
rainbow (2.2.2)
|
|
123
|
+
rake
|
|
124
|
+
rake (10.5.0)
|
|
125
|
+
redis (4.0.1)
|
|
126
|
+
rspec (3.7.0)
|
|
127
|
+
rspec-core (~> 3.7.0)
|
|
128
|
+
rspec-expectations (~> 3.7.0)
|
|
129
|
+
rspec-mocks (~> 3.7.0)
|
|
130
|
+
rspec-core (3.7.1)
|
|
131
|
+
rspec-support (~> 3.7.0)
|
|
132
|
+
rspec-expectations (3.7.0)
|
|
133
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
134
|
+
rspec-support (~> 3.7.0)
|
|
135
|
+
rspec-mocks (3.7.0)
|
|
136
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
137
|
+
rspec-support (~> 3.7.0)
|
|
138
|
+
rspec-support (3.7.1)
|
|
139
|
+
rubocop (0.53.0)
|
|
140
|
+
parallel (~> 1.10)
|
|
141
|
+
parser (>= 2.5)
|
|
142
|
+
powerpack (~> 0.1)
|
|
143
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
144
|
+
ruby-progressbar (~> 1.7)
|
|
145
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
|
146
|
+
rubocop-rspec (1.22.1)
|
|
147
|
+
rubocop (>= 0.52.1)
|
|
148
|
+
ruby-progressbar (1.9.0)
|
|
149
|
+
rugged (0.26.0)
|
|
150
|
+
sawyer (0.8.1)
|
|
151
|
+
addressable (>= 2.3.5, < 2.6)
|
|
152
|
+
faraday (~> 0.8, < 1.0)
|
|
153
|
+
sidekiq (5.0.5)
|
|
154
|
+
concurrent-ruby (~> 1.0)
|
|
155
|
+
connection_pool (~> 2.2, >= 2.2.0)
|
|
156
|
+
rack-protection (>= 1.5.0)
|
|
157
|
+
redis (>= 3.3.4, < 5)
|
|
158
|
+
simplecov (0.15.1)
|
|
159
|
+
docile (~> 1.1.0)
|
|
160
|
+
json (>= 1.8, < 3)
|
|
161
|
+
simplecov-html (~> 0.10.0)
|
|
162
|
+
simplecov-html (0.10.2)
|
|
163
|
+
sinatra (2.0.1)
|
|
164
|
+
mustermann (~> 1.0)
|
|
165
|
+
rack (~> 2.0)
|
|
166
|
+
rack-protection (= 2.0.1)
|
|
167
|
+
tilt (~> 2.0)
|
|
168
|
+
skinny (0.2.2)
|
|
169
|
+
eventmachine (~> 1.0)
|
|
170
|
+
thin
|
|
171
|
+
sqlite3 (1.3.13)
|
|
172
|
+
sqlite3-ruby (1.3.3)
|
|
173
|
+
sqlite3 (>= 1.3.3)
|
|
174
|
+
temple (0.8.0)
|
|
175
|
+
terminal-table (1.8.0)
|
|
176
|
+
unicode-display_width (~> 1.1, >= 1.1.1)
|
|
177
|
+
thin (1.7.2)
|
|
178
|
+
daemons (~> 1.0, >= 1.0.9)
|
|
179
|
+
eventmachine (~> 1.0, >= 1.0.4)
|
|
180
|
+
rack (>= 1, < 3)
|
|
181
|
+
thor (0.19.4)
|
|
182
|
+
thread_safe (0.3.6)
|
|
183
|
+
tilt (2.0.8)
|
|
184
|
+
tzinfo (1.2.4)
|
|
185
|
+
thread_safe (~> 0.1)
|
|
186
|
+
unicode-display_width (1.3.0)
|
|
187
|
+
|
|
188
|
+
PLATFORMS
|
|
189
|
+
ruby
|
|
190
|
+
|
|
191
|
+
DEPENDENCIES
|
|
192
|
+
actionmailer (~> 5.0)
|
|
193
|
+
bundler (~> 1.13)
|
|
194
|
+
checker_jobs!
|
|
195
|
+
mailcatcher
|
|
196
|
+
pronto
|
|
197
|
+
pronto-rubocop
|
|
198
|
+
pry-byebug
|
|
199
|
+
rake (~> 10.0)
|
|
200
|
+
rspec (~> 3.7)
|
|
201
|
+
rubocop
|
|
202
|
+
rubocop-rspec
|
|
203
|
+
sidekiq (~> 5.0)
|
|
204
|
+
simplecov
|
|
205
|
+
|
|
206
|
+
BUNDLED WITH
|
|
207
|
+
1.16.1
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2018 Drivy
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
# CheckerJobs
|
|
2
|
+
|
|
3
|
+
This gems provides a small DSL to check your data for inconsistencies.
|
|
4
|
+
|
|
5
|
+
[](https://codeclimate.com/github/drivy/checker_jobs/maintainability)
|
|
6
|
+
[](https://codeclimate.com/github/drivy/checker_jobs/test_coverage)
|
|
7
|
+
|
|
8
|
+
## Introduction
|
|
9
|
+
|
|
10
|
+
To ensure database integrity, DBMS provides some tools: foreign keys, triggers,
|
|
11
|
+
strored procedures, ... Those tools aren't the easiests to maintain unless your
|
|
12
|
+
project is based on them. Also, you may want to avoid to duplicate your business
|
|
13
|
+
rules from your application to another language and add operational complexity
|
|
14
|
+
around deployment.
|
|
15
|
+
|
|
16
|
+
This gem doesn't aim to replace those tools but provides something else that
|
|
17
|
+
could serve a close purpose: _ensure that you work with the data you expect_.
|
|
18
|
+
|
|
19
|
+
This gem helps you schedule some verifications on your data and get alerts when
|
|
20
|
+
something is unexpected. You declare checks that could contain any business code
|
|
21
|
+
you like and then those checks are run by your application, in background jobs.
|
|
22
|
+
|
|
23
|
+
A small DSL is provided to helps you express your predicates:
|
|
24
|
+
|
|
25
|
+
- `ensure_no` will check that the result of a given block is `zero?` or `empty?`
|
|
26
|
+
- `ensure_more` will check that the result of a given block is `>=` than a given number
|
|
27
|
+
- `ensure_fewer` will check that the result of a given block is `<=` than a given number
|
|
28
|
+
|
|
29
|
+
and an easy way to configure notifications.
|
|
30
|
+
|
|
31
|
+
For instance, at Drivy we don't expect users to get a negative credit amount. It
|
|
32
|
+
isn't easy to get all the validation right because many rules are in play here.
|
|
33
|
+
Because of those rules the credit isn't just a column in our database yet but
|
|
34
|
+
needs to be computed based on various parameters. What we would like to ensure is
|
|
35
|
+
that _no one ends up with a negative credit_. We could write something like:
|
|
36
|
+
|
|
37
|
+
``` ruby
|
|
38
|
+
class UsersChecker
|
|
39
|
+
include CheckerJobs::Base
|
|
40
|
+
|
|
41
|
+
options sidekiq: { queue: :slow }
|
|
42
|
+
|
|
43
|
+
notify "oss@drivy.com"
|
|
44
|
+
|
|
45
|
+
ensure_no :negative_rental_credit do
|
|
46
|
+
# The following code is an over-simplification
|
|
47
|
+
# Real code is more performance oriented...
|
|
48
|
+
|
|
49
|
+
user_ids_with_negative_rental_credit = []
|
|
50
|
+
|
|
51
|
+
User.find_each do |user|
|
|
52
|
+
if user.credit_amount < 0
|
|
53
|
+
user_ids_with_negative_rental_credit << user.id
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
user_ids_with_negative_rental_credit
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Then when something's wrong, [you'll get alerted](https://cl.ly/3l2b3T3n0o2a).
|
|
63
|
+
|
|
64
|
+
You'll find more use cases and tips in the [wiki](https://github.com/drivy/checker_jobs/wiki).
|
|
65
|
+
|
|
66
|
+
## Installation
|
|
67
|
+
|
|
68
|
+
Add this line to your application's Gemfile:
|
|
69
|
+
|
|
70
|
+
```ruby
|
|
71
|
+
gem 'checker_jobs'
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Usage
|
|
75
|
+
|
|
76
|
+
Have a look at the `examples` directory of the repository to get a clearer idea
|
|
77
|
+
about how to use and the gem is offering.
|
|
78
|
+
|
|
79
|
+
### Configure
|
|
80
|
+
|
|
81
|
+
At the moment this gems supports [Drivy][gh-drivy]'s stack which includes
|
|
82
|
+
[Sidekiq][gh-sidekiq] and [Rails][rails]. It has been designed to supports more
|
|
83
|
+
than Sidekiq a job processor and more that ActionMailer as a notification
|
|
84
|
+
gateway. If you're on the same stack as we are, configuration looks like this:
|
|
85
|
+
|
|
86
|
+
``` ruby
|
|
87
|
+
require "checker_jobs"
|
|
88
|
+
|
|
89
|
+
CheckerJobs.configure do |config|
|
|
90
|
+
config.repository_url = { github: "drivy/checker_jobs" }
|
|
91
|
+
|
|
92
|
+
config.jobs_processor = :sidekiq
|
|
93
|
+
|
|
94
|
+
config.emails_backend = :action_mailer
|
|
95
|
+
config.emails_options = { from: "oss@drivy.com", reply_to: "no-reply@drivy.com" }
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
This piece of code usually goes into the `config/initializers/checker_jobs.rb`
|
|
101
|
+
file in a rails application. It relies on the fact that ActionMailer and sidekiq
|
|
102
|
+
are already configured.
|
|
103
|
+
|
|
104
|
+
If you're on a different stack and you'll like to add a new job processor or
|
|
105
|
+
notification backend in this gem, [drop us a line][d-jobs].
|
|
106
|
+
|
|
107
|
+
### Write checkers
|
|
108
|
+
|
|
109
|
+
A checker is a class that inherits `CheckerJobs::Base` and uses the
|
|
110
|
+
[DSL](wiki/DSL) to declare checks.
|
|
111
|
+
|
|
112
|
+
``` ruby
|
|
113
|
+
class UserChecker
|
|
114
|
+
include CheckerJobs::Base
|
|
115
|
+
|
|
116
|
+
options sidekiq: { queue: :fast }
|
|
117
|
+
|
|
118
|
+
notify "tech@drivy.com"
|
|
119
|
+
|
|
120
|
+
ensure_no :user_without_email do
|
|
121
|
+
UserRepository.missing_email.size
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
The `UserChecker` will have the same interface as your usual jobs. In this
|
|
127
|
+
example, `UserChecker` will be a `Sidekiq::Worker`. Its `#perform` method will
|
|
128
|
+
run the check named `:user_without_email` and if
|
|
129
|
+
`UserRepository.missing_email.size` is greater than 0 then an email will be
|
|
130
|
+
fired through ActionMailer to `tech@drivy.com`.
|
|
131
|
+
|
|
132
|
+
### Schedule checks
|
|
133
|
+
|
|
134
|
+
Once you have checker jobs, you'll need to run them. There are many task
|
|
135
|
+
schedulers out there and it isn't really relevant what you'll be using.
|
|
136
|
+
|
|
137
|
+
You have to enqueue you job as often as you like and that's it.
|
|
138
|
+
|
|
139
|
+
``` ruby
|
|
140
|
+
UserChecker.perform_async
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Contributing
|
|
144
|
+
|
|
145
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/drivy/checker_jobs.
|
|
146
|
+
|
|
147
|
+
You'll find out that the CI is setup to run test coverage and linting.
|
|
148
|
+
|
|
149
|
+
## License
|
|
150
|
+
|
|
151
|
+
The gem is available as open source under the terms of the [MIT License][licence].
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
[d-jobs]: https://www.drivy.com/jobs
|
|
155
|
+
[gh-drivy]: https://github.com/drivy
|
|
156
|
+
[gh-sidekiq]: https://github.com/mperham/sidekiq
|
|
157
|
+
[licence]: http://opensource.org/licenses/MIT
|
|
158
|
+
[rails]: http://rubyonrails.org
|
data/Rakefile
ADDED
data/bin/console
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "checker_jobs"
|
|
5
|
+
|
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
+
|
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
+
# require "pry"
|
|
11
|
+
# Pry.start
|
|
12
|
+
|
|
13
|
+
require "irb"
|
|
14
|
+
IRB.start
|
data/bin/pronto-ci
ADDED
data/bin/setup
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
lib = File.expand_path("../lib", __FILE__)
|
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
3
|
+
require "checker_jobs/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "checker_jobs"
|
|
7
|
+
spec.version = CheckerJobs::VERSION
|
|
8
|
+
spec.authors = ["Drivy", "Nicolas Zermati"]
|
|
9
|
+
spec.email = ["oss@drivy.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Asynchronous data consistency checks"
|
|
12
|
+
spec.description = ""
|
|
13
|
+
spec.homepage = "https://drivy.engineering/"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
|
17
|
+
f.match(%r{^(test|spec|features)/})
|
|
18
|
+
end
|
|
19
|
+
spec.bindir = "exe"
|
|
20
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
21
|
+
spec.require_paths = ["lib"]
|
|
22
|
+
|
|
23
|
+
spec.add_development_dependency "actionmailer", "~> 5.0"
|
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.13"
|
|
25
|
+
spec.add_development_dependency "mailcatcher"
|
|
26
|
+
spec.add_development_dependency "pronto"
|
|
27
|
+
spec.add_development_dependency "pronto-rubocop"
|
|
28
|
+
spec.add_development_dependency "pry-byebug"
|
|
29
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
|
30
|
+
spec.add_development_dependency "rspec", "~> 3.7"
|
|
31
|
+
spec.add_development_dependency "rubocop"
|
|
32
|
+
spec.add_development_dependency "rubocop-rspec"
|
|
33
|
+
spec.add_development_dependency "sidekiq", "~> 5.0"
|
|
34
|
+
spec.add_development_dependency "simplecov"
|
|
35
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#
|
|
2
|
+
# Configuration
|
|
3
|
+
#
|
|
4
|
+
|
|
5
|
+
require "sidekiq"
|
|
6
|
+
require "sidekiq/testing"
|
|
7
|
+
|
|
8
|
+
Sidekiq::Testing.inline!
|
|
9
|
+
|
|
10
|
+
require "action_mailer"
|
|
11
|
+
|
|
12
|
+
ActionMailer::Base.raise_delivery_errors = true
|
|
13
|
+
ActionMailer::Base.delivery_method = :smtp
|
|
14
|
+
ActionMailer::Base.smtp_settings = { :address => "localhost", :port => 1025 }
|
|
15
|
+
# Be sure to run `mailcatcher` to see the actual emails on this example
|
|
16
|
+
|
|
17
|
+
require "checker_jobs"
|
|
18
|
+
|
|
19
|
+
CheckerJobs.configure do |config|
|
|
20
|
+
config.repository_url = { github: "drivy/checker_jobs" }
|
|
21
|
+
|
|
22
|
+
config.jobs_processor = :sidekiq
|
|
23
|
+
|
|
24
|
+
config.emails_backend = :action_mailer
|
|
25
|
+
config.emails_options = { from: "oss@drivy.com", reply_to: "no-reply@drivy.com" }
|
|
26
|
+
|
|
27
|
+
config.around_check = ->(check) {
|
|
28
|
+
puts "Starting check #{check.name}..."
|
|
29
|
+
check.perform
|
|
30
|
+
puts "Done #{check.name}!"
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
#
|
|
35
|
+
# Checker
|
|
36
|
+
#
|
|
37
|
+
|
|
38
|
+
class UserChecker
|
|
39
|
+
include CheckerJobs::Base
|
|
40
|
+
|
|
41
|
+
options sidekiq: { queue: :fast }
|
|
42
|
+
|
|
43
|
+
notify "tech@drivy.com"
|
|
44
|
+
|
|
45
|
+
ensure_no :inconsistent_payment do
|
|
46
|
+
UserRepository.missing_email.size
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
#
|
|
51
|
+
# Context
|
|
52
|
+
#
|
|
53
|
+
|
|
54
|
+
module UserRepository
|
|
55
|
+
def self.missing_email
|
|
56
|
+
[ User.new(1) ]
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
User = Struct.new(:id)
|
|
61
|
+
|
|
62
|
+
#
|
|
63
|
+
# Triggering the checker
|
|
64
|
+
#
|
|
65
|
+
|
|
66
|
+
UserChecker.perform_async
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
CheckerJobs::Checks::Base = Struct.new(:klass, :name, :options, :block) do
|
|
2
|
+
def perform
|
|
3
|
+
klass.new.instance_exec(&block).tap do |result|
|
|
4
|
+
handle_result(result)
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def notify(count:, entries: nil)
|
|
11
|
+
CheckerJobs.configuration.emails_backend_class.
|
|
12
|
+
new(self, count, entries).
|
|
13
|
+
notify
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def handle_result(_result)
|
|
17
|
+
raise NotImplementedError
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class CheckerJobs::Checks::EnsureFewer < CheckerJobs::Checks::Base
|
|
2
|
+
private
|
|
3
|
+
|
|
4
|
+
def handle_result(result)
|
|
5
|
+
case result
|
|
6
|
+
when Numeric
|
|
7
|
+
notify(count: result) if result > options.fetch(:than)
|
|
8
|
+
else
|
|
9
|
+
raise ArgumentError, "Unsupported result: '#{result.class.name}' for 'ensure_less'"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
class CheckerJobs::Checks::EnsureMore < CheckerJobs::Checks::Base
|
|
2
|
+
private
|
|
3
|
+
|
|
4
|
+
def handle_result(result)
|
|
5
|
+
case result
|
|
6
|
+
when Numeric
|
|
7
|
+
notify(count: result) if result < options.fetch(:than)
|
|
8
|
+
else
|
|
9
|
+
raise ArgumentError, "Unsupported result: '#{result.class.name}' for 'ensure_more'"
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class CheckerJobs::Checks::EnsureNo < CheckerJobs::Checks::Base
|
|
2
|
+
private
|
|
3
|
+
|
|
4
|
+
def handle_result(result)
|
|
5
|
+
case result
|
|
6
|
+
when Numeric
|
|
7
|
+
notify(count: result) unless result.zero?
|
|
8
|
+
when Enumerable
|
|
9
|
+
notify(count: result.size, entries: result) unless result.empty?
|
|
10
|
+
else
|
|
11
|
+
raise ArgumentError, "Unsupported result: '#{result.class.name}' for 'ensure_no'"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require "checker_jobs/emails_backends"
|
|
2
|
+
require "checker_jobs/jobs_processors"
|
|
3
|
+
|
|
4
|
+
class CheckerJobs::Configuration
|
|
5
|
+
DEFAULT_TIME_BETWEEN_CHECKS = 15 * 60 # 15 minutes, expressed in seconds
|
|
6
|
+
|
|
7
|
+
attr_accessor :jobs_processor,
|
|
8
|
+
:emails_backend,
|
|
9
|
+
:emails_options,
|
|
10
|
+
:emails_formatter_class,
|
|
11
|
+
:time_between_checks,
|
|
12
|
+
:repository_url,
|
|
13
|
+
:around_check
|
|
14
|
+
|
|
15
|
+
def self.default
|
|
16
|
+
new.tap do |config|
|
|
17
|
+
config.emails_options = {}
|
|
18
|
+
config.emails_formatter_class = CheckerJobs::EmailsBackends::DefaultFormatter
|
|
19
|
+
config.time_between_checks = DEFAULT_TIME_BETWEEN_CHECKS
|
|
20
|
+
config.around_check = ->(check) { check.perform }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def jobs_processor_module
|
|
25
|
+
case jobs_processor
|
|
26
|
+
when :sidekiq
|
|
27
|
+
CheckerJobs::JobsProcessors::Sidekiq
|
|
28
|
+
else
|
|
29
|
+
raise CheckerJobs::UnsupportedConfigurationOption.new(:jobs_processor, jobs_processor)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def emails_backend_class
|
|
34
|
+
case emails_backend
|
|
35
|
+
when :action_mailer
|
|
36
|
+
CheckerJobs::EmailsBackends::ActionMailer
|
|
37
|
+
else
|
|
38
|
+
raise CheckerJobs::UnsupportedConfigurationOption.new(:emails_backend, emails_backend)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require "checker_jobs/checks"
|
|
2
|
+
|
|
3
|
+
# DSL to declare checker jobs. It provides predicates:
|
|
4
|
+
#
|
|
5
|
+
# - ensure_no
|
|
6
|
+
# - ensure_more
|
|
7
|
+
# - ensure_fewer
|
|
8
|
+
#
|
|
9
|
+
# And configuration methods:
|
|
10
|
+
#
|
|
11
|
+
# - options
|
|
12
|
+
# - notify
|
|
13
|
+
# - interval
|
|
14
|
+
module CheckerJobs::DSL
|
|
15
|
+
def options(options_hash)
|
|
16
|
+
@options = options_hash
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def notify(target)
|
|
20
|
+
@notification_target = target
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def interval(duration)
|
|
24
|
+
@time_between_checks = duration
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def ensure_no(name, options = {}, &block)
|
|
28
|
+
add_check CheckerJobs::Checks::EnsureNo, name, options, block
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def ensure_more(name, options = {}, &block)
|
|
32
|
+
add_check CheckerJobs::Checks::EnsureMore, name, options, block
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def ensure_fewer(name, options = {}, &block)
|
|
36
|
+
add_check CheckerJobs::Checks::EnsureFewer, name, options, block
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
#
|
|
40
|
+
# Private API
|
|
41
|
+
#
|
|
42
|
+
|
|
43
|
+
def notification_target
|
|
44
|
+
raise CheckerJobs::MissingNotificationTarget, self.class unless defined?(@notification_target)
|
|
45
|
+
|
|
46
|
+
@notification_target
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def time_between_checks
|
|
50
|
+
@time_between_checks || CheckerJobs.configuration.time_between_checks
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def option(key, default = nil)
|
|
54
|
+
@options && @options[key] || default
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def checks
|
|
58
|
+
@check ||= {}
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def add_check(klass, name, options, block)
|
|
62
|
+
name = name.to_s
|
|
63
|
+
|
|
64
|
+
raise CheckerJobs::DuplicateCheckerName, name if checks.key?(name)
|
|
65
|
+
|
|
66
|
+
checks[name] = klass.new(self, name, options, block)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def perform_check(check_name)
|
|
70
|
+
check = checks.fetch(check_name.to_s)
|
|
71
|
+
CheckerJobs.configuration.around_check.call(check)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require "action_mailer"
|
|
2
|
+
|
|
3
|
+
class CheckerJobs::EmailsBackends::ActionMailer
|
|
4
|
+
def initialize(check, count, entries)
|
|
5
|
+
@check = check
|
|
6
|
+
@formatter = formatter_class.new(check, count, entries)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def notify
|
|
10
|
+
Mailer.notify(@formatter.body, options).deliver!
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
|
|
15
|
+
def options
|
|
16
|
+
CheckerJobs.configuration.emails_options.merge({
|
|
17
|
+
to: @check.klass.notification_target,
|
|
18
|
+
subject: @formatter.subject,
|
|
19
|
+
})
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def formatter_class
|
|
23
|
+
CheckerJobs.configuration.emails_formatter_class
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Simple mailer class based on ActionMailer to send HTML emails while reusing
|
|
27
|
+
# the ActionMailer configuration of the application embedding the checkers.
|
|
28
|
+
class Mailer < ::ActionMailer::Base
|
|
29
|
+
layout false
|
|
30
|
+
|
|
31
|
+
def notify(body, options)
|
|
32
|
+
mail(options) do |format|
|
|
33
|
+
format.html { render html: body.html_safe }
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
class CheckerJobs::EmailsBackends::DefaultFormatter
|
|
2
|
+
def initialize(check, count, entries)
|
|
3
|
+
@check = check
|
|
4
|
+
@count = count
|
|
5
|
+
@entries = entries
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def subject
|
|
9
|
+
name = @check.name.tr("_", " ").capitalize
|
|
10
|
+
"#{name} checker found #{@count} element(s)"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def body
|
|
14
|
+
body = "<p>See more about this email on <a href='#{repository_url}'>Github</a>.</p>"
|
|
15
|
+
if @entries
|
|
16
|
+
body += "<p>Found %<class_name>s are: <ul>%<html_ids>s</ul></p>" % {
|
|
17
|
+
class_name: @entries.first.class.name,
|
|
18
|
+
html_ids: @entries.map { |entry| "<li>#{format_entry(entry)}</li>" }.join,
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
body
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
GITHUB_URL_FORMAT = "https://github.com/%<repository>s/blob/master/%<path>s#L%<line>i".freeze
|
|
27
|
+
|
|
28
|
+
def repository_url
|
|
29
|
+
if repository_configuration.is_a?(String)
|
|
30
|
+
repository_configuration
|
|
31
|
+
elsif repository_configuration.key?(:github)
|
|
32
|
+
github_url
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def github_url
|
|
37
|
+
filepath, line_number = @check.block.source_location
|
|
38
|
+
filepath = filepath.sub(Dir.pwd + "/", "")
|
|
39
|
+
GITHUB_URL_FORMAT % {
|
|
40
|
+
repository: repository_configuration[:github],
|
|
41
|
+
path: filepath,
|
|
42
|
+
line: line_number,
|
|
43
|
+
}
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def format_entry(entry)
|
|
47
|
+
# NOTE: inherit and override to support your custom objects
|
|
48
|
+
entry.respond_to?(:id) ? entry.id : entry
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def repository_configuration
|
|
52
|
+
CheckerJobs.configuration.repository_url
|
|
53
|
+
end
|
|
54
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module CheckerJobs
|
|
2
|
+
class Error < StandardError
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
class MissingNotificationTarget < Error
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
class Unconfigured < Error
|
|
9
|
+
def message
|
|
10
|
+
"CheckerJobs: are unconfigured, do CheckerJobs.configure before using it."
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class UnsupportedConfigurationOption < Error
|
|
15
|
+
def initialize(option_name, value)
|
|
16
|
+
@option_name = option_name
|
|
17
|
+
@value = value
|
|
18
|
+
super()
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def message
|
|
22
|
+
"CheckerJobs: unsupported configuration option: '#{@value.inspect}' " \
|
|
23
|
+
"isn't valid for '#{@option_name}' option."
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class DuplicateCheckerName < Error
|
|
28
|
+
def initialize(checker_name)
|
|
29
|
+
@checker_name = checker_name
|
|
30
|
+
super
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def message
|
|
34
|
+
"CheckerJobs: the name '#{checker_name}' is already used for another checker."
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require "sidekiq"
|
|
2
|
+
|
|
3
|
+
module CheckerJobs::JobsProcessors::Sidekiq
|
|
4
|
+
def self.included(base)
|
|
5
|
+
base.include(Sidekiq::Worker)
|
|
6
|
+
base.extend(ClassMethods)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def perform(check_name = nil)
|
|
10
|
+
# Run on specific check
|
|
11
|
+
return self.class.perform_check(check_name.to_s) if check_name
|
|
12
|
+
|
|
13
|
+
# Enqueue one specific check for each declared check
|
|
14
|
+
self.class.checks.values.each.with_index do |check, index|
|
|
15
|
+
scheduled_in = index * self.class.time_between_checks
|
|
16
|
+
self.class.perform_check_in(check, scheduled_in)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
module ClassMethods
|
|
21
|
+
# Overrides DSL#options in order to pass specific options to Sidekiq.
|
|
22
|
+
# The options could be the queue the job processor must use, or other
|
|
23
|
+
# middleware options of your choice.
|
|
24
|
+
def options(*args)
|
|
25
|
+
super(*args).tap do
|
|
26
|
+
sidekiq_options option(:sidekiq, {})
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def perform_check_in(check, interval)
|
|
31
|
+
# Borrowed from Sidekiq implementation
|
|
32
|
+
item = {
|
|
33
|
+
"class" => self,
|
|
34
|
+
"args" => [check.name.to_s],
|
|
35
|
+
"at" => Time.now.to_f + interval.to_f,
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (specific_queue = check.options[:queue])
|
|
39
|
+
item["queue"] = specific_queue.to_s
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
client_push(item)
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
data/lib/checker_jobs.rb
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require "checker_jobs/version"
|
|
2
|
+
require "checker_jobs/errors"
|
|
3
|
+
require "checker_jobs/configuration"
|
|
4
|
+
require "checker_jobs/base"
|
|
5
|
+
|
|
6
|
+
module CheckerJobs
|
|
7
|
+
def self.configuration
|
|
8
|
+
Thread.current[:checker_jobs_configuration] || raise(Unconfigured)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.configure(&block)
|
|
12
|
+
Thread.current[:checker_jobs_configuration] = Configuration.default
|
|
13
|
+
block&.call(configuration)
|
|
14
|
+
end
|
|
15
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: checker_jobs
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0.pre
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Drivy
|
|
8
|
+
- Nicolas Zermati
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: exe
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2018-03-31 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: actionmailer
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
requirements:
|
|
18
|
+
- - "~>"
|
|
19
|
+
- !ruby/object:Gem::Version
|
|
20
|
+
version: '5.0'
|
|
21
|
+
type: :development
|
|
22
|
+
prerelease: false
|
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
24
|
+
requirements:
|
|
25
|
+
- - "~>"
|
|
26
|
+
- !ruby/object:Gem::Version
|
|
27
|
+
version: '5.0'
|
|
28
|
+
- !ruby/object:Gem::Dependency
|
|
29
|
+
name: bundler
|
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
|
31
|
+
requirements:
|
|
32
|
+
- - "~>"
|
|
33
|
+
- !ruby/object:Gem::Version
|
|
34
|
+
version: '1.13'
|
|
35
|
+
type: :development
|
|
36
|
+
prerelease: false
|
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
38
|
+
requirements:
|
|
39
|
+
- - "~>"
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '1.13'
|
|
42
|
+
- !ruby/object:Gem::Dependency
|
|
43
|
+
name: mailcatcher
|
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
|
45
|
+
requirements:
|
|
46
|
+
- - ">="
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: '0'
|
|
49
|
+
type: :development
|
|
50
|
+
prerelease: false
|
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
52
|
+
requirements:
|
|
53
|
+
- - ">="
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: '0'
|
|
56
|
+
- !ruby/object:Gem::Dependency
|
|
57
|
+
name: pronto
|
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '0'
|
|
63
|
+
type: :development
|
|
64
|
+
prerelease: false
|
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
66
|
+
requirements:
|
|
67
|
+
- - ">="
|
|
68
|
+
- !ruby/object:Gem::Version
|
|
69
|
+
version: '0'
|
|
70
|
+
- !ruby/object:Gem::Dependency
|
|
71
|
+
name: pronto-rubocop
|
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
|
73
|
+
requirements:
|
|
74
|
+
- - ">="
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: '0'
|
|
77
|
+
type: :development
|
|
78
|
+
prerelease: false
|
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
80
|
+
requirements:
|
|
81
|
+
- - ">="
|
|
82
|
+
- !ruby/object:Gem::Version
|
|
83
|
+
version: '0'
|
|
84
|
+
- !ruby/object:Gem::Dependency
|
|
85
|
+
name: pry-byebug
|
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
|
87
|
+
requirements:
|
|
88
|
+
- - ">="
|
|
89
|
+
- !ruby/object:Gem::Version
|
|
90
|
+
version: '0'
|
|
91
|
+
type: :development
|
|
92
|
+
prerelease: false
|
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
94
|
+
requirements:
|
|
95
|
+
- - ">="
|
|
96
|
+
- !ruby/object:Gem::Version
|
|
97
|
+
version: '0'
|
|
98
|
+
- !ruby/object:Gem::Dependency
|
|
99
|
+
name: rake
|
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
|
101
|
+
requirements:
|
|
102
|
+
- - "~>"
|
|
103
|
+
- !ruby/object:Gem::Version
|
|
104
|
+
version: '10.0'
|
|
105
|
+
type: :development
|
|
106
|
+
prerelease: false
|
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
108
|
+
requirements:
|
|
109
|
+
- - "~>"
|
|
110
|
+
- !ruby/object:Gem::Version
|
|
111
|
+
version: '10.0'
|
|
112
|
+
- !ruby/object:Gem::Dependency
|
|
113
|
+
name: rspec
|
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
|
115
|
+
requirements:
|
|
116
|
+
- - "~>"
|
|
117
|
+
- !ruby/object:Gem::Version
|
|
118
|
+
version: '3.7'
|
|
119
|
+
type: :development
|
|
120
|
+
prerelease: false
|
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
122
|
+
requirements:
|
|
123
|
+
- - "~>"
|
|
124
|
+
- !ruby/object:Gem::Version
|
|
125
|
+
version: '3.7'
|
|
126
|
+
- !ruby/object:Gem::Dependency
|
|
127
|
+
name: rubocop
|
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
|
129
|
+
requirements:
|
|
130
|
+
- - ">="
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: '0'
|
|
133
|
+
type: :development
|
|
134
|
+
prerelease: false
|
|
135
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
136
|
+
requirements:
|
|
137
|
+
- - ">="
|
|
138
|
+
- !ruby/object:Gem::Version
|
|
139
|
+
version: '0'
|
|
140
|
+
- !ruby/object:Gem::Dependency
|
|
141
|
+
name: rubocop-rspec
|
|
142
|
+
requirement: !ruby/object:Gem::Requirement
|
|
143
|
+
requirements:
|
|
144
|
+
- - ">="
|
|
145
|
+
- !ruby/object:Gem::Version
|
|
146
|
+
version: '0'
|
|
147
|
+
type: :development
|
|
148
|
+
prerelease: false
|
|
149
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
150
|
+
requirements:
|
|
151
|
+
- - ">="
|
|
152
|
+
- !ruby/object:Gem::Version
|
|
153
|
+
version: '0'
|
|
154
|
+
- !ruby/object:Gem::Dependency
|
|
155
|
+
name: sidekiq
|
|
156
|
+
requirement: !ruby/object:Gem::Requirement
|
|
157
|
+
requirements:
|
|
158
|
+
- - "~>"
|
|
159
|
+
- !ruby/object:Gem::Version
|
|
160
|
+
version: '5.0'
|
|
161
|
+
type: :development
|
|
162
|
+
prerelease: false
|
|
163
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
164
|
+
requirements:
|
|
165
|
+
- - "~>"
|
|
166
|
+
- !ruby/object:Gem::Version
|
|
167
|
+
version: '5.0'
|
|
168
|
+
- !ruby/object:Gem::Dependency
|
|
169
|
+
name: simplecov
|
|
170
|
+
requirement: !ruby/object:Gem::Requirement
|
|
171
|
+
requirements:
|
|
172
|
+
- - ">="
|
|
173
|
+
- !ruby/object:Gem::Version
|
|
174
|
+
version: '0'
|
|
175
|
+
type: :development
|
|
176
|
+
prerelease: false
|
|
177
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
178
|
+
requirements:
|
|
179
|
+
- - ">="
|
|
180
|
+
- !ruby/object:Gem::Version
|
|
181
|
+
version: '0'
|
|
182
|
+
description: ''
|
|
183
|
+
email:
|
|
184
|
+
- oss@drivy.com
|
|
185
|
+
executables: []
|
|
186
|
+
extensions: []
|
|
187
|
+
extra_rdoc_files: []
|
|
188
|
+
files:
|
|
189
|
+
- ".circleci/config.yml"
|
|
190
|
+
- ".gitignore"
|
|
191
|
+
- ".rspec"
|
|
192
|
+
- ".rubocop.yml"
|
|
193
|
+
- Gemfile
|
|
194
|
+
- Gemfile.lock
|
|
195
|
+
- LICENSE.txt
|
|
196
|
+
- README.md
|
|
197
|
+
- Rakefile
|
|
198
|
+
- bin/console
|
|
199
|
+
- bin/pronto-ci
|
|
200
|
+
- bin/setup
|
|
201
|
+
- checker_jobs.gemspec
|
|
202
|
+
- examples/user_checker.rb
|
|
203
|
+
- lib/checker_jobs.rb
|
|
204
|
+
- lib/checker_jobs/base.rb
|
|
205
|
+
- lib/checker_jobs/checks.rb
|
|
206
|
+
- lib/checker_jobs/checks/base.rb
|
|
207
|
+
- lib/checker_jobs/checks/ensure_fewer.rb
|
|
208
|
+
- lib/checker_jobs/checks/ensure_more.rb
|
|
209
|
+
- lib/checker_jobs/checks/ensure_no.rb
|
|
210
|
+
- lib/checker_jobs/configuration.rb
|
|
211
|
+
- lib/checker_jobs/dsl.rb
|
|
212
|
+
- lib/checker_jobs/emails_backends.rb
|
|
213
|
+
- lib/checker_jobs/emails_backends/action_mailer.rb
|
|
214
|
+
- lib/checker_jobs/emails_backends/default_formatter.rb
|
|
215
|
+
- lib/checker_jobs/errors.rb
|
|
216
|
+
- lib/checker_jobs/jobs_processors.rb
|
|
217
|
+
- lib/checker_jobs/jobs_processors/sidekiq.rb
|
|
218
|
+
- lib/checker_jobs/version.rb
|
|
219
|
+
homepage: https://drivy.engineering/
|
|
220
|
+
licenses:
|
|
221
|
+
- MIT
|
|
222
|
+
metadata: {}
|
|
223
|
+
post_install_message:
|
|
224
|
+
rdoc_options: []
|
|
225
|
+
require_paths:
|
|
226
|
+
- lib
|
|
227
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
228
|
+
requirements:
|
|
229
|
+
- - ">="
|
|
230
|
+
- !ruby/object:Gem::Version
|
|
231
|
+
version: '0'
|
|
232
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
233
|
+
requirements:
|
|
234
|
+
- - ">"
|
|
235
|
+
- !ruby/object:Gem::Version
|
|
236
|
+
version: 1.3.1
|
|
237
|
+
requirements: []
|
|
238
|
+
rubyforge_project:
|
|
239
|
+
rubygems_version: 2.6.14
|
|
240
|
+
signing_key:
|
|
241
|
+
specification_version: 4
|
|
242
|
+
summary: Asynchronous data consistency checks
|
|
243
|
+
test_files: []
|