rubocop-rails 2.34.3 → 2.35.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 +12 -12
- data/lib/rubocop/cop/mixin/database_type_resolvable.rb +3 -3
- data/lib/rubocop/cop/mixin/target_rails_version.rb +7 -17
- data/lib/rubocop/cop/rails/action_filter.rb +1 -1
- data/lib/rubocop/cop/rails/i18n_locale_texts.rb +24 -2
- data/lib/rubocop/cop/rails/not_null_column.rb +2 -0
- data/lib/rubocop/cop/rails/presence.rb +19 -12
- data/lib/rubocop/cop/rails/rake_environment.rb +1 -1
- data/lib/rubocop/cop/rails/response_parsed_body.rb +59 -50
- data/lib/rubocop/cop/rails/select_map.rb +15 -1
- data/lib/rubocop/cop/rails/strong_parameters_expect.rb +71 -1
- data/lib/rubocop/cop/rails/unknown_env.rb +39 -12
- data/lib/rubocop/rails/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: e4946e79ccf9661248df014d69703665df3b0a72aa800e8c456afc59594ad944
|
|
4
|
+
data.tar.gz: 7575ea946eb4e6985d6c0c365a0f39345fc6d32b0634c74949526f987d1ac38d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7a660eb16537b1dafe092cb100229fd9f039f8d98b51e1408b9438997176c8fc0fde61494ae462bfa8ea15e65524f5ec99e4d2efac42d0461395da65bfe8cb8b
|
|
7
|
+
data.tar.gz: ad601bb6b3296dceb196159604975b5e2efecaca5c66f715523368344f8a77e62d93cf8e22c4b3d53ce67d849a34cf9a1f4d184d6b655ecea0f12da23490bb35
|
data/config/default.yml
CHANGED
|
@@ -601,8 +601,8 @@ Rails/HttpPositionalArguments:
|
|
|
601
601
|
Enabled: true
|
|
602
602
|
VersionAdded: '0.44'
|
|
603
603
|
Include:
|
|
604
|
-
- 'spec/**/*'
|
|
605
|
-
- 'test/**/*'
|
|
604
|
+
- '**/spec/**/*'
|
|
605
|
+
- '**/test/**/*'
|
|
606
606
|
|
|
607
607
|
Rails/HttpStatus:
|
|
608
608
|
Description: 'Enforces use of symbolic or numeric value to define HTTP status.'
|
|
@@ -640,8 +640,8 @@ Rails/I18nLocaleAssignment:
|
|
|
640
640
|
Enabled: 'pending'
|
|
641
641
|
VersionAdded: '2.11'
|
|
642
642
|
Include:
|
|
643
|
-
- spec/**/*.rb
|
|
644
|
-
- test/**/*.rb
|
|
643
|
+
- '**/spec/**/*.rb'
|
|
644
|
+
- '**/test/**/*.rb'
|
|
645
645
|
|
|
646
646
|
Rails/I18nLocaleTexts:
|
|
647
647
|
Description: 'Enforces use of I18n and locale files instead of locale specific strings.'
|
|
@@ -929,8 +929,8 @@ Rails/RedundantTravelBack:
|
|
|
929
929
|
Enabled: pending
|
|
930
930
|
VersionAdded: '2.12'
|
|
931
931
|
Include:
|
|
932
|
-
- spec/**/*.rb
|
|
933
|
-
- test/**/*.rb
|
|
932
|
+
- '**/spec/**/*.rb'
|
|
933
|
+
- '**/test/**/*.rb'
|
|
934
934
|
|
|
935
935
|
Rails/ReflectionClassName:
|
|
936
936
|
Description: 'Use a string for `class_name` option value in the definition of a reflection.'
|
|
@@ -993,10 +993,10 @@ Rails/ResponseParsedBody:
|
|
|
993
993
|
VersionAdded: '2.18'
|
|
994
994
|
VersionChanged: '2.19'
|
|
995
995
|
Include:
|
|
996
|
-
- spec/controllers/**/*.rb
|
|
997
|
-
- spec/requests/**/*.rb
|
|
998
|
-
- test/controllers/**/*.rb
|
|
999
|
-
- test/integration/**/*.rb
|
|
996
|
+
- '**/spec/controllers/**/*.rb'
|
|
997
|
+
- '**/spec/requests/**/*.rb'
|
|
998
|
+
- '**/test/controllers/**/*.rb'
|
|
999
|
+
- '**/test/integration/**/*.rb'
|
|
1000
1000
|
|
|
1001
1001
|
Rails/ReversibleMigration:
|
|
1002
1002
|
Description: 'Checks whether the change method of the migration file is reversible.'
|
|
@@ -1190,8 +1190,8 @@ Rails/TimeZoneAssignment:
|
|
|
1190
1190
|
Enabled: 'pending'
|
|
1191
1191
|
VersionAdded: '2.10'
|
|
1192
1192
|
Include:
|
|
1193
|
-
- spec/**/*.rb
|
|
1194
|
-
- test/**/*.rb
|
|
1193
|
+
- '**/spec/**/*.rb'
|
|
1194
|
+
- '**/test/**/*.rb'
|
|
1195
1195
|
|
|
1196
1196
|
Rails/ToFormattedS:
|
|
1197
1197
|
Description: 'Checks for consistent uses of `to_fs` or `to_formatted_s`.'
|
|
@@ -40,7 +40,7 @@ module RuboCop
|
|
|
40
40
|
end
|
|
41
41
|
end
|
|
42
42
|
|
|
43
|
-
def database_yaml
|
|
43
|
+
def database_yaml(environment = 'development')
|
|
44
44
|
return unless File.exist?('config/database.yml')
|
|
45
45
|
|
|
46
46
|
yaml = if YAML.respond_to?(:unsafe_load_file)
|
|
@@ -50,7 +50,7 @@ module RuboCop
|
|
|
50
50
|
end
|
|
51
51
|
return unless yaml.is_a? Hash
|
|
52
52
|
|
|
53
|
-
config = yaml[
|
|
53
|
+
config = yaml[environment]
|
|
54
54
|
return unless config.is_a?(Hash)
|
|
55
55
|
|
|
56
56
|
config
|
|
@@ -59,7 +59,7 @@ module RuboCop
|
|
|
59
59
|
end
|
|
60
60
|
|
|
61
61
|
def database_adapter
|
|
62
|
-
database_yaml['adapter'] || database_yaml.first.last['adapter']
|
|
62
|
+
database_yaml['adapter'] || database_yaml('shared')&.dig('adapter') || database_yaml.first.last['adapter']
|
|
63
63
|
end
|
|
64
64
|
end
|
|
65
65
|
end
|
|
@@ -12,29 +12,19 @@ module RuboCop
|
|
|
12
12
|
TARGET_GEM_NAME = 'railties' # :nodoc:
|
|
13
13
|
|
|
14
14
|
def minimum_target_rails_version(version)
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
when String then requires_gem(TARGET_GEM_NAME, version)
|
|
19
|
-
end
|
|
20
|
-
else
|
|
21
|
-
# Fallback path for previous versions of RuboCop which don't support the `requires_gem` API yet.
|
|
22
|
-
@minimum_target_rails_version = version
|
|
15
|
+
case version
|
|
16
|
+
when Integer, Float then requires_gem(TARGET_GEM_NAME, ">= #{version}")
|
|
17
|
+
when String then requires_gem(TARGET_GEM_NAME, version)
|
|
23
18
|
end
|
|
24
19
|
end
|
|
25
20
|
|
|
26
21
|
def support_target_rails_version?(version)
|
|
27
|
-
|
|
28
|
-
return false unless gem_requirements
|
|
22
|
+
return false unless gem_requirements
|
|
29
23
|
|
|
30
|
-
|
|
31
|
-
|
|
24
|
+
gem_requirement = gem_requirements[TARGET_GEM_NAME]
|
|
25
|
+
return true unless gem_requirement # If we have no requirement, then we support all versions
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
else
|
|
35
|
-
# Fallback path for previous versions of RuboCop which don't support the `requires_gem` API yet.
|
|
36
|
-
@minimum_target_rails_version <= version
|
|
37
|
-
end
|
|
27
|
+
gem_requirement.satisfied_by?(Gem::Version.new(version))
|
|
38
28
|
end
|
|
39
29
|
end
|
|
40
30
|
end
|
|
@@ -72,7 +72,7 @@ module RuboCop
|
|
|
72
72
|
|
|
73
73
|
RESTRICT_ON_SEND = FILTER_METHODS + ACTION_METHODS
|
|
74
74
|
|
|
75
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
75
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
76
76
|
check_method_node(node.send_node)
|
|
77
77
|
end
|
|
78
78
|
|
|
@@ -47,6 +47,28 @@ module RuboCop
|
|
|
47
47
|
# end
|
|
48
48
|
#
|
|
49
49
|
# # bad
|
|
50
|
+
# class PostsController < ApplicationController
|
|
51
|
+
# def update
|
|
52
|
+
# # ...
|
|
53
|
+
# redirect_back_or_to root_path, alert: "Failed to update!"
|
|
54
|
+
# end
|
|
55
|
+
# end
|
|
56
|
+
#
|
|
57
|
+
# # good
|
|
58
|
+
# # config/locales/en.yml
|
|
59
|
+
# # en:
|
|
60
|
+
# # posts:
|
|
61
|
+
# # update:
|
|
62
|
+
# # failure: "Failed to update!"
|
|
63
|
+
#
|
|
64
|
+
# class PostsController < ApplicationController
|
|
65
|
+
# def update
|
|
66
|
+
# # ...
|
|
67
|
+
# redirect_back_or_to root_path, alert: t(".failure")
|
|
68
|
+
# end
|
|
69
|
+
# end
|
|
70
|
+
#
|
|
71
|
+
# # bad
|
|
50
72
|
# class UserMailer < ApplicationMailer
|
|
51
73
|
# def welcome(user)
|
|
52
74
|
# mail(to: user.email, subject: "Welcome to My Awesome Site")
|
|
@@ -69,7 +91,7 @@ module RuboCop
|
|
|
69
91
|
class I18nLocaleTexts < Base
|
|
70
92
|
MSG = 'Move locale texts to the locale files in the `config/locales` directory.'
|
|
71
93
|
|
|
72
|
-
RESTRICT_ON_SEND = %i[validates redirect_to redirect_back []= mail].freeze
|
|
94
|
+
RESTRICT_ON_SEND = %i[validates redirect_to redirect_back redirect_back_or_to []= mail].freeze
|
|
73
95
|
|
|
74
96
|
def_node_search :validation_message, <<~PATTERN
|
|
75
97
|
(pair (sym :message) $str)
|
|
@@ -98,7 +120,7 @@ module RuboCop
|
|
|
98
120
|
add_offense(text_node)
|
|
99
121
|
end
|
|
100
122
|
return
|
|
101
|
-
when :redirect_to, :redirect_back
|
|
123
|
+
when :redirect_to, :redirect_back, :redirect_back_or_to
|
|
102
124
|
text_node = redirect_to_flash(node).to_a.last
|
|
103
125
|
when :[]=
|
|
104
126
|
text_node = flash_assignment?(node)
|
|
@@ -86,10 +86,12 @@ module RuboCop
|
|
|
86
86
|
check_add_reference(node)
|
|
87
87
|
end
|
|
88
88
|
|
|
89
|
+
# rubocop:todo InternalAffairs/ItblockHandler -- Add an alias for `on_itblock` alongside the tests for `on_numblock`.
|
|
89
90
|
def on_block(node)
|
|
90
91
|
check_change_table(node)
|
|
91
92
|
end
|
|
92
93
|
alias on_numblock on_block
|
|
94
|
+
# rubocop:enable InternalAffairs/ItblockHandler
|
|
93
95
|
|
|
94
96
|
private
|
|
95
97
|
|
|
@@ -54,6 +54,7 @@ module RuboCop
|
|
|
54
54
|
# # good
|
|
55
55
|
# a.presence&.foo
|
|
56
56
|
#
|
|
57
|
+
# @example
|
|
57
58
|
# # good
|
|
58
59
|
# a.present? ? a[1] : nil
|
|
59
60
|
#
|
|
@@ -62,13 +63,17 @@ module RuboCop
|
|
|
62
63
|
#
|
|
63
64
|
# # good
|
|
64
65
|
# a.present? ? a > 1 : nil
|
|
66
|
+
#
|
|
67
|
+
# # good
|
|
65
68
|
# a <= 0 if a.present?
|
|
69
|
+
#
|
|
70
|
+
# # good
|
|
71
|
+
# a << "bar" if a.present?
|
|
66
72
|
class Presence < Base
|
|
67
73
|
include RangeHelp
|
|
68
74
|
extend AutoCorrector
|
|
69
75
|
|
|
70
76
|
MSG = 'Use `%<prefer>s` instead of `%<current>s`.'
|
|
71
|
-
INDEX_ACCESS_METHODS = %i[[] []=].freeze
|
|
72
77
|
|
|
73
78
|
def_node_matcher :redundant_receiver_and_other, <<~PATTERN
|
|
74
79
|
{
|
|
@@ -119,14 +124,14 @@ module RuboCop
|
|
|
119
124
|
private
|
|
120
125
|
|
|
121
126
|
def register_offense(node, receiver, other)
|
|
122
|
-
replacement = replacement(receiver, other
|
|
127
|
+
replacement = replacement(node, receiver, other)
|
|
123
128
|
add_offense(node, message: message(node, replacement)) do |corrector|
|
|
124
129
|
corrector.replace(node, replacement)
|
|
125
130
|
end
|
|
126
131
|
end
|
|
127
132
|
|
|
128
133
|
def register_chain_offense(node, receiver, chain)
|
|
129
|
-
replacement = chain_replacement(receiver, chain
|
|
134
|
+
replacement = chain_replacement(receiver, chain)
|
|
130
135
|
add_offense(node, message: message(node, replacement)) do |corrector|
|
|
131
136
|
corrector.replace(node, replacement)
|
|
132
137
|
end
|
|
@@ -141,7 +146,7 @@ module RuboCop
|
|
|
141
146
|
end
|
|
142
147
|
|
|
143
148
|
def ignore_chain_node?(node)
|
|
144
|
-
|
|
149
|
+
node.assignment? || node.operator_method?
|
|
145
150
|
end
|
|
146
151
|
|
|
147
152
|
def message(node, replacement)
|
|
@@ -158,7 +163,7 @@ module RuboCop
|
|
|
158
163
|
end
|
|
159
164
|
end
|
|
160
165
|
|
|
161
|
-
def replacement(receiver, other
|
|
166
|
+
def replacement(node, receiver, other)
|
|
162
167
|
or_source = if other&.send_type?
|
|
163
168
|
build_source_for_or_method(other)
|
|
164
169
|
elsif other.nil? || other.nil_type?
|
|
@@ -168,7 +173,13 @@ module RuboCop
|
|
|
168
173
|
end
|
|
169
174
|
|
|
170
175
|
replaced = "#{receiver.source}.presence#{or_source}"
|
|
171
|
-
|
|
176
|
+
require_parentheses?(node, or_source) ? "(#{replaced})" : replaced
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def require_parentheses?(node, or_source)
|
|
180
|
+
return false if or_source.empty?
|
|
181
|
+
|
|
182
|
+
(node.parent&.send_type? && !node.parent.parenthesized?) || node.parent&.and_type?
|
|
172
183
|
end
|
|
173
184
|
|
|
174
185
|
def build_source_for_or_method(other)
|
|
@@ -186,14 +197,10 @@ module RuboCop
|
|
|
186
197
|
range_between(node.source_range.begin_pos, node.first_argument.source_range.begin_pos - 1)
|
|
187
198
|
end
|
|
188
199
|
|
|
189
|
-
def chain_replacement(receiver, chain
|
|
200
|
+
def chain_replacement(receiver, chain)
|
|
190
201
|
replaced = "#{receiver.source}.presence&.#{chain.method_name}"
|
|
191
202
|
replaced += "(#{chain.arguments.map(&:source).join(', ')})" if chain.arguments?
|
|
192
|
-
|
|
193
|
-
end
|
|
194
|
-
|
|
195
|
-
def index_access_method?(node)
|
|
196
|
-
INDEX_ACCESS_METHODS.include?(node.method_name)
|
|
203
|
+
replaced
|
|
197
204
|
end
|
|
198
205
|
end
|
|
199
206
|
end
|
|
@@ -39,7 +39,7 @@ module RuboCop
|
|
|
39
39
|
(block $(send nil? :task ...) ...)
|
|
40
40
|
PATTERN
|
|
41
41
|
|
|
42
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
|
|
42
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
43
43
|
task_definition?(node) do |task_method|
|
|
44
44
|
return if task_name(task_method) == :default
|
|
45
45
|
return if with_dependencies?(task_method)
|
|
@@ -14,12 +14,15 @@ module RuboCop
|
|
|
14
14
|
# @example
|
|
15
15
|
# # bad
|
|
16
16
|
# JSON.parse(response.body)
|
|
17
|
-
#
|
|
18
|
-
#
|
|
17
|
+
# Nokogiri::HTML(response.body)
|
|
18
|
+
# Nokogiri::HTML4(response.body)
|
|
19
|
+
# Nokogiri::HTML5(response.body)
|
|
19
20
|
# Nokogiri::HTML.parse(response.body)
|
|
20
|
-
#
|
|
21
|
-
# # bad
|
|
21
|
+
# Nokogiri::HTML4.parse(response.body)
|
|
22
22
|
# Nokogiri::HTML5.parse(response.body)
|
|
23
|
+
# Nokogiri::HTML::Document.parse(response.body)
|
|
24
|
+
# Nokogiri::HTML4::Document.parse(response.body)
|
|
25
|
+
# Nokogiri::HTML5::Document.parse(response.body)
|
|
23
26
|
#
|
|
24
27
|
# # good
|
|
25
28
|
# response.parsed_body
|
|
@@ -27,71 +30,77 @@ module RuboCop
|
|
|
27
30
|
extend AutoCorrector
|
|
28
31
|
extend TargetRailsVersion
|
|
29
32
|
|
|
30
|
-
|
|
33
|
+
MSG = 'Prefer `response.parsed_body`.'
|
|
34
|
+
|
|
35
|
+
HTML = %i[HTML HTML4 HTML5].to_set.freeze
|
|
36
|
+
|
|
37
|
+
RESTRICT_ON_SEND = [:parse, *HTML].freeze
|
|
31
38
|
|
|
32
39
|
minimum_target_rails_version 5.0
|
|
33
40
|
|
|
34
41
|
# @!method json_parse_response_body?(node)
|
|
35
42
|
def_node_matcher :json_parse_response_body?, <<~PATTERN
|
|
36
|
-
(send
|
|
37
|
-
(const {nil? cbase} :JSON)
|
|
38
|
-
:parse
|
|
39
|
-
(send
|
|
40
|
-
(send nil? :response)
|
|
41
|
-
:body
|
|
42
|
-
)
|
|
43
|
-
)
|
|
43
|
+
(send #json? :parse #response_body?)
|
|
44
44
|
PATTERN
|
|
45
45
|
|
|
46
|
-
# @!method
|
|
47
|
-
def_node_matcher :
|
|
48
|
-
(send
|
|
49
|
-
(const
|
|
50
|
-
(const {nil? cbase} :Nokogiri)
|
|
51
|
-
${:HTML :HTML5}
|
|
52
|
-
)
|
|
53
|
-
:parse
|
|
54
|
-
(send
|
|
55
|
-
(send nil? :response)
|
|
56
|
-
:body
|
|
57
|
-
)
|
|
58
|
-
)
|
|
46
|
+
# @!method nokogiri_html_response_body?(node)
|
|
47
|
+
def_node_matcher :nokogiri_html_response_body?, <<~PATTERN
|
|
48
|
+
(send #nokogiri? HTML #response_body?)
|
|
59
49
|
PATTERN
|
|
60
50
|
|
|
61
|
-
|
|
62
|
-
|
|
51
|
+
# @!method nokogiri_html_parse_response_body?(node)
|
|
52
|
+
def_node_matcher :nokogiri_html_parse_response_body?, <<~PATTERN
|
|
53
|
+
(send #nokogiri_html? :parse #response_body?)
|
|
54
|
+
PATTERN
|
|
63
55
|
|
|
64
|
-
|
|
56
|
+
# @!method nokogiri_html_document_parse_response_body?(node)
|
|
57
|
+
def_node_matcher :nokogiri_html_document_parse_response_body?, <<~PATTERN
|
|
58
|
+
(send (const #nokogiri_html? :Document) :parse #response_body?)
|
|
59
|
+
PATTERN
|
|
65
60
|
|
|
66
|
-
|
|
67
|
-
|
|
61
|
+
# @!method json?(node)
|
|
62
|
+
def_node_matcher :json?, <<~PATTERN
|
|
63
|
+
(const {nil? cbase} :JSON)
|
|
64
|
+
PATTERN
|
|
68
65
|
|
|
69
|
-
|
|
66
|
+
# @!method nokogiri?(node)
|
|
67
|
+
def_node_matcher :nokogiri?, <<~PATTERN
|
|
68
|
+
(const {nil? cbase} :Nokogiri)
|
|
69
|
+
PATTERN
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
# @!method nokogiri_html?(node)
|
|
72
|
+
def_node_matcher :nokogiri_html?, <<~PATTERN
|
|
73
|
+
(const #nokogiri? HTML)
|
|
74
|
+
PATTERN
|
|
74
75
|
|
|
75
|
-
|
|
76
|
-
|
|
76
|
+
# @!method response_body?(node)
|
|
77
|
+
def_node_matcher :response_body?, <<~PATTERN
|
|
78
|
+
(send (send nil? :response) :body)
|
|
79
|
+
PATTERN
|
|
80
|
+
|
|
81
|
+
def on_send(node)
|
|
82
|
+
return unless html_offense?(node) || json_offense?(node)
|
|
77
83
|
|
|
78
|
-
add_offense(
|
|
79
|
-
node,
|
|
80
|
-
message: 'Prefer `response.parsed_body` to `JSON.parse(response.body)`.'
|
|
81
|
-
) do |corrector|
|
|
82
|
-
autocorrect(corrector, node)
|
|
84
|
+
add_offense(node) do |corrector|
|
|
85
|
+
corrector.replace(node, 'response.parsed_body')
|
|
83
86
|
end
|
|
84
87
|
end
|
|
85
88
|
|
|
86
|
-
|
|
87
|
-
return unless (const = nokogiri_html_parse_response_body(node))
|
|
89
|
+
private
|
|
88
90
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
91
|
+
def html_offense?(node)
|
|
92
|
+
support_response_parsed_body_for_html? &&
|
|
93
|
+
(nokogiri_html_response_body?(node) ||
|
|
94
|
+
nokogiri_html_parse_response_body?(node) ||
|
|
95
|
+
nokogiri_html_document_parse_response_body?(node))
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def json_offense?(node)
|
|
99
|
+
json_parse_response_body?(node)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def support_response_parsed_body_for_html?
|
|
103
|
+
target_rails_version >= 7.1
|
|
95
104
|
end
|
|
96
105
|
end
|
|
97
106
|
end
|
|
@@ -53,7 +53,9 @@ module RuboCop
|
|
|
53
53
|
|
|
54
54
|
return unless select_method_nodes.one?
|
|
55
55
|
|
|
56
|
-
select_method_nodes.first
|
|
56
|
+
select_node = select_method_nodes.first
|
|
57
|
+
|
|
58
|
+
receiver_chain?(node, select_node) ? select_node : nil
|
|
57
59
|
end
|
|
58
60
|
|
|
59
61
|
# rubocop:disable Metrics/AbcSize
|
|
@@ -81,6 +83,18 @@ module RuboCop
|
|
|
81
83
|
|
|
82
84
|
argument == column_name
|
|
83
85
|
end
|
|
86
|
+
|
|
87
|
+
def receiver_chain?(map_node, select_node)
|
|
88
|
+
current = map_node.receiver
|
|
89
|
+
while current
|
|
90
|
+
current = current.children.last if current.begin_type?
|
|
91
|
+
return true if current.equal?(select_node)
|
|
92
|
+
break unless current.call_type?
|
|
93
|
+
|
|
94
|
+
current = current.receiver
|
|
95
|
+
end
|
|
96
|
+
false
|
|
97
|
+
end
|
|
84
98
|
end
|
|
85
99
|
end
|
|
86
100
|
end
|
|
@@ -5,6 +5,15 @@ module RuboCop
|
|
|
5
5
|
module Rails
|
|
6
6
|
# Enforces the use of `ActionController::Parameters#expect` as a method for strong parameter handling.
|
|
7
7
|
#
|
|
8
|
+
# In the following cases, `params[:key]` is treated as a key that is expected to be passed from the HTTP client,
|
|
9
|
+
# and the cop detects it using the `expect` method.
|
|
10
|
+
#
|
|
11
|
+
# - Method calls on `params[:key]` without comparison methods
|
|
12
|
+
# - Passing `params[:key]` as an argument to finder methods that raise on missing records
|
|
13
|
+
# - Strong parameter methods using `require` or `permit`
|
|
14
|
+
#
|
|
15
|
+
# Other cases are not detected, as they are cases where `params[:key]` may not be passed from the HTTP client.
|
|
16
|
+
#
|
|
8
17
|
# @safety
|
|
9
18
|
# This cop's autocorrection is considered unsafe because there are cases where the HTTP status may change
|
|
10
19
|
# from 500 to 400 when handling invalid parameters. This change, however, reflects an intentional
|
|
@@ -14,6 +23,24 @@ module RuboCop
|
|
|
14
23
|
# @example
|
|
15
24
|
#
|
|
16
25
|
# # bad
|
|
26
|
+
# params[:key].do_something
|
|
27
|
+
#
|
|
28
|
+
# # good
|
|
29
|
+
# params.expect(:key).do_something
|
|
30
|
+
#
|
|
31
|
+
# # bad
|
|
32
|
+
# Model.find(params[:id])
|
|
33
|
+
#
|
|
34
|
+
# # good
|
|
35
|
+
# Model.find(params.expect(:id))
|
|
36
|
+
#
|
|
37
|
+
# # bad
|
|
38
|
+
# Model.find_by!(key: params[:key])
|
|
39
|
+
#
|
|
40
|
+
# # good
|
|
41
|
+
# Model.find_by!(key: params.expect(:key))
|
|
42
|
+
#
|
|
43
|
+
# # bad
|
|
17
44
|
# params.require(:user).permit(:name, :age)
|
|
18
45
|
# params.permit(user: [:name, :age]).require(:user)
|
|
19
46
|
#
|
|
@@ -25,10 +52,16 @@ module RuboCop
|
|
|
25
52
|
extend TargetRailsVersion
|
|
26
53
|
|
|
27
54
|
MSG = 'Use `%<prefer>s` instead.'
|
|
28
|
-
RESTRICT_ON_SEND = %i[require permit].freeze
|
|
55
|
+
RESTRICT_ON_SEND = %i[[] require permit].freeze
|
|
56
|
+
PRESENCE_CHECK_METHODS = %i[nil? blank? present? presence].freeze
|
|
57
|
+
RAISING_FINDER_METHODS = %i[find find_by! find_sole_by].freeze
|
|
29
58
|
|
|
30
59
|
minimum_target_rails_version 8.0
|
|
31
60
|
|
|
61
|
+
def_node_matcher :params_bracket_access, <<~PATTERN
|
|
62
|
+
(send (send nil? :params) :[] $_)
|
|
63
|
+
PATTERN
|
|
64
|
+
|
|
32
65
|
def_node_matcher :params_require_permit, <<~PATTERN
|
|
33
66
|
$(call
|
|
34
67
|
$(call
|
|
@@ -46,6 +79,11 @@ module RuboCop
|
|
|
46
79
|
def on_send(node)
|
|
47
80
|
return if part_of_ignored_node?(node)
|
|
48
81
|
|
|
82
|
+
if (params_key = params_bracket_access(node))
|
|
83
|
+
register_bracket_access_offense(node, params_key)
|
|
84
|
+
return
|
|
85
|
+
end
|
|
86
|
+
|
|
49
87
|
if (permit_method, require_method = params_require_permit(node))
|
|
50
88
|
range = offense_range(require_method, node)
|
|
51
89
|
prefer = expect_method(require_method, permit_method)
|
|
@@ -74,6 +112,38 @@ module RuboCop
|
|
|
74
112
|
|
|
75
113
|
private
|
|
76
114
|
|
|
115
|
+
def register_bracket_access_offense(node, params_key)
|
|
116
|
+
return unless offensive_bracket_access?(node)
|
|
117
|
+
|
|
118
|
+
range = offense_range(node, node)
|
|
119
|
+
prefer = "expect(#{params_key.source})"
|
|
120
|
+
|
|
121
|
+
add_offense(range, message: format(MSG, prefer: prefer)) do |corrector|
|
|
122
|
+
corrector.replace(range, ".#{prefer}")
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
127
|
+
def offensive_bracket_access?(node)
|
|
128
|
+
return false unless (parent = node.parent)
|
|
129
|
+
return false if parent.or_type?
|
|
130
|
+
return true if parent.each_ancestor(:call).any? { |node| raising_finder_method?(node) }
|
|
131
|
+
return false unless parent.call_type?
|
|
132
|
+
|
|
133
|
+
if parent.receiver == node
|
|
134
|
+
return false if parent.comparison_method?
|
|
135
|
+
|
|
136
|
+
!parent.method?(:[]) && !PRESENCE_CHECK_METHODS.include?(parent.method_name)
|
|
137
|
+
else
|
|
138
|
+
raising_finder_method?(parent)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
142
|
+
|
|
143
|
+
def raising_finder_method?(node)
|
|
144
|
+
RAISING_FINDER_METHODS.include?(node.method_name)
|
|
145
|
+
end
|
|
146
|
+
|
|
77
147
|
def offense_range(method_node, node)
|
|
78
148
|
method_node.loc.selector.join(node.source_range.end)
|
|
79
149
|
end
|
|
@@ -13,10 +13,26 @@ module RuboCop
|
|
|
13
13
|
# # bad
|
|
14
14
|
# Rails.env.proudction?
|
|
15
15
|
# Rails.env == 'proudction'
|
|
16
|
+
# Rails.env != 'proudction'
|
|
16
17
|
#
|
|
17
18
|
# # good
|
|
18
19
|
# Rails.env.production?
|
|
19
20
|
# Rails.env == 'production'
|
|
21
|
+
# Rails.env != 'production'
|
|
22
|
+
#
|
|
23
|
+
# @example
|
|
24
|
+
# # bad
|
|
25
|
+
# case Rails.env
|
|
26
|
+
# when 'proudction'
|
|
27
|
+
# do_something
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# # good
|
|
31
|
+
# case Rails.env
|
|
32
|
+
# when 'production'
|
|
33
|
+
# do_something
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
20
36
|
class UnknownEnv < Base
|
|
21
37
|
MSG = 'Unknown environment `%<name>s`.'
|
|
22
38
|
MSG_SIMILAR = 'Unknown environment `%<name>s`. Did you mean `%<similar>s`?'
|
|
@@ -33,8 +49,8 @@ module RuboCop
|
|
|
33
49
|
|
|
34
50
|
def_node_matcher :unknown_environment_equal?, <<~PATTERN
|
|
35
51
|
{
|
|
36
|
-
(send #rails_env? {:== :===} $(str #unknown_env_name?))
|
|
37
|
-
(send $(str #unknown_env_name?) {:== :===} #rails_env?)
|
|
52
|
+
(send #rails_env? {:== :=== :!=} $(str #unknown_env_name?))
|
|
53
|
+
(send $(str #unknown_env_name?) {:== :=== :!=} #rails_env?)
|
|
38
54
|
}
|
|
39
55
|
PATTERN
|
|
40
56
|
|
|
@@ -49,12 +65,21 @@ module RuboCop
|
|
|
49
65
|
end
|
|
50
66
|
end
|
|
51
67
|
|
|
52
|
-
|
|
68
|
+
def on_case(node)
|
|
69
|
+
return unless rails_env?(node.condition)
|
|
53
70
|
|
|
54
|
-
|
|
55
|
-
|
|
71
|
+
node.when_branches.each do |branch|
|
|
72
|
+
branch.conditions.each do |condition|
|
|
73
|
+
next unless condition.str_type?
|
|
74
|
+
next unless unknown_env_name?(condition.value)
|
|
75
|
+
|
|
76
|
+
add_offense(condition, message: message(condition.value))
|
|
77
|
+
end
|
|
78
|
+
end
|
|
56
79
|
end
|
|
57
80
|
|
|
81
|
+
private
|
|
82
|
+
|
|
58
83
|
def message(name)
|
|
59
84
|
name = name.to_s.chomp('?')
|
|
60
85
|
|
|
@@ -78,19 +103,21 @@ module RuboCop
|
|
|
78
103
|
|
|
79
104
|
def unknown_env_predicate?(name)
|
|
80
105
|
name = name.to_s
|
|
81
|
-
name.end_with?('?') && !environments.include?(name[0..-2])
|
|
106
|
+
name.end_with?('?') && !environments(with_local: true).include?(name[0..-2])
|
|
82
107
|
end
|
|
83
108
|
|
|
84
109
|
def unknown_env_name?(name)
|
|
85
110
|
!environments.include?(name)
|
|
86
111
|
end
|
|
87
112
|
|
|
88
|
-
def environments
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
113
|
+
def environments(with_local: false)
|
|
114
|
+
environments = cop_config['Environments'] || []
|
|
115
|
+
environments += ['local'] if with_local && supports_local?
|
|
116
|
+
environments
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def supports_local?
|
|
120
|
+
target_rails_version >= 7.1
|
|
94
121
|
end
|
|
95
122
|
end
|
|
96
123
|
end
|
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.35.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bozhidar Batsov
|
|
@@ -269,7 +269,7 @@ metadata:
|
|
|
269
269
|
homepage_uri: https://docs.rubocop.org/rubocop-rails/
|
|
270
270
|
changelog_uri: https://github.com/rubocop/rubocop-rails/blob/master/CHANGELOG.md
|
|
271
271
|
source_code_uri: https://github.com/rubocop/rubocop-rails/
|
|
272
|
-
documentation_uri: https://docs.rubocop.org/rubocop-rails/2.
|
|
272
|
+
documentation_uri: https://docs.rubocop.org/rubocop-rails/2.35/
|
|
273
273
|
bug_tracker_uri: https://github.com/rubocop/rubocop-rails/issues
|
|
274
274
|
rubygems_mfa_required: 'true'
|
|
275
275
|
default_lint_roller_plugin: RuboCop::Rails::Plugin
|