rubocop-rails 2.16.1 → 2.17.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/config/default.yml +39 -0
- data/lib/rubocop/cop/rails/action_controller_flash_before_render.rb +7 -6
- data/lib/rubocop/cop/rails/action_controller_test_case.rb +1 -1
- data/lib/rubocop/cop/rails/action_order.rb +81 -0
- data/lib/rubocop/cop/rails/content_tag.rb +2 -1
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +6 -2
- data/lib/rubocop/cop/rails/eager_evaluation_log_message.rb +4 -0
- data/lib/rubocop/cop/rails/find_each.rb +1 -1
- data/lib/rubocop/cop/rails/freeze_time.rb +1 -1
- data/lib/rubocop/cop/rails/http_status.rb +6 -1
- data/lib/rubocop/cop/rails/ignored_columns_assignment.rb +50 -0
- data/lib/rubocop/cop/rails/pluck.rb +8 -7
- data/lib/rubocop/cop/rails/presence.rb +21 -10
- data/lib/rubocop/cop/rails/redundant_presence_validation_on_belongs_to.rb +1 -1
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +2 -0
- data/lib/rubocop/cop/rails/time_zone.rb +2 -2
- data/lib/rubocop/cop/rails/where_not_with_multiple_conditions.rb +55 -0
- data/lib/rubocop/cop/rails_cops.rb +3 -0
- data/lib/rubocop/rails/version.rb +1 -1
- data/lib/rubocop-rails.rb +10 -0
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a7a516c82d0953f3f1995b190ddc427aa251583287a9232229e82a218c93ba6
|
4
|
+
data.tar.gz: f224a35df6de69c04c7c6f6314f9cfa713cc5accfba1bbce9d5554c7fd50a121
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b3277765f3819855d3e29d3226c81ebc6231b1b1aaf583e1ae86db9e18b7b450bbc2b3f9bccaaa85623ca57e0ac139abde604c35bccc81c3081a80c4392f35d
|
7
|
+
data.tar.gz: a3d7b79f10c2090b04a05d1b30cc3c639645aa6b3c2ad33247fecfa7597907238d8f6e727a8e0dd088b92d69842c488489e9883f9acfd4551e08a9b8f3dd1f8c
|
data/config/default.yml
CHANGED
@@ -10,6 +10,9 @@ AllCops:
|
|
10
10
|
# Exclude db/schema.rb and db/[CONFIGURATION_NAMESPACE]_schema.rb by default.
|
11
11
|
# See: https://guides.rubyonrails.org/active_record_multiple_databases.html#setting-up-your-application
|
12
12
|
- db/*schema.rb
|
13
|
+
- log/**/*
|
14
|
+
- public/**/*
|
15
|
+
- storage/**/*
|
13
16
|
# Enable checking Active Support extensions.
|
14
17
|
# See: https://docs.rubocop.org/rubocop/configuration.html#enable-checking-active-support-extensions
|
15
18
|
ActiveSupportExtensionsEnabled: true
|
@@ -95,6 +98,21 @@ Rails/ActionFilter:
|
|
95
98
|
- app/controllers/**/*.rb
|
96
99
|
- app/mailers/**/*.rb
|
97
100
|
|
101
|
+
Rails/ActionOrder:
|
102
|
+
Description: 'Enforce consistent ordering of controller actions.'
|
103
|
+
Enabled: pending
|
104
|
+
VersionAdded: '2.17'
|
105
|
+
ExpectedOrder:
|
106
|
+
- index
|
107
|
+
- show
|
108
|
+
- new
|
109
|
+
- edit
|
110
|
+
- create
|
111
|
+
- update
|
112
|
+
- destroy
|
113
|
+
Include:
|
114
|
+
- app/controllers/**/*.rb
|
115
|
+
|
98
116
|
Rails/ActiveRecordAliases:
|
99
117
|
Description: >-
|
100
118
|
Avoid Active Record aliases:
|
@@ -348,10 +366,13 @@ Rails/DynamicFindBy:
|
|
348
366
|
# The `Whitelist` has been deprecated, Please use `AllowedMethods` instead.
|
349
367
|
Whitelist:
|
350
368
|
- find_by_sql
|
369
|
+
- find_by_token_for
|
351
370
|
AllowedMethods:
|
352
371
|
- find_by_sql
|
372
|
+
- find_by_token_for
|
353
373
|
AllowedReceivers:
|
354
374
|
- Gem::Specification
|
375
|
+
- page # Prevents a warning for `page.find_by_id`. See: https://github.com/rubocop/rubocop-rails/issues/778
|
355
376
|
|
356
377
|
Rails/EagerEvaluationLogMessage:
|
357
378
|
Description: 'Checks that blocks are used for interpolated strings passed to `Rails.logger.debug`.'
|
@@ -535,6 +556,13 @@ Rails/I18nLocaleTexts:
|
|
535
556
|
Enabled: pending
|
536
557
|
VersionAdded: '2.14'
|
537
558
|
|
559
|
+
Rails/IgnoredColumnsAssignment:
|
560
|
+
Description: 'Looks for assignments of `ignored_columns` that override previous assignments.'
|
561
|
+
StyleGuide: 'https://rails.rubystyle.guide/#append-ignored-columns'
|
562
|
+
Enabled: pending
|
563
|
+
SafeAutoCorrect: false
|
564
|
+
VersionAdded: '2.17'
|
565
|
+
|
538
566
|
Rails/IgnoredSkipActionFilterOption:
|
539
567
|
Description: 'Checks that `if` and `only` (or `except`) are not used together as options of `skip_*` action filter.'
|
540
568
|
Reference: 'https://api.rubyonrails.org/classes/AbstractController/Callbacks/ClassMethods.html#method-i-_normalize_callback_options'
|
@@ -1090,6 +1118,17 @@ Rails/WhereNot:
|
|
1090
1118
|
Enabled: 'pending'
|
1091
1119
|
VersionAdded: '2.8'
|
1092
1120
|
|
1121
|
+
Rails/WhereNotWithMultipleConditions:
|
1122
|
+
Description: 'Do not use `where.not(...)` with multiple conditions.'
|
1123
|
+
Enabled: 'pending'
|
1124
|
+
VersionAdded: '2.17'
|
1125
|
+
|
1093
1126
|
# Accept `redirect_to(...) and return` and similar cases.
|
1094
1127
|
Style/AndOr:
|
1095
1128
|
EnforcedStyle: conditionals
|
1129
|
+
|
1130
|
+
Style/SymbolProc:
|
1131
|
+
AllowedMethods:
|
1132
|
+
- define_method
|
1133
|
+
- mail
|
1134
|
+
- respond_to
|
@@ -37,8 +37,8 @@ module RuboCop
|
|
37
37
|
^(send (send nil? :flash) :[]= ...)
|
38
38
|
PATTERN
|
39
39
|
|
40
|
-
def_node_search :
|
41
|
-
(send nil? :
|
40
|
+
def_node_search :redirect_to?, <<~PATTERN
|
41
|
+
(send nil? :redirect_to ...)
|
42
42
|
PATTERN
|
43
43
|
|
44
44
|
def_node_search :action_controller?, <<~PATTERN
|
@@ -53,7 +53,7 @@ module RuboCop
|
|
53
53
|
def on_send(flash_node)
|
54
54
|
return unless flash_assignment?(flash_node)
|
55
55
|
|
56
|
-
return
|
56
|
+
return if followed_by_redirect_to?(flash_node)
|
57
57
|
|
58
58
|
return unless instance_method_or_block?(flash_node)
|
59
59
|
|
@@ -66,12 +66,13 @@ module RuboCop
|
|
66
66
|
|
67
67
|
private
|
68
68
|
|
69
|
-
def
|
69
|
+
def followed_by_redirect_to?(flash_node)
|
70
70
|
flash_assigment_node = find_ancestor(flash_node, type: :send)
|
71
71
|
context = flash_assigment_node.parent
|
72
72
|
|
73
|
-
context.
|
74
|
-
|
73
|
+
flash_index = context.children.index(flash_assigment_node)
|
74
|
+
context.each_child_node.with_index.any? do |node, index|
|
75
|
+
index > flash_index && redirect_to?(node)
|
75
76
|
end
|
76
77
|
end
|
77
78
|
|
@@ -31,7 +31,7 @@ module RuboCop
|
|
31
31
|
def_node_matcher :action_controller_test_case?, <<~PATTERN
|
32
32
|
(class
|
33
33
|
(const nil? _)
|
34
|
-
(const (const {nil? cbase} :ActionController) :TestCase)
|
34
|
+
(const (const {nil? cbase} :ActionController) :TestCase) _)
|
35
35
|
PATTERN
|
36
36
|
|
37
37
|
def on_class(node)
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Enforces consistent ordering of the standard Rails RESTful controller actions.
|
7
|
+
#
|
8
|
+
# The cop is configurable and can enforce any ordering of the standard actions.
|
9
|
+
# All other methods are ignored.
|
10
|
+
#
|
11
|
+
# [source,yaml]
|
12
|
+
# ----
|
13
|
+
# Rails/ActionOrder:
|
14
|
+
# ExpectedOrder:
|
15
|
+
# - index
|
16
|
+
# - show
|
17
|
+
# - new
|
18
|
+
# - edit
|
19
|
+
# - create
|
20
|
+
# - update
|
21
|
+
# - destroy
|
22
|
+
# ----
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# # bad
|
26
|
+
# def index; end
|
27
|
+
# def destroy; end
|
28
|
+
# def show; end
|
29
|
+
#
|
30
|
+
# # good
|
31
|
+
# def index; end
|
32
|
+
# def show; end
|
33
|
+
# def destroy; end
|
34
|
+
class ActionOrder < Base
|
35
|
+
extend AutoCorrector
|
36
|
+
include VisibilityHelp
|
37
|
+
include DefNode
|
38
|
+
|
39
|
+
MSG = 'Action `%<current>s` should appear before `%<previous>s`.'
|
40
|
+
|
41
|
+
def_node_search :action_declarations, '(def {%1} ...)'
|
42
|
+
|
43
|
+
def on_class(node)
|
44
|
+
action_declarations(node, actions).each_cons(2) do |previous, current|
|
45
|
+
next if node_visibility(current) != :public || non_public?(current)
|
46
|
+
next if find_index(current) >= find_index(previous)
|
47
|
+
|
48
|
+
register_offense(previous, current)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def expected_order
|
55
|
+
cop_config['ExpectedOrder'].map(&:to_sym)
|
56
|
+
end
|
57
|
+
|
58
|
+
def actions
|
59
|
+
@actions ||= Set.new(expected_order)
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_index(node)
|
63
|
+
expected_order.find_index(node.method_name)
|
64
|
+
end
|
65
|
+
|
66
|
+
def register_offense(previous, current)
|
67
|
+
message = format(
|
68
|
+
MSG,
|
69
|
+
expected_order: expected_order.join(', '),
|
70
|
+
previous: previous.method_name,
|
71
|
+
current: current.method_name
|
72
|
+
)
|
73
|
+
add_offense(current, message: message) do |corrector|
|
74
|
+
corrector.replace(current, previous.source)
|
75
|
+
corrector.replace(previous, current.source)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -22,19 +22,23 @@ module RuboCop
|
|
22
22
|
# User.find_by(name: name, email: email)
|
23
23
|
# User.find_by!(email: email)
|
24
24
|
#
|
25
|
-
# @example AllowedMethods: find_by_sql
|
25
|
+
# @example AllowedMethods: ['find_by_sql', 'find_by_token_for'] (default)
|
26
26
|
# # bad
|
27
27
|
# User.find_by_query(users_query)
|
28
|
+
# User.find_by_token_for(:password_reset, token)
|
28
29
|
#
|
29
30
|
# # good
|
30
31
|
# User.find_by_sql(users_sql)
|
32
|
+
# User.find_by_token_for(:password_reset, token)
|
31
33
|
#
|
32
|
-
# @example AllowedReceivers: Gem::Specification
|
34
|
+
# @example AllowedReceivers: ['Gem::Specification', 'page'] (default)
|
33
35
|
# # bad
|
34
36
|
# Specification.find_by_name('backend').gem_dir
|
37
|
+
# page.find_by_id('a_dom_id').click
|
35
38
|
#
|
36
39
|
# # good
|
37
40
|
# Gem::Specification.find_by_name('backend').gem_dir
|
41
|
+
# page.find_by_id('a_dom_id').click
|
38
42
|
class DynamicFindBy < Base
|
39
43
|
include ActiveRecordHelper
|
40
44
|
extend AutoCorrector
|
@@ -43,7 +43,7 @@ module RuboCop
|
|
43
43
|
PATTERN
|
44
44
|
|
45
45
|
def on_send(node)
|
46
|
-
child_node, method_name, time_argument = *node.first_argument
|
46
|
+
child_node, method_name, time_argument = *node.first_argument&.children
|
47
47
|
return if time_argument || !child_node
|
48
48
|
return unless current_time?(child_node, method_name) || current_time_with_convert?(child_node, method_name)
|
49
49
|
|
@@ -12,6 +12,7 @@ module RuboCop
|
|
12
12
|
# render plain: 'foo/bar', status: 304
|
13
13
|
# redirect_to root_url, status: 301
|
14
14
|
# head 200
|
15
|
+
# get '/foobar', to: redirect('/foobar/baz', status: 301)
|
15
16
|
#
|
16
17
|
# # good
|
17
18
|
# render :foo, status: :ok
|
@@ -19,6 +20,7 @@ module RuboCop
|
|
19
20
|
# render plain: 'foo/bar', status: :not_modified
|
20
21
|
# redirect_to root_url, status: :moved_permanently
|
21
22
|
# head :ok
|
23
|
+
# get '/foobar', to: redirect('/foobar/baz', status: :moved_permanently)
|
22
24
|
#
|
23
25
|
# @example EnforcedStyle: numeric
|
24
26
|
# # bad
|
@@ -27,6 +29,7 @@ module RuboCop
|
|
27
29
|
# render plain: 'foo/bar', status: :not_modified
|
28
30
|
# redirect_to root_url, status: :moved_permanently
|
29
31
|
# head :ok
|
32
|
+
# get '/foobar', to: redirect('/foobar/baz', status: :moved_permanently)
|
30
33
|
#
|
31
34
|
# # good
|
32
35
|
# render :foo, status: 200
|
@@ -34,18 +37,20 @@ module RuboCop
|
|
34
37
|
# render plain: 'foo/bar', status: 304
|
35
38
|
# redirect_to root_url, status: 301
|
36
39
|
# head 200
|
40
|
+
# get '/foobar', to: redirect('/foobar/baz', status: 301)
|
37
41
|
#
|
38
42
|
class HttpStatus < Base
|
39
43
|
include ConfigurableEnforcedStyle
|
40
44
|
extend AutoCorrector
|
41
45
|
|
42
|
-
RESTRICT_ON_SEND = %i[render redirect_to head].freeze
|
46
|
+
RESTRICT_ON_SEND = %i[render redirect_to head redirect].freeze
|
43
47
|
|
44
48
|
def_node_matcher :http_status, <<~PATTERN
|
45
49
|
{
|
46
50
|
(send nil? {:render :redirect_to} _ $hash)
|
47
51
|
(send nil? {:render :redirect_to} $hash)
|
48
52
|
(send nil? :head ${int sym} ...)
|
53
|
+
(send nil? :redirect _ $hash)
|
49
54
|
}
|
50
55
|
PATTERN
|
51
56
|
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Looks for assignments of `ignored_columns` that may override previous
|
7
|
+
# assignments.
|
8
|
+
#
|
9
|
+
# Overwriting previous assignments is usually a mistake, since it will
|
10
|
+
# un-ignore the first set of columns. Since duplicate column names is not
|
11
|
+
# a problem, it is better to simply append to the list.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# class User < ActiveRecord::Base
|
17
|
+
# self.ignored_columns = [:one]
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # bad
|
21
|
+
# class User < ActiveRecord::Base
|
22
|
+
# self.ignored_columns = [:one, :two]
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# # good
|
26
|
+
# class User < ActiveRecord::Base
|
27
|
+
# self.ignored_columns += [:one, :two]
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# # good
|
31
|
+
# class User < ActiveRecord::Base
|
32
|
+
# self.ignored_columns += [:one]
|
33
|
+
# self.ignored_columns += [:two]
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
class IgnoredColumnsAssignment < Base
|
37
|
+
extend AutoCorrector
|
38
|
+
|
39
|
+
MSG = 'Use `+=` instead of `=`.'
|
40
|
+
RESTRICT_ON_SEND = %i[ignored_columns=].freeze
|
41
|
+
|
42
|
+
def on_send(node)
|
43
|
+
add_offense(node.loc.operator) do |corrector|
|
44
|
+
corrector.replace(node.loc.operator, '+=')
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -21,16 +21,16 @@ module RuboCop
|
|
21
21
|
extend AutoCorrector
|
22
22
|
extend TargetRailsVersion
|
23
23
|
|
24
|
-
MSG = 'Prefer
|
24
|
+
MSG = 'Prefer `%<replacement>s` over `%<current>s`.'
|
25
25
|
|
26
26
|
minimum_target_rails_version 5.0
|
27
27
|
|
28
28
|
def_node_matcher :pluck_candidate?, <<~PATTERN
|
29
|
-
({block numblock} (send _ {:map :collect}) $_argument (send (lvar $_element) :[]
|
29
|
+
({block numblock} (send _ {:map :collect}) $_argument (send (lvar $_element) :[] $_key))
|
30
30
|
PATTERN
|
31
31
|
|
32
32
|
def on_block(node)
|
33
|
-
pluck_candidate?(node) do |argument, element,
|
33
|
+
pluck_candidate?(node) do |argument, element, key|
|
34
34
|
match = if node.block_type?
|
35
35
|
argument.children.first.source.to_sym == element
|
36
36
|
else # numblock
|
@@ -38,10 +38,11 @@ module RuboCop
|
|
38
38
|
end
|
39
39
|
next unless match
|
40
40
|
|
41
|
-
|
41
|
+
replacement = "pluck(#{key.source})"
|
42
|
+
message = message(replacement, node)
|
42
43
|
|
43
44
|
add_offense(offense_range(node), message: message) do |corrector|
|
44
|
-
corrector.replace(offense_range(node),
|
45
|
+
corrector.replace(offense_range(node), replacement)
|
45
46
|
end
|
46
47
|
end
|
47
48
|
end
|
@@ -53,10 +54,10 @@ module RuboCop
|
|
53
54
|
node.send_node.loc.selector.join(node.loc.end)
|
54
55
|
end
|
55
56
|
|
56
|
-
def message(
|
57
|
+
def message(replacement, node)
|
57
58
|
current = offense_range(node).source
|
58
59
|
|
59
|
-
format(MSG,
|
60
|
+
format(MSG, replacement: replacement, current: current)
|
60
61
|
end
|
61
62
|
end
|
62
63
|
end
|
@@ -93,7 +93,7 @@ module RuboCop
|
|
93
93
|
|
94
94
|
def register_offense(node, receiver, other)
|
95
95
|
add_offense(node, message: message(node, receiver, other)) do |corrector|
|
96
|
-
corrector.replace(node.source_range, replacement(receiver, other))
|
96
|
+
corrector.replace(node.source_range, replacement(receiver, other, node.left_sibling))
|
97
97
|
end
|
98
98
|
end
|
99
99
|
|
@@ -106,10 +106,20 @@ module RuboCop
|
|
106
106
|
end
|
107
107
|
|
108
108
|
def message(node, receiver, other)
|
109
|
-
|
109
|
+
prefer = replacement(receiver, other, node.left_sibling).gsub(/^\s*|\n/, '')
|
110
|
+
current = current(node).gsub(/^\s*|\n/, '')
|
111
|
+
format(MSG, prefer: prefer, current: current)
|
110
112
|
end
|
111
113
|
|
112
|
-
def
|
114
|
+
def current(node)
|
115
|
+
if node.source.include?("\n")
|
116
|
+
"#{node.loc.keyword.with(end_pos: node.condition.loc.selector.end_pos).source} ... end"
|
117
|
+
else
|
118
|
+
node.source
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def replacement(receiver, other, left_sibling)
|
113
123
|
or_source = if other&.send_type?
|
114
124
|
build_source_for_or_method(other)
|
115
125
|
elsif other.nil? || other.nil_type?
|
@@ -118,23 +128,24 @@ module RuboCop
|
|
118
128
|
" || #{other.source}"
|
119
129
|
end
|
120
130
|
|
121
|
-
"#{receiver.source}.presence"
|
131
|
+
replaced = "#{receiver.source}.presence#{or_source}"
|
132
|
+
left_sibling ? "(#{replaced})" : replaced
|
122
133
|
end
|
123
134
|
|
124
135
|
def build_source_for_or_method(other)
|
125
|
-
if other.parenthesized? || other.method?('[]') || !other.arguments?
|
136
|
+
if other.parenthesized? || other.method?('[]') || other.arithmetic_operation? || !other.arguments?
|
126
137
|
" || #{other.source}"
|
127
138
|
else
|
128
|
-
method =
|
129
|
-
other.source_range.begin_pos,
|
130
|
-
other.first_argument.source_range.begin_pos - 1
|
131
|
-
).source
|
132
|
-
|
139
|
+
method = method_range(other).source
|
133
140
|
arguments = other.arguments.map(&:source).join(', ')
|
134
141
|
|
135
142
|
" || #{method}(#{arguments})"
|
136
143
|
end
|
137
144
|
end
|
145
|
+
|
146
|
+
def method_range(node)
|
147
|
+
range_between(node.source_range.begin_pos, node.first_argument.source_range.begin_pos - 1)
|
148
|
+
end
|
138
149
|
end
|
139
150
|
end
|
140
151
|
end
|
@@ -143,7 +143,7 @@ module RuboCop
|
|
143
143
|
def_node_matcher :belongs_to_without_fk?, <<~PATTERN
|
144
144
|
{
|
145
145
|
(send nil? :belongs_to (sym %1)) # belongs_to :user
|
146
|
-
(send nil? :belongs_to (sym %1) !hash) # belongs_to :user, -> { not_deleted }
|
146
|
+
(send nil? :belongs_to (sym %1) !hash ...) # belongs_to :user, -> { not_deleted }
|
147
147
|
(send nil? :belongs_to (sym %1) !(hash <(pair (sym :foreign_key) _) ...>))
|
148
148
|
}
|
149
149
|
PATTERN
|
@@ -55,7 +55,7 @@ module RuboCop
|
|
55
55
|
|
56
56
|
ACCEPTED_METHODS = %i[in_time_zone utc getlocal xmlschema iso8601 jisx0301 rfc3339 httpdate to_i to_f].freeze
|
57
57
|
|
58
|
-
TIMEZONE_SPECIFIER = /[A-z]/.freeze
|
58
|
+
TIMEZONE_SPECIFIER = /([A-z]|[+-]\d{2}:?\d{2})\z/.freeze
|
59
59
|
|
60
60
|
def on_const(node)
|
61
61
|
mod, klass = *node
|
@@ -126,7 +126,7 @@ module RuboCop
|
|
126
126
|
end
|
127
127
|
|
128
128
|
def attach_timezone_specifier?(date)
|
129
|
-
date.respond_to?(:value) && TIMEZONE_SPECIFIER.match?(date.value.to_s
|
129
|
+
date.respond_to?(:value) && TIMEZONE_SPECIFIER.match?(date.value.to_s)
|
130
130
|
end
|
131
131
|
|
132
132
|
def build_message(klass, method_name, node)
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# Identifies calls to `where.not` with multiple hash arguments.
|
7
|
+
#
|
8
|
+
# The behavior of `where.not` changed in Rails 6.1. Prior to the change,
|
9
|
+
# `.where.not(trashed: true, role: 'admin')` evaluated to
|
10
|
+
# `WHERE trashed != TRUE AND role != 'admin'`.
|
11
|
+
# From Rails 6.1 onwards, this executes the query
|
12
|
+
# `WHERE NOT (trashed == TRUE AND roles == 'admin')`.
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# # bad
|
16
|
+
# User.where.not(trashed: true, role: 'admin')
|
17
|
+
# User.where.not(trashed: true, role: ['moderator', 'admin'])
|
18
|
+
# User.joins(:posts).where.not(posts: { trashed: true, title: 'Rails' })
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# User.where.not(trashed: true)
|
22
|
+
# User.where.not(role: ['moderator', 'admin'])
|
23
|
+
# User.where.not(trashed: true).where.not(role: ['moderator', 'admin'])
|
24
|
+
# User.where.not('trashed = ? OR role = ?', true, 'admin')
|
25
|
+
class WhereNotWithMultipleConditions < Base
|
26
|
+
MSG = 'Use a SQL statement instead of `where.not` with multiple conditions.'
|
27
|
+
RESTRICT_ON_SEND = %i[not].freeze
|
28
|
+
|
29
|
+
def_node_matcher :where_not_call?, <<~PATTERN
|
30
|
+
(send (send _ :where) :not $...)
|
31
|
+
PATTERN
|
32
|
+
|
33
|
+
def on_send(node)
|
34
|
+
where_not_call?(node) do |args|
|
35
|
+
next unless args[0].hash_type?
|
36
|
+
next unless multiple_arguments_hash? args[0]
|
37
|
+
|
38
|
+
range = node.receiver.loc.selector.with(end_pos: node.loc.expression.end_pos)
|
39
|
+
|
40
|
+
add_offense(range)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def multiple_arguments_hash?(hash)
|
47
|
+
return true if hash.pairs.size >= 2
|
48
|
+
return false unless hash.values[0].hash_type?
|
49
|
+
|
50
|
+
multiple_arguments_hash?(hash.values[0])
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -11,6 +11,7 @@ require_relative 'mixin/target_rails_version'
|
|
11
11
|
require_relative 'rails/action_controller_flash_before_render'
|
12
12
|
require_relative 'rails/action_controller_test_case'
|
13
13
|
require_relative 'rails/action_filter'
|
14
|
+
require_relative 'rails/action_order'
|
14
15
|
require_relative 'rails/active_record_aliases'
|
15
16
|
require_relative 'rails/active_record_callbacks_order'
|
16
17
|
require_relative 'rails/active_record_override'
|
@@ -61,6 +62,7 @@ require_relative 'rails/http_status'
|
|
61
62
|
require_relative 'rails/i18n_lazy_lookup'
|
62
63
|
require_relative 'rails/i18n_locale_assignment'
|
63
64
|
require_relative 'rails/i18n_locale_texts'
|
65
|
+
require_relative 'rails/ignored_columns_assignment'
|
64
66
|
require_relative 'rails/ignored_skip_action_filter_option'
|
65
67
|
require_relative 'rails/index_by'
|
66
68
|
require_relative 'rails/index_with'
|
@@ -127,3 +129,4 @@ require_relative 'rails/where_equals'
|
|
127
129
|
require_relative 'rails/where_exists'
|
128
130
|
require_relative 'rails/where_missing'
|
129
131
|
require_relative 'rails/where_not'
|
132
|
+
require_relative 'rails/where_not_with_multiple_conditions'
|
data/lib/rubocop-rails.rb
CHANGED
@@ -14,6 +14,16 @@ RuboCop::Rails::Inject.defaults!
|
|
14
14
|
|
15
15
|
require_relative 'rubocop/cop/rails_cops'
|
16
16
|
|
17
|
+
RuboCop::Cop::Style::HashExcept.minimum_target_ruby_version(2.0)
|
18
|
+
|
19
|
+
RuboCop::Cop::Style::MethodCallWithArgsParentheses.singleton_class.prepend(
|
20
|
+
Module.new do
|
21
|
+
def autocorrect_incompatible_with
|
22
|
+
super.push(RuboCop::Cop::Rails::EagerEvaluationLogMessage)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
)
|
26
|
+
|
17
27
|
RuboCop::Cop::Style::RedundantSelf.singleton_class.prepend(
|
18
28
|
Module.new do
|
19
29
|
def autocorrect_incompatible_with
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rubocop-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.17.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bozhidar Batsov
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2022-
|
13
|
+
date: 2022-10-22 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activesupport
|
@@ -85,6 +85,7 @@ files:
|
|
85
85
|
- lib/rubocop/cop/rails/action_controller_flash_before_render.rb
|
86
86
|
- lib/rubocop/cop/rails/action_controller_test_case.rb
|
87
87
|
- lib/rubocop/cop/rails/action_filter.rb
|
88
|
+
- lib/rubocop/cop/rails/action_order.rb
|
88
89
|
- lib/rubocop/cop/rails/active_record_aliases.rb
|
89
90
|
- lib/rubocop/cop/rails/active_record_callbacks_order.rb
|
90
91
|
- lib/rubocop/cop/rails/active_record_override.rb
|
@@ -135,6 +136,7 @@ files:
|
|
135
136
|
- lib/rubocop/cop/rails/i18n_lazy_lookup.rb
|
136
137
|
- lib/rubocop/cop/rails/i18n_locale_assignment.rb
|
137
138
|
- lib/rubocop/cop/rails/i18n_locale_texts.rb
|
139
|
+
- lib/rubocop/cop/rails/ignored_columns_assignment.rb
|
138
140
|
- lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb
|
139
141
|
- lib/rubocop/cop/rails/index_by.rb
|
140
142
|
- lib/rubocop/cop/rails/index_with.rb
|
@@ -201,6 +203,7 @@ files:
|
|
201
203
|
- lib/rubocop/cop/rails/where_exists.rb
|
202
204
|
- lib/rubocop/cop/rails/where_missing.rb
|
203
205
|
- lib/rubocop/cop/rails/where_not.rb
|
206
|
+
- lib/rubocop/cop/rails/where_not_with_multiple_conditions.rb
|
204
207
|
- lib/rubocop/cop/rails_cops.rb
|
205
208
|
- lib/rubocop/rails.rb
|
206
209
|
- lib/rubocop/rails/inject.rb
|
@@ -214,7 +217,7 @@ metadata:
|
|
214
217
|
homepage_uri: https://docs.rubocop.org/rubocop-rails/
|
215
218
|
changelog_uri: https://github.com/rubocop/rubocop-rails/blob/master/CHANGELOG.md
|
216
219
|
source_code_uri: https://github.com/rubocop/rubocop-rails/
|
217
|
-
documentation_uri: https://docs.rubocop.org/rubocop-rails/2.
|
220
|
+
documentation_uri: https://docs.rubocop.org/rubocop-rails/2.17/
|
218
221
|
bug_tracker_uri: https://github.com/rubocop/rubocop-rails/issues
|
219
222
|
rubygems_mfa_required: 'true'
|
220
223
|
post_install_message:
|