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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cceb6a56dba516cdc368294a42ff59397a70f758801e5a1285df315a9d4abc8f
4
- data.tar.gz: 555f1da6ed7abe2a2be37867c5405abaa16007b77be36e68c67b2a79d11865c9
3
+ metadata.gz: e4946e79ccf9661248df014d69703665df3b0a72aa800e8c456afc59594ad944
4
+ data.tar.gz: 7575ea946eb4e6985d6c0c365a0f39345fc6d32b0634c74949526f987d1ac38d
5
5
  SHA512:
6
- metadata.gz: 25a1c6aecce64df1814142ee6a4a0dfb6014ddc9f7a4fdb4c9f59035277a63512163bab201202f9b90fcfbaeae6bf7c87479326f4415f853beaf6fd7c3e64486
7
- data.tar.gz: 5eec318843c618ae00bf370ef17060d90b5978e427812a90d4b8f7015ab25af5a09a3adee9e973dd821a5ad67d4ba0f503af11a067ceb30a3e81823cfbab2b93
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['development']
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
- if respond_to?(:requires_gem)
16
- case version
17
- when Integer, Float then requires_gem(TARGET_GEM_NAME, ">= #{version}")
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
- if respond_to?(:requires_gem)
28
- return false unless gem_requirements
22
+ return false unless gem_requirements
29
23
 
30
- gem_requirement = gem_requirements[TARGET_GEM_NAME]
31
- return true unless gem_requirement # If we have no requirement, then we support all versions
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
- gem_requirement.satisfied_by?(Gem::Version.new(version))
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, node.left_sibling)
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, node.left_sibling)
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
- index_access_method?(node) || node.assignment? || node.arithmetic_operation? || node.comparison_method?
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, left_sibling)
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
- left_sibling ? "(#{replaced})" : replaced
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, left_sibling)
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
- left_sibling ? "(#{replaced})" : replaced
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
- # # bad
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
- RESTRICT_ON_SEND = %i[parse].freeze
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 nokogiri_html_parse_response_body(node)
47
- def_node_matcher :nokogiri_html_parse_response_body, <<~PATTERN
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
- def on_send(node)
62
- check_json_parse_response_body(node)
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
- return unless target_rails_version >= 7.1
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
- check_nokogiri_html_parse_response_body(node)
67
- end
61
+ # @!method json?(node)
62
+ def_node_matcher :json?, <<~PATTERN
63
+ (const {nil? cbase} :JSON)
64
+ PATTERN
68
65
 
69
- private
66
+ # @!method nokogiri?(node)
67
+ def_node_matcher :nokogiri?, <<~PATTERN
68
+ (const {nil? cbase} :Nokogiri)
69
+ PATTERN
70
70
 
71
- def autocorrect(corrector, node)
72
- corrector.replace(node, 'response.parsed_body')
73
- end
71
+ # @!method nokogiri_html?(node)
72
+ def_node_matcher :nokogiri_html?, <<~PATTERN
73
+ (const #nokogiri? HTML)
74
+ PATTERN
74
75
 
75
- def check_json_parse_response_body(node)
76
- return unless json_parse_response_body?(node)
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
- def check_nokogiri_html_parse_response_body(node)
87
- return unless (const = nokogiri_html_parse_response_body(node))
89
+ private
88
90
 
89
- add_offense(
90
- node,
91
- message: "Prefer `response.parsed_body` to `Nokogiri::#{const}.parse(response.body)`."
92
- ) do |corrector|
93
- autocorrect(corrector, node)
94
- end
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
- private
68
+ def on_case(node)
69
+ return unless rails_env?(node.condition)
53
70
 
54
- def collect_variable_like_names(_scope)
55
- environments
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
- @environments ||= begin
90
- environments = cop_config['Environments'].dup || []
91
- environments << 'local' if target_rails_version >= 7.1
92
- environments
93
- end
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
@@ -4,7 +4,7 @@ module RuboCop
4
4
  module Rails
5
5
  # This module holds the RuboCop Rails version information.
6
6
  module Version
7
- STRING = '2.34.3'
7
+ STRING = '2.35.0'
8
8
 
9
9
  def self.document_version
10
10
  STRING.match('\d+\.\d+').to_s
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.34.3
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.34/
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