rubocop-rails 2.16.1 → 2.17.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/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:
|