rubocop-gusto 11.1.1 → 11.2.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 +15 -0
- data/config/gusto_cops.yml +11 -3
- data/config/rails.yml +6 -0
- data/lib/rubocop/cop/gusto/pluck_on_select.rb +57 -0
- data/lib/rubocop/cop/gusto/smart_todo_team.rb +50 -0
- data/lib/rubocop/cop/gusto/unreferenced_let.rb +1 -1
- data/lib/rubocop/gusto/version.rb +1 -1
- metadata +31 -2
- data/lib/rubocop/cop/gusto/rails_env.rb +0 -77
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: bd7d9b067df7edfe822d63daa4c1ffb54b54086cbf80f185ca20888a513d88ec
|
|
4
|
+
data.tar.gz: 4f62f8d399612f2aa26e929b765b33e54324bee3306c27f515b43e15f0c3111f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5393a0a5937de49f0f70bfd74f401bcabe17a0c24292a9921277d2f81dbf51661e730995b16db0c3de5888ef7e3842236016f156fc94b7446f1eb8693a8beb77
|
|
7
|
+
data.tar.gz: 53114fe101a37c7d0a34f291e038b70c16186c8b4352991d7b7bd03e40b8b293281e62b1adc8fd4d2c6fdeabcc143bd7ff262baaef4c471e5e895f00e843803d
|
data/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,21 @@
|
|
|
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.2.0](https://github.com/Gusto/rubocop-gusto/compare/v11.1.1...v11.2.0) (2026-06-26)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
* add Gusto/PluckOnSelect cop ([#137](https://github.com/Gusto/rubocop-gusto/issues/137)) ([d87a5cc](https://github.com/Gusto/rubocop-gusto/commit/d87a5ccf45f3f0fca89ed6b6e28cd6f61fc8b023))
|
|
12
|
+
* add Gusto/SmartTodoTeam cop enforcing valid team in TODOs (RR-866) ([#143](https://github.com/Gusto/rubocop-gusto/issues/143)) ([8d36bf5](https://github.com/Gusto/rubocop-gusto/commit/8d36bf58983c32d48ff90b3862eb273f543e60a7))
|
|
13
|
+
* Ensure rubocop-gusto has access to CodeTeams everywhere it runs (RR-880) ([#141](https://github.com/Gusto/rubocop-gusto/issues/141)) ([b2184ba](https://github.com/Gusto/rubocop-gusto/commit/b2184ba3430359e1210206a09746c979f3826ce3))
|
|
14
|
+
* replace Gusto/RailsEnv with upstream Rails/Env ([#106](https://github.com/Gusto/rubocop-gusto/issues/106)) ([7e00033](https://github.com/Gusto/rubocop-gusto/commit/7e0003347dcbed728e4346a7ec2b4f215923a69d))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
### Bug Fixes
|
|
18
|
+
|
|
19
|
+
* use UTF-8 encoding when reading support files in UnreferencedLet ([#136](https://github.com/Gusto/rubocop-gusto/issues/136)) ([651dd25](https://github.com/Gusto/rubocop-gusto/commit/651dd252843a5cfd81dc72bab0abc4b57143c138))
|
|
20
|
+
|
|
6
21
|
## [11.1.1](https://github.com/Gusto/rubocop-gusto/compare/v11.1.0...v11.1.1) (2026-06-18)
|
|
7
22
|
|
|
8
23
|
|
data/config/gusto_cops.yml
CHANGED
|
@@ -67,6 +67,9 @@ Gusto/PerformClassMethod:
|
|
|
67
67
|
WorkerModules:
|
|
68
68
|
- Sidekiq::Worker
|
|
69
69
|
|
|
70
|
+
Gusto/PluckOnSelect:
|
|
71
|
+
Description: "Do not use `.pluck` on `.select`. Use one or the other."
|
|
72
|
+
|
|
70
73
|
Gusto/PolymorphicTypeValidation:
|
|
71
74
|
Description: 'Ensures that polymorphic relations include a type validation, which is necessary for generating Sorbet types.'
|
|
72
75
|
Include:
|
|
@@ -80,9 +83,6 @@ Gusto/RablExtends:
|
|
|
80
83
|
Include:
|
|
81
84
|
- '**/*.json.rabl'
|
|
82
85
|
|
|
83
|
-
Gusto/RailsEnv:
|
|
84
|
-
Description: 'Use Feature Flags or config instead of `Rails.env`.'
|
|
85
|
-
|
|
86
86
|
Gusto/RakeConstants:
|
|
87
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
88
|
Include:
|
|
@@ -104,6 +104,10 @@ Gusto/RspecDateTimeMock:
|
|
|
104
104
|
Gusto/SidekiqParams:
|
|
105
105
|
Description: 'Sidekiq perform methods cannot take keyword arguments.'
|
|
106
106
|
|
|
107
|
+
Gusto/SmartTodoTeam:
|
|
108
|
+
Description: 'TODO comments must be SmartTodo-formatted and target a valid team (CodeTeams).'
|
|
109
|
+
Enabled: false
|
|
110
|
+
|
|
107
111
|
Gusto/ToplevelConstants:
|
|
108
112
|
Description: 'Prevents top-level constants from being defined outside of initializers.'
|
|
109
113
|
Include:
|
|
@@ -133,3 +137,7 @@ Gusto/UsePaintNotColorize:
|
|
|
133
137
|
|
|
134
138
|
Gusto/VcrRecordings:
|
|
135
139
|
Description: 'VCR should be set to not record in tests. Use vcr: {record: :none}.'
|
|
140
|
+
|
|
141
|
+
# We extend this via Gusto/SmartTodoTeam; don't let the upstream cop run standalone.
|
|
142
|
+
SmartTodo/SmartTodoCop:
|
|
143
|
+
Enabled: false
|
data/config/rails.yml
CHANGED
|
@@ -18,6 +18,9 @@ Gusto/DiscouragedGem:
|
|
|
18
18
|
Gems:
|
|
19
19
|
timecop: "Use Rails' time helpers (e.g., freeze_time, travel_to) instead of Timecop."
|
|
20
20
|
|
|
21
|
+
Gusto/PluckOnSelect:
|
|
22
|
+
Description: "Do not use `.pluck` on `.select`. Use one or the other."
|
|
23
|
+
|
|
21
24
|
Performance/DoubleStartEndWith:
|
|
22
25
|
IncludeActiveSupportAliases: true
|
|
23
26
|
|
|
@@ -55,6 +58,9 @@ Rails/EnumHash:
|
|
|
55
58
|
Include:
|
|
56
59
|
- '**/app/models/**/*'
|
|
57
60
|
|
|
61
|
+
Rails/Env:
|
|
62
|
+
Enabled: true
|
|
63
|
+
|
|
58
64
|
Rails/Exit:
|
|
59
65
|
Include:
|
|
60
66
|
- app/**/*
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Gusto
|
|
6
|
+
# Do not use `.pluck` on `.select`.
|
|
7
|
+
#
|
|
8
|
+
# `.select` returns an ActiveRecord relation with only the selected columns marked for
|
|
9
|
+
# retrieval. `.pluck` returns an array of column values. When chained, `.pluck` is unaware
|
|
10
|
+
# of any directive passed to `.select` (e.g. column aliases or a DISTINCT clause), which
|
|
11
|
+
# can cause unexpected behavior.
|
|
12
|
+
#
|
|
13
|
+
# @example Redundant select
|
|
14
|
+
# # bad
|
|
15
|
+
# User.select(:id).pluck(:id)
|
|
16
|
+
#
|
|
17
|
+
# # good
|
|
18
|
+
# User.pluck(:id)
|
|
19
|
+
#
|
|
20
|
+
# @example Column alias: .pluck raises "Unknown column" because it ignores the alias
|
|
21
|
+
# # bad
|
|
22
|
+
# User.select('id AS id2').pluck('id2')
|
|
23
|
+
#
|
|
24
|
+
# # good: use .select alone if you need the alias
|
|
25
|
+
# User.select(:id, 'id AS id2')
|
|
26
|
+
#
|
|
27
|
+
# # good: use .pluck alone if you don't need the alias
|
|
28
|
+
# User.pluck(:id)
|
|
29
|
+
#
|
|
30
|
+
# @example DISTINCT: .pluck loads all rows, ignoring the DISTINCT from .select
|
|
31
|
+
# # bad
|
|
32
|
+
# User.select('DISTINCT email').pluck(:email)
|
|
33
|
+
#
|
|
34
|
+
# # good
|
|
35
|
+
# User.distinct.pluck(:email)
|
|
36
|
+
#
|
|
37
|
+
class PluckOnSelect < Base
|
|
38
|
+
RESTRICT_ON_SEND = %i(pluck).freeze
|
|
39
|
+
MSG = "Do not use `.pluck` on `.select`."
|
|
40
|
+
|
|
41
|
+
def on_send(node)
|
|
42
|
+
return unless node.receiver
|
|
43
|
+
|
|
44
|
+
receiver_node = node.receiver
|
|
45
|
+
while receiver_node
|
|
46
|
+
if receiver_node.call_type? && receiver_node.method?(:select)
|
|
47
|
+
add_offense(node)
|
|
48
|
+
break
|
|
49
|
+
end
|
|
50
|
+
receiver_node = receiver_node.receiver
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
alias_method :on_csend, :on_send
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "smart_todo_cop" # defines RuboCop::Cop::SmartTodo::SmartTodoCop (also requires "smart_todo")
|
|
4
|
+
require "code_teams"
|
|
5
|
+
|
|
6
|
+
module RuboCop
|
|
7
|
+
module Cop
|
|
8
|
+
module Gusto
|
|
9
|
+
# Enforces SmartTodo syntax (via the upstream `SmartTodo/SmartTodoCop`) and,
|
|
10
|
+
# in addition, requires every TODO's `to:` assignee to name a valid team as
|
|
11
|
+
# defined by CodeTeams (`config/teams/**/*.yml`).
|
|
12
|
+
#
|
|
13
|
+
# All of the upstream cop's failure modes are preserved verbatim via `super`.
|
|
14
|
+
# The only additional offense is raised when an otherwise-valid SmartTodo
|
|
15
|
+
# comment is assigned to an unknown team.
|
|
16
|
+
#
|
|
17
|
+
# @example
|
|
18
|
+
# # bad - assigned to an unknown team
|
|
19
|
+
# # TODO(on: date('2025-01-01'), to: 'NotATeam')
|
|
20
|
+
# # Remove this
|
|
21
|
+
#
|
|
22
|
+
# # good - assigned to a team in config/teams
|
|
23
|
+
# # TODO(on: date('2025-01-01'), to: 'Payroll')
|
|
24
|
+
# # Remove this
|
|
25
|
+
class SmartTodoTeam < ::RuboCop::Cop::SmartTodo::SmartTodoCop
|
|
26
|
+
TEAM_HELP = "TODO `to:` must name a valid team (see config/teams). Match the human readable `name:` key (ex: 'Benefits Admin Transfers'), *not* a sluggified form."
|
|
27
|
+
|
|
28
|
+
# @param processed_source [RuboCop::ProcessedSource]
|
|
29
|
+
# @return [void]
|
|
30
|
+
def on_new_investigation
|
|
31
|
+
# Registers every existing SmartTodo offense. RuboCop dedupes offenses by
|
|
32
|
+
# source range (see Cop::Base#add_offense), so any comment flagged here
|
|
33
|
+
# silently swallows the duplicate team offense we might add below.
|
|
34
|
+
super
|
|
35
|
+
|
|
36
|
+
processed_source.comments.each do |comment|
|
|
37
|
+
next unless TODO_PATTERN.match?(comment.text)
|
|
38
|
+
|
|
39
|
+
# `is_a?(String)` guards CodeTeams.find's Sorbet signature, which raises on a
|
|
40
|
+
# non-string. Such assignees are already flagged by `super` (invalid assignee).
|
|
41
|
+
unknown = metadata(comment.text).assignees.select { |assignee| assignee.is_a?(String) && !CodeTeams.find(assignee) }
|
|
42
|
+
next if unknown.empty?
|
|
43
|
+
|
|
44
|
+
add_offense(comment, message: "Unknown team(s): #{unknown.join(', ')}. #{TEAM_HELP}")
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
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.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Gusto Engineering
|
|
@@ -9,6 +9,20 @@ bindir: exe
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: code_teams
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
12
26
|
- !ruby/object:Gem::Dependency
|
|
13
27
|
name: lint_roller
|
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -93,6 +107,20 @@ dependencies:
|
|
|
93
107
|
- - ">="
|
|
94
108
|
- !ruby/object:Gem::Version
|
|
95
109
|
version: '0'
|
|
110
|
+
- !ruby/object:Gem::Dependency
|
|
111
|
+
name: smart_todo
|
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - ">="
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '0'
|
|
117
|
+
type: :runtime
|
|
118
|
+
prerelease: false
|
|
119
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ">="
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '0'
|
|
96
124
|
- !ruby/object:Gem::Dependency
|
|
97
125
|
name: thor
|
|
98
126
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -138,14 +166,15 @@ files:
|
|
|
138
166
|
- lib/rubocop/cop/gusto/no_send.rb
|
|
139
167
|
- lib/rubocop/cop/gusto/paperclip_or_attachable.rb
|
|
140
168
|
- lib/rubocop/cop/gusto/perform_class_method.rb
|
|
169
|
+
- lib/rubocop/cop/gusto/pluck_on_select.rb
|
|
141
170
|
- lib/rubocop/cop/gusto/polymorphic_type_validation.rb
|
|
142
171
|
- lib/rubocop/cop/gusto/prefer_process_last_status.rb
|
|
143
172
|
- lib/rubocop/cop/gusto/rabl_extends.rb
|
|
144
|
-
- lib/rubocop/cop/gusto/rails_env.rb
|
|
145
173
|
- lib/rubocop/cop/gusto/rake_constants.rb
|
|
146
174
|
- lib/rubocop/cop/gusto/regexp_bypass.rb
|
|
147
175
|
- lib/rubocop/cop/gusto/rspec_date_time_mock.rb
|
|
148
176
|
- lib/rubocop/cop/gusto/sidekiq_params.rb
|
|
177
|
+
- lib/rubocop/cop/gusto/smart_todo_team.rb
|
|
149
178
|
- lib/rubocop/cop/gusto/toplevel_constants.rb
|
|
150
179
|
- lib/rubocop/cop/gusto/unreferenced_let.rb
|
|
151
180
|
- lib/rubocop/cop/gusto/use_paint_not_colorize.rb
|
|
@@ -1,77 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module RuboCop
|
|
4
|
-
module Cop
|
|
5
|
-
module Gusto
|
|
6
|
-
# NOTE: Being pushed upstream here: https://github.com/rubocop/rubocop-rails/pull/1375
|
|
7
|
-
# Checks for usage of `Rails.env` which can be replaced with Feature Flags
|
|
8
|
-
#
|
|
9
|
-
# Although `local?` is a form of an environment-specific check, it is allowed because
|
|
10
|
-
# it cannot be used to control overall environment rollout, but it can be helpful to
|
|
11
|
-
# distinguish or protect code that is explicitly written to only ever execute in a
|
|
12
|
-
# dev or test environment. `local?` is also a form of a feature flag.
|
|
13
|
-
#
|
|
14
|
-
# @example
|
|
15
|
-
#
|
|
16
|
-
# # bad
|
|
17
|
-
# Rails.env.production? || Rails.env.demo?
|
|
18
|
-
#
|
|
19
|
-
# # good
|
|
20
|
-
# if FeatureFlag.enabled?(:new_feature)
|
|
21
|
-
# # new feature code
|
|
22
|
-
# end
|
|
23
|
-
#
|
|
24
|
-
# # good
|
|
25
|
-
# raise unless Rails.env.local?
|
|
26
|
-
#
|
|
27
|
-
# # good
|
|
28
|
-
# abort ("The Rails environment is running in production mode!") unless Rails.env.local?
|
|
29
|
-
#
|
|
30
|
-
class RailsEnv < Base
|
|
31
|
-
# This allow list is derived from:
|
|
32
|
-
# (Rails.env.methods - Object.instance_methods).select { |m| m.to_s.end_with?('?') }
|
|
33
|
-
# and then removing the environment specific methods like development?, test?, production?
|
|
34
|
-
ALLOWED_LIST = Set.new(
|
|
35
|
-
%i(
|
|
36
|
-
unicode_normalized?
|
|
37
|
-
exclude?
|
|
38
|
-
empty?
|
|
39
|
-
acts_like_string?
|
|
40
|
-
include?
|
|
41
|
-
is_utf8?
|
|
42
|
-
casecmp?
|
|
43
|
-
match?
|
|
44
|
-
starts_with?
|
|
45
|
-
ends_with?
|
|
46
|
-
start_with?
|
|
47
|
-
end_with?
|
|
48
|
-
valid_encoding?
|
|
49
|
-
ascii_only?
|
|
50
|
-
between?
|
|
51
|
-
local?
|
|
52
|
-
)
|
|
53
|
-
).freeze
|
|
54
|
-
MSG = "Use Feature Flags or config instead of `Rails.env`."
|
|
55
|
-
RESTRICT_ON_SEND = %i(env).freeze
|
|
56
|
-
|
|
57
|
-
# @!method prohibited_rails_env?(node)
|
|
58
|
-
def_node_matcher :prohibited_rails_env?, <<~PATTERN
|
|
59
|
-
(send
|
|
60
|
-
(send (const _ :Rails) :env)
|
|
61
|
-
#prohibited_predicate?
|
|
62
|
-
)
|
|
63
|
-
PATTERN
|
|
64
|
-
|
|
65
|
-
def on_send(node)
|
|
66
|
-
add_offense(node.parent) if prohibited_rails_env?(node.parent)
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
private
|
|
70
|
-
|
|
71
|
-
def prohibited_predicate?(name)
|
|
72
|
-
name.to_s.end_with?("?") && !ALLOWED_LIST.include?(name)
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
end
|