Skip to content

Commit 822c438

Browse files
committed
Add cop for checking duplicate assingments to ignored_columns
Overwriting previous assignments is usually a mistake, since it will un-ignore the first set of columns
1 parent 7485ea7 commit 822c438

File tree

7 files changed

+167
-0
lines changed

7 files changed

+167
-0
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@
22

33
## master (unreleased)
44

5+
### New features
6+
7+
* [#514](https://github.com/rubocop/rubocop-rails/pull/514): Add new `Rails/DuplicateIgnoredColumns` cop. ([@fsateler][])
8+
59
## 2.11.1 (2021-06-25)
610

711
### Bug fixes
@@ -417,3 +421,4 @@
417421
[@nvasilevski]: https://github.com/nvasilevski
418422
[@skryukov]: https://github.com/skryukov
419423
[@johnsyweb]: https://github.com/johnsyweb
424+
[@fsateler]: https://github.com/fsateler

config/default.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -236,6 +236,11 @@ Rails/DelegateAllowBlank:
236236
Enabled: true
237237
VersionAdded: '0.44'
238238

239+
Rails/DuplicateIgnoredColumns:
240+
Description: 'Looks for assignments of `ignored_columns` that override previous assignments.'
241+
Enabled: pending
242+
VersionAdded: '2.12'
243+
239244
Rails/DynamicFindBy:
240245
Description: 'Use `find_by` instead of dynamic `find_by_*`.'
241246
StyleGuide: 'https://rails.rubystyle.guide#find_by'

docs/modules/ROOT/pages/cops.adoc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ based on the https://rails.rubystyle.guide/[Rails Style Guide].
3939
* xref:cops_rails.adoc#railsdefaultscope[Rails/DefaultScope]
4040
* xref:cops_rails.adoc#railsdelegate[Rails/Delegate]
4141
* xref:cops_rails.adoc#railsdelegateallowblank[Rails/DelegateAllowBlank]
42+
* xref:cops_rails.adoc#railsduplicateignoredcolumns[Rails/DuplicateIgnoredColumns]
4243
* xref:cops_rails.adoc#railsdynamicfindby[Rails/DynamicFindBy]
4344
* xref:cops_rails.adoc#railseagerevaluationlogmessage[Rails/EagerEvaluationLogMessage]
4445
* xref:cops_rails.adoc#railsenumhash[Rails/EnumHash]

docs/modules/ROOT/pages/cops_rails.adoc

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1156,6 +1156,52 @@ delegate :foo, to: :bar, allow_blank: true
11561156
delegate :foo, to: :bar, allow_nil: true
11571157
----
11581158

1159+
== Rails/DuplicateIgnoredColumns
1160+
1161+
|===
1162+
| Enabled by default | Safe | Supports autocorrection | VersionAdded | VersionChanged
1163+
1164+
| Pending
1165+
| Yes
1166+
| No
1167+
| 2.12
1168+
| -
1169+
|===
1170+
1171+
This cops looks for assignments of `ignored_columns` that override previous
1172+
assignments.
1173+
1174+
Overwriting previous assignments is usually a mistake, since it will
1175+
un-ignore the first set of columns
1176+
1177+
=== Examples
1178+
1179+
[source,ruby]
1180+
----
1181+
# bad
1182+
class User < ActiveRecord::Base
1183+
self.ignored_columns = [:one]
1184+
self.ignored_columns = [:two]
1185+
end
1186+
1187+
# bad
1188+
class User < ActiveRecord::Base
1189+
self.ignored_columns += [:one]
1190+
self.ignored_columns = [:two]
1191+
end
1192+
1193+
# good
1194+
class User < ActiveRecord::Base
1195+
self.ignored_columns = [:one, :two]
1196+
end
1197+
1198+
# good
1199+
class User < ActiveRecord::Base
1200+
self.ignored_columns = [:one]
1201+
self.ignored_columns += [:two]
1202+
end
1203+
----
1204+
11591205
== Rails/DynamicFindBy
11601206

11611207
|===
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module Rails
6+
# This cops looks for assignments of `ignored_columns` that override previous
7+
# assignments.
8+
#
9+
# Overwriting previous assignments is usually a mistake, since it will
10+
# un-ignore the first set of columns
11+
#
12+
# @example
13+
#
14+
# # bad
15+
# class User < ActiveRecord::Base
16+
# self.ignored_columns = [:one]
17+
# self.ignored_columns = [:two]
18+
# end
19+
#
20+
# # bad
21+
# class User < ActiveRecord::Base
22+
# self.ignored_columns += [:one]
23+
# self.ignored_columns = [:two]
24+
# end
25+
#
26+
# # good
27+
# class User < ActiveRecord::Base
28+
# self.ignored_columns = [:one, :two]
29+
# end
30+
#
31+
# # good
32+
# class User < ActiveRecord::Base
33+
# self.ignored_columns = [:one]
34+
# self.ignored_columns += [:two]
35+
# end
36+
#
37+
class DuplicateIgnoredColumns < Base
38+
MSG = 'This assignment to `ignored_columns` overwrites previous ones.'
39+
40+
def_node_matcher :ignored_columns_assign, <<-PATTERN
41+
(send self :ignored_columns= ...)
42+
PATTERN
43+
def_node_matcher :ignored_columns_append, <<-PATTERN
44+
(op-asgn
45+
(send self :ignored_columns)
46+
:+ ...
47+
)
48+
PATTERN
49+
50+
def on_op_asgn(node)
51+
ignored_columns_append(node) do
52+
@seen_before = true
53+
end
54+
end
55+
56+
def on_send(node)
57+
ignored_columns_assign(node) do
58+
add_offense(node.loc.selector) if @seen_before
59+
@seen_before = true
60+
end
61+
end
62+
63+
def on_new_investigation
64+
@seen_before = false
65+
super
66+
end
67+
end
68+
end
69+
end
70+
end

lib/rubocop/cop/rails_cops.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
require_relative 'rails/default_scope'
2929
require_relative 'rails/delegate'
3030
require_relative 'rails/delegate_allow_blank'
31+
require_relative 'rails/duplicate_ignored_columns'
3132
require_relative 'rails/dynamic_find_by'
3233
require_relative 'rails/eager_evaluation_log_message'
3334
require_relative 'rails/enum_hash'
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RuboCop::Cop::Rails::DuplicateIgnoredColumns, :config do
4+
it 'registers an offense when setting `ignored_columns` twice' do
5+
expect_offense(<<~RUBY)
6+
class User < ActiveRecord::Base
7+
self.ignored_columns = [:one]
8+
self.ignored_columns = [:two]
9+
^^^^^^^^^^^^^^^ This assignment to `ignored_columns` overwrites previous ones.
10+
end
11+
RUBY
12+
end
13+
14+
it 'registers an offense when setting `ignored_columns` after appending' do
15+
expect_offense(<<~RUBY)
16+
class User < ActiveRecord::Base
17+
self.ignored_columns += [:one]
18+
self.ignored_columns = [:two]
19+
^^^^^^^^^^^^^^^ This assignment to `ignored_columns` overwrites previous ones.
20+
end
21+
RUBY
22+
end
23+
24+
it 'does not register an offense when using `ignored_columns` once' do
25+
expect_no_offenses(<<~RUBY)
26+
class User < ActiveRecord::Base
27+
self.ignored_columns = [:one]
28+
end
29+
RUBY
30+
end
31+
it 'does not register an offense when setting `ignored_columns` and then appending' do
32+
expect_no_offenses(<<~RUBY)
33+
class User < ActiveRecord::Base
34+
self.ignored_columns = [:one]
35+
self.ignored_columns += [:two]
36+
end
37+
RUBY
38+
end
39+
end

0 commit comments

Comments
 (0)