rubocop-gusto 11.0.0 → 11.1.0
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 +4 -4
- data/CHANGELOG.md +8 -0
- data/README.md +18 -2
- data/config/default.yml +9 -129
- data/config/gusto_cops.yml +135 -0
- data/config/sidekiq.yml +7 -0
- data/lib/rubocop/cop/gusto/all.rb +7 -0
- data/lib/rubocop/cop/gusto/bootsnap_load_file.rb +11 -1
- data/lib/rubocop/cop/gusto/datadog_constant.rb +10 -0
- data/lib/rubocop/cop/gusto/discouraged_gem.rb +8 -5
- data/lib/rubocop/cop/gusto/execute_migration.rb +14 -0
- data/lib/rubocop/cop/gusto/factory_classes_or_modules.rb +18 -0
- data/lib/rubocop/cop/gusto/paperclip_or_attachable.rb +9 -0
- data/lib/rubocop/cop/gusto/polymorphic_type_validation.rb +18 -3
- data/lib/rubocop/cop/gusto/sidekiq_params.rb +12 -0
- data/lib/rubocop/cop/gusto/unreferenced_let.rb +3 -2
- data/lib/rubocop/cop/sidekiq/perform_async_stub.rb +90 -0
- data/lib/rubocop/gusto/init.rb +20 -7
- data/lib/rubocop/gusto/version.rb +1 -1
- metadata +6 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5814e91aac6e402768ff64bd32f0c196838e20d90cbd927286238f68bb9d25b9
|
|
4
|
+
data.tar.gz: d4e988b9973101b7ea9a683900ae7489cbfe128b681d4137c7317f06a45cc247
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 82385ed24fdf344897513f4f034e1f8ae662a5d41b62ccc8dc2c14e735d479deb31acf4c28047fbad1f59d828485798a4677148ca203989f15489d768cb88995
|
|
7
|
+
data.tar.gz: 403192785be424b372d709d2c7d9bac18491603c2a9449a2edeb5b35c981652e7ee87c307265a381b51d5285134fda5a0178adb04087bb6d2db7c10b51d41ae9
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,14 @@
|
|
|
3
3
|
- Remove redundant `Rails: Enabled: true` from `config/rails.yml` (already set by rubocop-rails' own defaults)
|
|
4
4
|
- Enable `Rails/DefaultScope` cop (disabled by default in rubocop-rails)
|
|
5
5
|
|
|
6
|
+
## [11.1.0](https://github.com/Gusto/rubocop-gusto/compare/v11.0.0...v11.1.0) (2026-06-17)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* add `Sidekiq/PerformAsyncStub` in a separate config ([#131](https://github.com/Gusto/rubocop-gusto/issues/131)) ([22670d4](https://github.com/Gusto/rubocop-gusto/commit/22670d4a34487417bdb50b8be25e82fb0cac3614))
|
|
12
|
+
* add cops-only entrypoints for selective adoption ([#132](https://github.com/Gusto/rubocop-gusto/issues/132)) ([f0911c2](https://github.com/Gusto/rubocop-gusto/commit/f0911c2f9fc6d272bc8ccb5af830daf659c6ba62))
|
|
13
|
+
|
|
6
14
|
## [11.0.0](https://github.com/Gusto/rubocop-gusto/compare/v10.10.0...v11.0.0) (2026-06-08)
|
|
7
15
|
|
|
8
16
|
|
data/README.md
CHANGED
|
@@ -28,15 +28,31 @@ $ bundle
|
|
|
28
28
|
bundle exec rubocop-gusto init
|
|
29
29
|
```
|
|
30
30
|
|
|
31
|
-
This adds `rubocop-gusto` to your `.rubocop.yml` `plugins:` list and includes any relevant configuration (e.g. Rails-specific rules when Rails is detected).
|
|
31
|
+
This adds `rubocop-gusto` to your `.rubocop.yml` `plugins:` list and includes any relevant configuration (e.g. Rails-specific rules when Rails is detected, Sidekiq-specific rules when the Sidekiq gem is present).
|
|
32
32
|
|
|
33
33
|
If this is an existing project, it is recommended to run the autocorrector (`bundle exec rubocop -a`) and then to regenerate the `.rubocop_todo.yml` (`bundle exec rubocop --auto-gen-config`), so issues can be dealt with piecemeal.
|
|
34
34
|
|
|
35
|
+
#### Sidekiq configuration
|
|
36
|
+
|
|
37
|
+
Sidekiq-specific cops live in `config/sidekiq.yml` and are **not** included in `config/default.yml`, so projects without Sidekiq are not linted for those patterns. Running `bundle exec rubocop-gusto init` adds `config/sidekiq.yml` to your `inherit_gem` list automatically when Sidekiq is listed in your `Gemfile` or `Gemfile.lock`.
|
|
38
|
+
|
|
39
|
+
For an existing project that already uses Sidekiq, add the Sidekiq config to your `.rubocop.yml`:
|
|
40
|
+
|
|
41
|
+
```yaml
|
|
42
|
+
inherit_gem:
|
|
43
|
+
rubocop-gusto:
|
|
44
|
+
- config/default.yml
|
|
45
|
+
- config/sidekiq.yml
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
If your project also uses Rails, include `config/rails.yml` as well (order does not matter). Re-run `bundle exec rubocop-gusto init` to merge this in automatically.
|
|
49
|
+
|
|
35
50
|
### Available cops
|
|
36
51
|
|
|
37
52
|
Custom cops live under the following namespaces:
|
|
38
53
|
|
|
39
54
|
- `Gusto/` — general Gusto-specific cops (see [`lib/rubocop/cop/gusto/`](lib/rubocop/cop/gusto/))
|
|
55
|
+
- `Sidekiq/` — cops scoped to Sidekiq patterns (see [`lib/rubocop/cop/sidekiq/`](lib/rubocop/cop/sidekiq/)); configured in [`config/sidekiq.yml`](config/sidekiq.yml)
|
|
40
56
|
- `Rack/` — cops scoped to Rack middleware patterns (see [`lib/rubocop/cop/rack/`](lib/rubocop/cop/rack/))
|
|
41
57
|
|
|
42
58
|
## Publishing New Versions
|
|
@@ -66,7 +82,7 @@ PR titles must use [Conventional Commits](https://www.conventionalcommits.org/)
|
|
|
66
82
|
|
|
67
83
|
### Adding a new cop
|
|
68
84
|
|
|
69
|
-
1. Create `lib/rubocop/cop/gusto/<cop_name>.rb
|
|
85
|
+
1. Create `lib/rubocop/cop/gusto/<cop_name>.rb`, see [how to create a new cop](https://docs.rubocop.org/rubocop/latest/development.html#create-a-new-cop) and [how to choose a name](https://docs.rubocop.org/rubocop-rspec/development.html#choose-a-name)
|
|
70
86
|
2. Add an entry to `config/default.yml`, then sort it:
|
|
71
87
|
```sh
|
|
72
88
|
bundle exec rubocop-gusto sort config/default.yml
|
data/config/default.yml
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
inherit_from: gusto_cops.yml
|
|
2
|
+
|
|
1
3
|
plugins:
|
|
2
4
|
- rubocop-rspec
|
|
3
5
|
- rubocop-performance
|
|
@@ -30,135 +32,6 @@ Gemspec/RequiredRubyVersion:
|
|
|
30
32
|
# We manage our Ruby version in our version file and import that to our Gemfile.
|
|
31
33
|
Enabled: false
|
|
32
34
|
|
|
33
|
-
Gusto/BootsnapLoadFile:
|
|
34
|
-
Description: 'Do not use Bootsnap to load files. Use `require` instead.'
|
|
35
|
-
|
|
36
|
-
Gusto/DatadogConstant:
|
|
37
|
-
Exclude:
|
|
38
|
-
# calling DataDog directly only allowed in initializers, its library, and tests
|
|
39
|
-
- 'config/application.rb'
|
|
40
|
-
- 'config/initializers/datadog.rb'
|
|
41
|
-
- 'lib/datadog/**/*'
|
|
42
|
-
- '**/spec/**/*'
|
|
43
|
-
Description: 'Do not call Datadog directly, use an appropriate wrapper library.'
|
|
44
|
-
|
|
45
|
-
Gusto/DescribedClassConstantReference:
|
|
46
|
-
Description: 'Flags constants scoped through `described_class` (e.g. `described_class::Foo`), which Sorbet cannot resolve statically.'
|
|
47
|
-
Enabled: true
|
|
48
|
-
SafeAutoCorrect: false
|
|
49
|
-
Include:
|
|
50
|
-
- '**/spec/**/*'
|
|
51
|
-
|
|
52
|
-
Gusto/DiscouragedGem:
|
|
53
|
-
Description: 'Flags installation of discouraged gems in Gemfiles and gemspecs.'
|
|
54
|
-
Enabled: false
|
|
55
|
-
Gems: {}
|
|
56
|
-
|
|
57
|
-
Gusto/ExecuteMigration:
|
|
58
|
-
Description: "Don't use `execute` in migrations. Use a backfill rake task instead."
|
|
59
|
-
Include:
|
|
60
|
-
- 'db/migrate/*.rb'
|
|
61
|
-
|
|
62
|
-
Gusto/FactoryClassesOrModules:
|
|
63
|
-
Description: 'Do not define modules or classes in factory directories - they break reloading.'
|
|
64
|
-
Include:
|
|
65
|
-
- 'spec/**/factories/*.rb'
|
|
66
|
-
|
|
67
|
-
Gusto/MinByMaxBy:
|
|
68
|
-
Description: 'Checks for the use of `min` or `max` with a proc. Corrects to `min_by` or `max_by`.'
|
|
69
|
-
Safe: false
|
|
70
|
-
Severity: error
|
|
71
|
-
|
|
72
|
-
Gusto/NoMetaprogramming:
|
|
73
|
-
Description: 'Avoid using metaprogramming techniques like define_method and instance_eval which make code harder to understand and debug.'
|
|
74
|
-
|
|
75
|
-
Gusto/NoRescueErrorMessageChecking:
|
|
76
|
-
Description: 'Checks for the presence of error message checking within rescue blocks.'
|
|
77
|
-
|
|
78
|
-
Gusto/NoSend:
|
|
79
|
-
Description: 'Do not call a private method via `__send__`.'
|
|
80
|
-
Exclude:
|
|
81
|
-
- '**/spec/**/*'
|
|
82
|
-
|
|
83
|
-
Gusto/PaperclipOrAttachable:
|
|
84
|
-
Description: 'No more new paperclip or Attachable are allowed. Use ActiveStorage instead.'
|
|
85
|
-
|
|
86
|
-
Gusto/PerformClassMethod:
|
|
87
|
-
Description: 'Prevents accidental definition of `perform` class methods (should be instance methods instead).'
|
|
88
|
-
# List of modules that include Sidekiq::Worker.
|
|
89
|
-
# Add your other base modules here if they include Sidekiq::Worker too.
|
|
90
|
-
WorkerModules:
|
|
91
|
-
- Sidekiq::Worker
|
|
92
|
-
|
|
93
|
-
Gusto/PolymorphicTypeValidation:
|
|
94
|
-
Description: 'Ensures that polymorphic relations include a type validation, which is necessary for generating Sorbet types.'
|
|
95
|
-
Include:
|
|
96
|
-
- '**/models/**/*.rb'
|
|
97
|
-
|
|
98
|
-
Gusto/PreferProcessLastStatus:
|
|
99
|
-
Description: 'Prefer using `Process.last_status` instead of the global variables: `$?` and `$CHILD_STATUS`.'
|
|
100
|
-
|
|
101
|
-
Gusto/RablExtends:
|
|
102
|
-
Description: 'Disallows the use of `extends` in Rabl templates due to poor caching performance. Inline the templating to generate your JSON instead.'
|
|
103
|
-
Include:
|
|
104
|
-
- '**/*.json.rabl'
|
|
105
|
-
|
|
106
|
-
Gusto/RailsEnv:
|
|
107
|
-
Description: 'Use Feature Flags or config instead of `Rails.env`.'
|
|
108
|
-
|
|
109
|
-
Gusto/RakeConstants:
|
|
110
|
-
Description: 'Do not define a constant in rake file, because they are sometimes `load`ed, instead of `require`d which can lead to warnings about redefining constants.'
|
|
111
|
-
Include:
|
|
112
|
-
- '**/*.rake'
|
|
113
|
-
- 'Rakefile'
|
|
114
|
-
|
|
115
|
-
Gusto/RegexpBypass:
|
|
116
|
-
Description: 'Ensures regular expressions use \A and \z anchors instead of ^ and $ for security validation.'
|
|
117
|
-
Exclude:
|
|
118
|
-
- '**/spec/**/*'
|
|
119
|
-
Safe: false
|
|
120
|
-
|
|
121
|
-
Gusto/RspecDateTimeMock:
|
|
122
|
-
Description: "Don't mock Date/Time/DateTime directly. Use Rails Testing Time Helpers (eg `freeze_time` and `travel_to`) instead."
|
|
123
|
-
Include:
|
|
124
|
-
- '**/spec/**/*'
|
|
125
|
-
Safe: false
|
|
126
|
-
|
|
127
|
-
Gusto/SidekiqParams:
|
|
128
|
-
Description: 'Sidekiq perform methods cannot take keyword arguments.'
|
|
129
|
-
|
|
130
|
-
Gusto/ToplevelConstants:
|
|
131
|
-
Description: 'Prevents top-level constants from being defined outside of initializers.'
|
|
132
|
-
Include:
|
|
133
|
-
- '**/*.rb'
|
|
134
|
-
Exclude:
|
|
135
|
-
- '**/bin/*'
|
|
136
|
-
- 'bin/*'
|
|
137
|
-
- 'config/**/*'
|
|
138
|
-
- 'lib/*.rb'
|
|
139
|
-
- 'packs/**/{db/seeds,lib,config/initializers}/**/*'
|
|
140
|
-
- 'script/**/*'
|
|
141
|
-
- 'spec/rails_helper.rb'
|
|
142
|
-
- '**/spec/support/**/*'
|
|
143
|
-
- '**/*/spec_helper.rb'
|
|
144
|
-
- 'spec/support/**/*.rb'
|
|
145
|
-
|
|
146
|
-
Gusto/UnreferencedLet:
|
|
147
|
-
Description: 'Removes a lazy let whose name is never referenced (its block never runs).'
|
|
148
|
-
Enabled: true
|
|
149
|
-
Include:
|
|
150
|
-
- '**/spec/**/*_spec.rb'
|
|
151
|
-
# Deletion is unsafe (explicit -A required, never applied on a plain run): the cop is heuristic
|
|
152
|
-
# and cannot see references made across files via shared examples or included harnesses.
|
|
153
|
-
SafeAutoCorrect: false
|
|
154
|
-
|
|
155
|
-
Gusto/UsePaintNotColorize:
|
|
156
|
-
Description: 'Use Paint instead of colorize for terminal colors.'
|
|
157
|
-
SafeAutoCorrect: false
|
|
158
|
-
|
|
159
|
-
Gusto/VcrRecordings:
|
|
160
|
-
Description: 'VCR should be set to not record in tests. Use vcr: {record: :none}.'
|
|
161
|
-
|
|
162
35
|
Layout/BlockAlignment:
|
|
163
36
|
EnforcedStyleAlignWith: start_of_block
|
|
164
37
|
|
|
@@ -389,6 +262,13 @@ Rack/LowercaseHeaderKeys:
|
|
|
389
262
|
Rake/ClassDefinitionInTask:
|
|
390
263
|
Enabled: false
|
|
391
264
|
|
|
265
|
+
Sidekiq/PerformAsyncStub:
|
|
266
|
+
Description: 'Prefer checking enqueued jobs over stubbing `perform_async`.'
|
|
267
|
+
Enabled: false
|
|
268
|
+
SafeAutoCorrect: false # Autocorrect appends `.and_call_original`, which runs real `perform_async` during the example
|
|
269
|
+
Include:
|
|
270
|
+
- '**/spec/**/*_spec.rb'
|
|
271
|
+
|
|
392
272
|
Sorbet/ForbidTUnsafe:
|
|
393
273
|
# T.unsafe completely disables typechecking. Prefer T.cast, shims, or
|
|
394
274
|
# reorganizing the code over silently turning off the type checker.
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
# Gusto cop defaults only — no built-in cop overrides.
|
|
2
|
+
# Use this with `inherit_gem` when you want Gusto cop scoping (Include/Exclude/Severity)
|
|
3
|
+
# without inheriting rubocop-gusto's full configuration layer.
|
|
4
|
+
#
|
|
5
|
+
# In your .rubocop.yml:
|
|
6
|
+
# require:
|
|
7
|
+
# - rubocop/cop/gusto/all
|
|
8
|
+
# inherit_gem:
|
|
9
|
+
# rubocop-gusto:
|
|
10
|
+
# - config/gusto_cops.yml
|
|
11
|
+
|
|
12
|
+
Gusto/BootsnapLoadFile:
|
|
13
|
+
Description: 'Do not use Bootsnap to load files. Use `require` instead.'
|
|
14
|
+
|
|
15
|
+
Gusto/DatadogConstant:
|
|
16
|
+
Exclude:
|
|
17
|
+
# calling DataDog directly only allowed in initializers, its library, and tests
|
|
18
|
+
- 'config/application.rb'
|
|
19
|
+
- 'config/initializers/datadog.rb'
|
|
20
|
+
- 'lib/datadog/**/*'
|
|
21
|
+
- '**/spec/**/*'
|
|
22
|
+
Description: 'Do not call Datadog directly, use an appropriate wrapper library.'
|
|
23
|
+
|
|
24
|
+
Gusto/DescribedClassConstantReference:
|
|
25
|
+
Description: 'Flags constants scoped through `described_class` (e.g. `described_class::Foo`), which Sorbet cannot resolve statically.'
|
|
26
|
+
Enabled: true
|
|
27
|
+
SafeAutoCorrect: false
|
|
28
|
+
Include:
|
|
29
|
+
- '**/spec/**/*'
|
|
30
|
+
|
|
31
|
+
Gusto/DiscouragedGem:
|
|
32
|
+
Description: 'Flags installation of discouraged gems in Gemfiles and gemspecs.'
|
|
33
|
+
Enabled: false
|
|
34
|
+
Gems: {}
|
|
35
|
+
|
|
36
|
+
Gusto/ExecuteMigration:
|
|
37
|
+
Description: "Don't use `execute` in migrations. Use a backfill rake task instead."
|
|
38
|
+
Include:
|
|
39
|
+
- 'db/migrate/*.rb'
|
|
40
|
+
|
|
41
|
+
Gusto/FactoryClassesOrModules:
|
|
42
|
+
Description: 'Do not define modules or classes in factory directories - they break reloading.'
|
|
43
|
+
Include:
|
|
44
|
+
- 'spec/**/factories/*.rb'
|
|
45
|
+
|
|
46
|
+
Gusto/MinByMaxBy:
|
|
47
|
+
Description: 'Checks for the use of `min` or `max` with a proc. Corrects to `min_by` or `max_by`.'
|
|
48
|
+
Safe: false
|
|
49
|
+
Severity: error
|
|
50
|
+
|
|
51
|
+
Gusto/NoMetaprogramming:
|
|
52
|
+
Description: 'Avoid using metaprogramming techniques like define_method and instance_eval which make code harder to understand and debug.'
|
|
53
|
+
|
|
54
|
+
Gusto/NoRescueErrorMessageChecking:
|
|
55
|
+
Description: 'Checks for the presence of error message checking within rescue blocks.'
|
|
56
|
+
|
|
57
|
+
Gusto/NoSend:
|
|
58
|
+
Description: 'Do not call a private method via `__send__`.'
|
|
59
|
+
Exclude:
|
|
60
|
+
- '**/spec/**/*'
|
|
61
|
+
|
|
62
|
+
Gusto/PaperclipOrAttachable:
|
|
63
|
+
Description: 'No more new paperclip or Attachable are allowed. Use ActiveStorage instead.'
|
|
64
|
+
|
|
65
|
+
Gusto/PerformClassMethod:
|
|
66
|
+
Description: 'Prevents accidental definition of `perform` class methods (should be instance methods instead).'
|
|
67
|
+
WorkerModules:
|
|
68
|
+
- Sidekiq::Worker
|
|
69
|
+
|
|
70
|
+
Gusto/PolymorphicTypeValidation:
|
|
71
|
+
Description: 'Ensures that polymorphic relations include a type validation, which is necessary for generating Sorbet types.'
|
|
72
|
+
Include:
|
|
73
|
+
- '**/models/**/*.rb'
|
|
74
|
+
|
|
75
|
+
Gusto/PreferProcessLastStatus:
|
|
76
|
+
Description: 'Prefer using `Process.last_status` instead of the global variables: `$?` and `$CHILD_STATUS`.'
|
|
77
|
+
|
|
78
|
+
Gusto/RablExtends:
|
|
79
|
+
Description: 'Disallows the use of `extends` in Rabl templates due to poor caching performance. Inline the templating to generate your JSON instead.'
|
|
80
|
+
Include:
|
|
81
|
+
- '**/*.json.rabl'
|
|
82
|
+
|
|
83
|
+
Gusto/RailsEnv:
|
|
84
|
+
Description: 'Use Feature Flags or config instead of `Rails.env`.'
|
|
85
|
+
|
|
86
|
+
Gusto/RakeConstants:
|
|
87
|
+
Description: 'Do not define a constant in rake file, because they are sometimes `load`ed, instead of `require`d which can lead to warnings about redefining constants.'
|
|
88
|
+
Include:
|
|
89
|
+
- '**/*.rake'
|
|
90
|
+
- 'Rakefile'
|
|
91
|
+
|
|
92
|
+
Gusto/RegexpBypass:
|
|
93
|
+
Description: 'Ensures regular expressions use \A and \z anchors instead of ^ and $ for security validation.'
|
|
94
|
+
Exclude:
|
|
95
|
+
- '**/spec/**/*'
|
|
96
|
+
Safe: false
|
|
97
|
+
|
|
98
|
+
Gusto/RspecDateTimeMock:
|
|
99
|
+
Description: "Don't mock Date/Time/DateTime directly. Use Rails Testing Time Helpers (eg `freeze_time` and `travel_to`) instead."
|
|
100
|
+
Include:
|
|
101
|
+
- '**/spec/**/*'
|
|
102
|
+
Safe: false
|
|
103
|
+
|
|
104
|
+
Gusto/SidekiqParams:
|
|
105
|
+
Description: 'Sidekiq perform methods cannot take keyword arguments.'
|
|
106
|
+
|
|
107
|
+
Gusto/ToplevelConstants:
|
|
108
|
+
Description: 'Prevents top-level constants from being defined outside of initializers.'
|
|
109
|
+
Include:
|
|
110
|
+
- '**/*.rb'
|
|
111
|
+
Exclude:
|
|
112
|
+
- '**/bin/*'
|
|
113
|
+
- 'bin/*'
|
|
114
|
+
- 'config/**/*'
|
|
115
|
+
- 'lib/*.rb'
|
|
116
|
+
- 'packs/**/{db/seeds,lib,config/initializers}/**/*'
|
|
117
|
+
- 'script/**/*'
|
|
118
|
+
- 'spec/rails_helper.rb'
|
|
119
|
+
- '**/spec/support/**/*'
|
|
120
|
+
- '**/*/spec_helper.rb'
|
|
121
|
+
- 'spec/support/**/*.rb'
|
|
122
|
+
|
|
123
|
+
Gusto/UnreferencedLet:
|
|
124
|
+
Description: 'Removes a lazy let whose name is never referenced (its block never runs).'
|
|
125
|
+
Enabled: true
|
|
126
|
+
Include:
|
|
127
|
+
- '**/spec/**/*_spec.rb'
|
|
128
|
+
SafeAutoCorrect: false
|
|
129
|
+
|
|
130
|
+
Gusto/UsePaintNotColorize:
|
|
131
|
+
Description: 'Use Paint instead of colorize for terminal colors.'
|
|
132
|
+
SafeAutoCorrect: false
|
|
133
|
+
|
|
134
|
+
Gusto/VcrRecordings:
|
|
135
|
+
Description: 'VCR should be set to not record in tests. Use vcr: {record: :none}.'
|
data/config/sidekiq.yml
ADDED
|
@@ -3,7 +3,17 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Gusto
|
|
6
|
-
#
|
|
6
|
+
# Prefer `YAML.load_file`/`JSON.load_file` over reading a file and then
|
|
7
|
+
# parsing its contents. Bootsnap caches the parsed result of `load_file`,
|
|
8
|
+
# so this improves load time.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# # bad
|
|
12
|
+
# YAML.load(File.read("config.yml"))
|
|
13
|
+
# File.open("config.yml") { |f| YAML.load(f) }
|
|
14
|
+
#
|
|
15
|
+
# # good
|
|
16
|
+
# YAML.load_file("config.yml")
|
|
7
17
|
class BootsnapLoadFile < Base
|
|
8
18
|
PROHIBITED_CONSTANTS = Set[:YAML, :JSON].freeze
|
|
9
19
|
RESTRICT_ON_SEND = %i(load).freeze
|
|
@@ -3,6 +3,16 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Gusto
|
|
6
|
+
# Disallow referencing the `Datadog` constant directly. Calls should go
|
|
7
|
+
# through an approved wrapper library so instrumentation stays consistent
|
|
8
|
+
# and swappable.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# # bad
|
|
12
|
+
# Datadog::Tracing.active_trace
|
|
13
|
+
#
|
|
14
|
+
# # good
|
|
15
|
+
# Observability.active_trace
|
|
6
16
|
class DatadogConstant < Base
|
|
7
17
|
MSG = "Do not call Datadog directly, use an appropriate wrapper library."
|
|
8
18
|
NAMESPACE = "Datadog"
|
|
@@ -3,13 +3,16 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Gusto
|
|
6
|
-
#
|
|
6
|
+
# Flag installation of discouraged gems (e.g. timecop) in Gemfiles and
|
|
7
|
+
# gemspecs. The discouraged gems an advice about alternatives are configured under
|
|
8
|
+
# `Gems:`; intended to be enabled in Rails projects via config/rails.yml.
|
|
7
9
|
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
10
|
+
# @example Gems: { timecop: "Use Rails' time helpers instead of Timecop." }
|
|
11
|
+
# # bad
|
|
12
|
+
# gem "timecop"
|
|
11
13
|
#
|
|
12
|
-
#
|
|
14
|
+
# # good
|
|
15
|
+
# # Use Rails' time helpers (freeze_time, travel_to) instead.
|
|
13
16
|
class DiscouragedGem < Base
|
|
14
17
|
MSG = "Avoid using the '%{gem}' gem. %{advice}"
|
|
15
18
|
|
|
@@ -3,6 +3,20 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Gusto
|
|
6
|
+
# Disallow `execute` (raw SQL) in migrations. Run raw SQL from a backfill
|
|
7
|
+
# rake task, or pass SQL options to `add_column`/`change_column` instead,
|
|
8
|
+
# so migrations stay reversible and schema-focused.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# # bad
|
|
12
|
+
# def up
|
|
13
|
+
# execute("UPDATE users SET active = true")
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# # good
|
|
17
|
+
# def up
|
|
18
|
+
# add_column :users, :active, :boolean, default: true
|
|
19
|
+
# end
|
|
6
20
|
class ExecuteMigration < Base
|
|
7
21
|
MSG = "Do not use `execute` to run raw SQL in a migration. Run the query from a backfill rake task or pass the SQL options to the `add_column`/`change_column` method."
|
|
8
22
|
RESTRICT_ON_SEND = [:execute].freeze
|
|
@@ -3,6 +3,24 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Gusto
|
|
6
|
+
# Disallow defining classes or modules in factory directories. They break
|
|
7
|
+
# Rails autoloading/reloading; define shared helpers outside the factories.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# # bad
|
|
11
|
+
# # spec/factories/users.rb
|
|
12
|
+
# class UserHelper
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# FactoryBot.define do
|
|
16
|
+
# factory :user
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# # good
|
|
20
|
+
# # spec/factories/users.rb
|
|
21
|
+
# FactoryBot.define do
|
|
22
|
+
# factory :user
|
|
23
|
+
# end
|
|
6
24
|
class FactoryClassesOrModules < Base
|
|
7
25
|
MSG = "Do not define modules or classes in factory directories - they break reloading"
|
|
8
26
|
|
|
@@ -4,6 +4,15 @@
|
|
|
4
4
|
module RuboCop
|
|
5
5
|
module Cop
|
|
6
6
|
module Gusto
|
|
7
|
+
# Disallow new Paperclip / Attachable attachments. New attachments should
|
|
8
|
+
# use ActiveStorage instead.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# # bad
|
|
12
|
+
# has_attached_file :avatar
|
|
13
|
+
#
|
|
14
|
+
# # good
|
|
15
|
+
# has_one_attached :avatar
|
|
7
16
|
class PaperclipOrAttachable < Base
|
|
8
17
|
MSG = "No more new paperclip or Attachable are allowed. New attachments should use ActiveStorage instead"
|
|
9
18
|
RESTRICT_ON_SEND = %i(has_attached_file has_pdf_attachment has_attachment).freeze
|
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# This cop enforces that polymorphic relations have a corresponding validation
|
|
4
|
-
# for their type field with an inclusion validation. This is required in order for Tapioca
|
|
5
|
-
# to generate correct Sorbet types
|
|
6
3
|
module RuboCop
|
|
7
4
|
module Cop
|
|
8
5
|
module Gusto
|
|
6
|
+
# Require polymorphic relations to validate their `*_type` field with an
|
|
7
|
+
# inclusion validation (or `polymorphic_methods_for`). This is needed for
|
|
8
|
+
# Tapioca to generate correct Sorbet types.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# # bad
|
|
12
|
+
# belongs_to :subscription_detail, polymorphic: true
|
|
13
|
+
#
|
|
14
|
+
# # good
|
|
15
|
+
# VALID_TYPES = T.let([Foo.polymorphic_name, Bar.polymorphic_name].freeze, T::Array[String])
|
|
16
|
+
# belongs_to :subscription_detail, polymorphic: true
|
|
17
|
+
# validates :subscription_detail_type, presence: true, inclusion: { in: VALID_TYPES }
|
|
18
|
+
#
|
|
19
|
+
# # also good
|
|
20
|
+
# include PolymorphicCallable
|
|
21
|
+
# VALID_TYPES = T.let([Foo.polymorphic_name, Bar.polymorphic_name].freeze, T::Array[String])
|
|
22
|
+
# belongs_to :subscription_detail, polymorphic: true
|
|
23
|
+
# polymorphic_methods_for :subscription_detail, VALID_TYPES
|
|
9
24
|
class PolymorphicTypeValidation < Base
|
|
10
25
|
RESTRICT_ON_SEND = %i(belongs_to validates polymorphic_methods_for).freeze
|
|
11
26
|
|
|
@@ -3,6 +3,18 @@
|
|
|
3
3
|
module RuboCop
|
|
4
4
|
module Cop
|
|
5
5
|
module Gusto
|
|
6
|
+
# Disallow keyword arguments on Sidekiq `perform` methods. Sidekiq
|
|
7
|
+
# serializes job arguments as JSON and replays them positionally, so
|
|
8
|
+
# keyword arguments are not preserved.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
# # bad
|
|
12
|
+
# def perform(user_id:, force: false)
|
|
13
|
+
# end
|
|
14
|
+
#
|
|
15
|
+
# # good
|
|
16
|
+
# def perform(user_id, force = false)
|
|
17
|
+
# end
|
|
6
18
|
class SidekiqParams < Base
|
|
7
19
|
MSG = "Sidekiq perform methods cannot take keyword arguments"
|
|
8
20
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require "open3"
|
|
4
|
+
require "rubocop-rspec"
|
|
4
5
|
|
|
5
6
|
module RuboCop
|
|
6
7
|
module Cop
|
|
@@ -54,7 +55,7 @@ module RuboCop
|
|
|
54
55
|
extend AutoCorrector
|
|
55
56
|
include RangeHelp
|
|
56
57
|
|
|
57
|
-
DEFINITION_METHODS =
|
|
58
|
+
DEFINITION_METHODS = Set[:let, :let!, :subject].freeze
|
|
58
59
|
# `let`s consumed by a test framework rather than by a reference in the spec file. The
|
|
59
60
|
# rubocop-rspec `:config` shared context reads `cop_config`, so it is live even though the
|
|
60
61
|
# spec never names it.
|
|
@@ -80,7 +81,7 @@ module RuboCop
|
|
|
80
81
|
# `subject` that overrides a `let` of the same name) are never flagged.
|
|
81
82
|
# @!method definition_name(node)
|
|
82
83
|
def_node_matcher :definition_name, <<~PATTERN
|
|
83
|
-
(any_block (send nil?
|
|
84
|
+
(any_block (send nil? %DEFINITION_METHODS (sym $_) ...) ...)
|
|
84
85
|
PATTERN
|
|
85
86
|
|
|
86
87
|
class << self
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Sidekiq
|
|
6
|
+
# Checks that `perform_async` calls to enqueue Sidekiq jobs are not stubbed
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# # bad
|
|
10
|
+
# allow(Foo).to receive(:perform_async)
|
|
11
|
+
# expect(Foo).to receive(:perform_async)
|
|
12
|
+
# expect(Foo).not_to receive(:perform_async)
|
|
13
|
+
#
|
|
14
|
+
# # good (still invokes the real method)
|
|
15
|
+
# allow(Foo).to receive(:perform_async).and_call_original
|
|
16
|
+
# expect(Foo).to receive(:perform_async).with(arg).and_call_original
|
|
17
|
+
# allow(Foo).to receive(:perform_async).and_wrap_original { |m, *args| m.call(*args) }
|
|
18
|
+
#
|
|
19
|
+
# # good (checking enqueued jobs)
|
|
20
|
+
# expect { subject }.to change(Foo.jobs, :count).by(n)
|
|
21
|
+
# expect { subject }.not_to change(Foo.jobs, :count)
|
|
22
|
+
# expect(Foo.jobs.count).to eq(n)
|
|
23
|
+
#
|
|
24
|
+
# # good (only checks previously pre-stubbed objects)
|
|
25
|
+
# expect(Foo).to have_received(:perform_async)
|
|
26
|
+
#
|
|
27
|
+
# @safety
|
|
28
|
+
# Autocorrect is unsafe: it appends `.and_call_original` on positive `receive` only, which runs
|
|
29
|
+
# the real `perform_async` during the example (may enqueue jobs, hit external code, or
|
|
30
|
+
# change expectations vs a pure stub). There is no autocorrect for `not_to` / `to_not receive`,
|
|
31
|
+
# since `.and_call_original` would not apply to a negative expectation. Autocorrect is also
|
|
32
|
+
# suppressed when the expectation uses a block, since appending `.and_call_original` would
|
|
33
|
+
# rebind the block to the wrong method.
|
|
34
|
+
class PerformAsyncStub < Base
|
|
35
|
+
extend AutoCorrector
|
|
36
|
+
|
|
37
|
+
MSG = "Prefer checking enqueued jobs over stubbing `perform_async`."
|
|
38
|
+
MSG_RECEIVE = "Prefer checking enqueued jobs over stubbing `perform_async` or add `.and_call_original`."
|
|
39
|
+
RESTRICT_ON_SEND = %i(receive).freeze
|
|
40
|
+
|
|
41
|
+
# TODO: this should match on perform_async, not on receive, requires pattern update
|
|
42
|
+
# @!method stub_perform_async?(node)
|
|
43
|
+
def_node_matcher :stub_perform_async?, <<~PATTERN
|
|
44
|
+
(send nil? :receive (sym :perform_async))
|
|
45
|
+
PATTERN
|
|
46
|
+
|
|
47
|
+
def on_send(node)
|
|
48
|
+
return unless stub_perform_async?(node)
|
|
49
|
+
|
|
50
|
+
negative_expectation = false
|
|
51
|
+
calls_original = false
|
|
52
|
+
|
|
53
|
+
current = node.parent
|
|
54
|
+
while current&.call_type?
|
|
55
|
+
negative_expectation = true if current.method?(:not_to) || current.method?(:to_not)
|
|
56
|
+
calls_original = true if current.method?(:and_call_original) || current.method?(:and_wrap_original)
|
|
57
|
+
|
|
58
|
+
current = current.parent
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
return add_offense(node) if negative_expectation
|
|
62
|
+
return if calls_original # already have .and_call_original, not an offense
|
|
63
|
+
|
|
64
|
+
tail = message_expectation_chain_tail(node)
|
|
65
|
+
return add_offense(node, message: MSG_RECEIVE) if tail.parent&.block_type?
|
|
66
|
+
|
|
67
|
+
add_offense(node, message: MSG_RECEIVE) do |corrector|
|
|
68
|
+
corrector.insert_after(tail, ".and_call_original")
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
alias_method :on_csend, :on_send
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
def message_expectation_chain_tail(node)
|
|
77
|
+
tail = node
|
|
78
|
+
loop do
|
|
79
|
+
parent = tail.parent
|
|
80
|
+
break unless parent&.call_type?
|
|
81
|
+
break unless parent.receiver.equal?(tail)
|
|
82
|
+
|
|
83
|
+
tail = parent
|
|
84
|
+
end
|
|
85
|
+
tail
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
data/lib/rubocop/gusto/init.rb
CHANGED
|
@@ -10,6 +10,8 @@ module RuboCop
|
|
|
10
10
|
include Thor::Actions
|
|
11
11
|
|
|
12
12
|
PLUGINS = %w(rubocop-gusto rubocop-rspec rubocop-performance rubocop-rake rubocop-rails).freeze
|
|
13
|
+
SIDEKIQ_GEM_PATTERN = /\A\s*gem\s+['"]sidekiq['"]/
|
|
14
|
+
SIDEKIQ_LOCKFILE_PATTERN = /\A\s+sidekiq\s+\(/
|
|
13
15
|
|
|
14
16
|
class_option :rubocop_yml, type: :string, default: ".rubocop.yml"
|
|
15
17
|
|
|
@@ -34,13 +36,8 @@ module RuboCop
|
|
|
34
36
|
config = ConfigYml.load_file(options[:rubocop_yml])
|
|
35
37
|
end
|
|
36
38
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
config.add_plugin(PLUGINS)
|
|
40
|
-
else
|
|
41
|
-
config.add_inherit_gem("rubocop-gusto", "config/default.yml")
|
|
42
|
-
config.add_plugin(PLUGINS - %w(rubocop-rails))
|
|
43
|
-
end
|
|
39
|
+
config.add_inherit_gem("rubocop-gusto", *inherit_gem_configs)
|
|
40
|
+
config.add_plugin(rails? ? PLUGINS : PLUGINS - %w(rubocop-rails))
|
|
44
41
|
|
|
45
42
|
config.sort!
|
|
46
43
|
config.write(options[:rubocop_yml])
|
|
@@ -51,9 +48,25 @@ module RuboCop
|
|
|
51
48
|
|
|
52
49
|
private
|
|
53
50
|
|
|
51
|
+
def inherit_gem_configs
|
|
52
|
+
configs = ["config/default.yml"]
|
|
53
|
+
configs << "config/rails.yml" if rails?
|
|
54
|
+
configs << "config/sidekiq.yml" if sidekiq?
|
|
55
|
+
configs
|
|
56
|
+
end
|
|
57
|
+
|
|
54
58
|
def rails?
|
|
55
59
|
File.exist?("config/application.rb")
|
|
56
60
|
end
|
|
61
|
+
|
|
62
|
+
def sidekiq?
|
|
63
|
+
gem_referenced?("Gemfile", SIDEKIQ_GEM_PATTERN) ||
|
|
64
|
+
gem_referenced?("Gemfile.lock", SIDEKIQ_LOCKFILE_PATTERN)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def gem_referenced?(path, pattern)
|
|
68
|
+
File.exist?(path) && File.foreach(path).any? { |line| line.match?(pattern) }
|
|
69
|
+
end
|
|
57
70
|
end
|
|
58
71
|
end
|
|
59
72
|
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubocop-gusto
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 11.
|
|
4
|
+
version: 11.1.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gusto Engineering
|
|
@@ -119,10 +119,13 @@ files:
|
|
|
119
119
|
- LICENSE
|
|
120
120
|
- README.md
|
|
121
121
|
- config/default.yml
|
|
122
|
+
- config/gusto_cops.yml
|
|
122
123
|
- config/rails.yml
|
|
124
|
+
- config/sidekiq.yml
|
|
123
125
|
- exe/gusto-rubocop
|
|
124
126
|
- exe/rubocop-gusto
|
|
125
127
|
- lib/rubocop-gusto.rb
|
|
128
|
+
- lib/rubocop/cop/gusto/all.rb
|
|
126
129
|
- lib/rubocop/cop/gusto/bootsnap_load_file.rb
|
|
127
130
|
- lib/rubocop/cop/gusto/datadog_constant.rb
|
|
128
131
|
- lib/rubocop/cop/gusto/described_class_constant_reference.rb
|
|
@@ -151,6 +154,7 @@ files:
|
|
|
151
154
|
- lib/rubocop/cop/internal_affairs/require_restrict_on_send.rb
|
|
152
155
|
- lib/rubocop/cop/rack/lowercase_header_keys.rb
|
|
153
156
|
- lib/rubocop/cop/rspec/scattered_let.rb
|
|
157
|
+
- lib/rubocop/cop/sidekiq/perform_async_stub.rb
|
|
154
158
|
- lib/rubocop/gusto.rb
|
|
155
159
|
- lib/rubocop/gusto/cli.rb
|
|
156
160
|
- lib/rubocop/gusto/config_yml.rb
|
|
@@ -171,7 +175,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
171
175
|
requirements:
|
|
172
176
|
- - ">="
|
|
173
177
|
- !ruby/object:Gem::Version
|
|
174
|
-
version: '3.
|
|
178
|
+
version: '3.3'
|
|
175
179
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
176
180
|
requirements:
|
|
177
181
|
- - ">="
|