Skip to content

Commit d27afd6

Browse files
committed
Rename cop and refactor to use on_send matcher
1 parent 4c226a1 commit d27afd6

File tree

6 files changed

+285
-145
lines changed

6 files changed

+285
-145
lines changed

config/default.yml

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,14 @@ Rails/DeprecatedActiveModelErrorsMethods:
394394
VersionAdded: '2.14'
395395
VersionChanged: '2.18'
396396

397+
Rails/DeprecatedHttpStatusNames:
398+
Description: 'Use the new HTTP status names instead of deprecated ones.'
399+
Enabled: pending
400+
Severity: warning
401+
VersionAdded: '<<next>>'
402+
Include:
403+
- '**/app/controllers/**/*.rb'
404+
397405
Rails/DotSeparatedKeys:
398406
Description: 'Enforces the use of dot-separated keys instead of `:scope` options in `I18n` translation methods.'
399407
StyleGuide: 'https://rails.rubystyle.guide/#dot-separated-keys'
@@ -1232,14 +1240,6 @@ Rails/UnknownEnv:
12321240
- test
12331241
- production
12341242

1235-
Rails/UnprocessableContentStatus:
1236-
Description: 'Use `:unprocessable_content` instead of `:unprocessable_entity`.'
1237-
Enabled: pending
1238-
Severity: warning
1239-
VersionAdded: '<<next>>'
1240-
Include:
1241-
- '**/app/controllers/**/*.rb'
1242-
12431243
Rails/UnusedIgnoredColumns:
12441244
Description: 'Remove a column that does not exist from `ignored_columns`.'
12451245
Enabled: false
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module Rails
6+
# Enforces the use of the new HTTP status names instead of deprecated ones.
7+
#
8+
# @example
9+
# # bad
10+
# render json: { error: "Invalid data" }, status: :unprocessable_entity
11+
# head :payload_too_large
12+
#
13+
# # good
14+
# render json: { error: "Invalid data" }, status: :unprocessable_content
15+
# head :content_too_large
16+
#
17+
class DeprecatedHttpStatusNames < Base
18+
extend AutoCorrector
19+
20+
requires_gem 'rack', '>= 3.1.0'
21+
22+
RESTRICT_ON_SEND = %i[render redirect_to head assert_response assert_redirected_to].freeze
23+
24+
# Mapping of deprecated HTTP status names to their replacements
25+
DEPRECATED_STATUSES = {
26+
unprocessable_entity: :unprocessable_content,
27+
payload_too_large: :content_too_large
28+
}.freeze
29+
30+
MSG = 'Use `:%<preferred>s` instead of `:%<deprecated>s`. The `:%<deprecated>s` status is deprecated.'
31+
32+
def_node_matcher :status_method_call, <<~PATTERN
33+
{
34+
(send nil? {:render :redirect_to} _ $hash)
35+
(send nil? {:render :redirect_to} $hash)
36+
(send nil? {:head :assert_response} $_ ...)
37+
(send nil? :assert_redirected_to _ $hash ...)
38+
(send nil? :assert_redirected_to $hash ...)
39+
}
40+
PATTERN
41+
42+
def_node_matcher :status_hash_value, <<~PATTERN
43+
(hash <(pair (sym :status) $_) ...>)
44+
PATTERN
45+
46+
def on_send(node)
47+
status_method_call(node) do |status_node|
48+
if status_node.hash_type?
49+
# Handle hash arguments like { status: :unprocessable_entity }
50+
status_hash_value(status_node) do |status_value|
51+
find_deprecated_status_names(status_value)
52+
end
53+
else
54+
# Handle positional arguments like head :unprocessable_entity
55+
find_deprecated_status_names(status_node)
56+
end
57+
end
58+
end
59+
60+
private
61+
62+
def find_deprecated_status_names(node)
63+
if node.sym_type? && DEPRECATED_STATUSES.key?(node.value)
64+
deprecated_status = node.value
65+
preferred_status = DEPRECATED_STATUSES[deprecated_status]
66+
67+
message = format(MSG, deprecated: deprecated_status, preferred: preferred_status)
68+
69+
add_offense(node, message: message) do |corrector|
70+
corrector.replace(node, ":#{preferred_status}")
71+
end
72+
elsif node.respond_to?(:children)
73+
# Recursively search child nodes (handles ternary expressions, etc.)
74+
node.children.each do |child|
75+
find_deprecated_status_names(child) if child.is_a?(Parser::AST::Node)
76+
end
77+
end
78+
end
79+
end
80+
end
81+
end
82+
end

lib/rubocop/cop/rails/unprocessable_content_status.rb

Lines changed: 0 additions & 74 deletions
This file was deleted.

lib/rubocop/cop/rails_cops.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
require_relative 'rails/delegate'
4242
require_relative 'rails/delegate_allow_blank'
4343
require_relative 'rails/deprecated_active_model_errors_methods'
44+
require_relative 'rails/deprecated_http_status_names'
4445
require_relative 'rails/dot_separated_keys'
4546
require_relative 'rails/duplicate_association'
4647
require_relative 'rails/duplicate_scope'
@@ -136,7 +137,6 @@
136137
require_relative 'rails/uniq_before_pluck'
137138
require_relative 'rails/unique_validation_without_index'
138139
require_relative 'rails/unknown_env'
139-
require_relative 'rails/unprocessable_content_status'
140140
require_relative 'rails/unused_ignored_columns'
141141
require_relative 'rails/unused_render_content'
142142
require_relative 'rails/validation'
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RuboCop::Cop::Rails::DeprecatedHttpStatusNames, :config do
4+
context 'when Rack is older than 3.1' do
5+
let(:gem_versions) { { 'rack' => '3.0.0' } }
6+
7+
it 'does nothing' do
8+
expect_no_offenses(<<~RUBY)
9+
render json: { error: 'Invalid data' }, status: :unprocessable_entity
10+
head :payload_too_large
11+
RUBY
12+
end
13+
end
14+
15+
context 'when Rack is 3.1 or later' do
16+
let(:gem_versions) { { 'rack' => '3.1.0' } }
17+
18+
context 'with :unprocessable_entity' do
19+
it 'registers an offense when using :unprocessable_entity in render' do
20+
expect_offense(<<~RUBY)
21+
render json: { error: 'Invalid data' }, status: :unprocessable_entity
22+
^^^^^^^^^^^^^^^^^^^^^ Use `:unprocessable_content` instead of `:unprocessable_entity`. The `:unprocessable_entity` status is deprecated.
23+
RUBY
24+
25+
expect_correction(<<~RUBY)
26+
render json: { error: 'Invalid data' }, status: :unprocessable_content
27+
RUBY
28+
end
29+
30+
it 'registers an offense when using :unprocessable_entity in head' do
31+
expect_offense(<<~RUBY)
32+
head :unprocessable_entity
33+
^^^^^^^^^^^^^^^^^^^^^ Use `:unprocessable_content` instead of `:unprocessable_entity`. The `:unprocessable_entity` status is deprecated.
34+
RUBY
35+
36+
expect_correction(<<~RUBY)
37+
head :unprocessable_content
38+
RUBY
39+
end
40+
41+
it 'registers an offense when using :unprocessable_entity in redirect_to' do
42+
expect_offense(<<~RUBY)
43+
redirect_to some_path, status: :unprocessable_entity
44+
^^^^^^^^^^^^^^^^^^^^^ Use `:unprocessable_content` instead of `:unprocessable_entity`. The `:unprocessable_entity` status is deprecated.
45+
RUBY
46+
47+
expect_correction(<<~RUBY)
48+
redirect_to some_path, status: :unprocessable_content
49+
RUBY
50+
end
51+
52+
it 'registers an offense when using :unprocessable_entity in assert_response' do
53+
expect_offense(<<~RUBY)
54+
assert_response :unprocessable_entity
55+
^^^^^^^^^^^^^^^^^^^^^ Use `:unprocessable_content` instead of `:unprocessable_entity`. The `:unprocessable_entity` status is deprecated.
56+
RUBY
57+
58+
expect_correction(<<~RUBY)
59+
assert_response :unprocessable_content
60+
RUBY
61+
end
62+
63+
it 'registers an offense when using :unprocessable_entity in assert_redirected_to' do
64+
expect_offense(<<~RUBY)
65+
assert_redirected_to some_path, status: :unprocessable_entity
66+
^^^^^^^^^^^^^^^^^^^^^ Use `:unprocessable_content` instead of `:unprocessable_entity`. The `:unprocessable_entity` status is deprecated.
67+
RUBY
68+
69+
expect_correction(<<~RUBY)
70+
assert_redirected_to some_path, status: :unprocessable_content
71+
RUBY
72+
end
73+
74+
it 'registers an offense when using :unprocessable_entity in ternary expression' do
75+
expect_offense(<<~RUBY)
76+
render json: { error: 'Invalid data' }, status: some_condition ? :unprocessable_entity : :ok
77+
^^^^^^^^^^^^^^^^^^^^^ Use `:unprocessable_content` instead of `:unprocessable_entity`. The `:unprocessable_entity` status is deprecated.
78+
RUBY
79+
80+
expect_correction(<<~RUBY)
81+
render json: { error: 'Invalid data' }, status: some_condition ? :unprocessable_content : :ok
82+
RUBY
83+
end
84+
85+
it 'does not register an offense when using :unprocessable_content' do
86+
expect_no_offenses(<<~RUBY)
87+
render json: { error: 'Invalid data' }, status: :unprocessable_content
88+
RUBY
89+
end
90+
91+
it 'does not register an offense when using :unprocessable_entity in hash key' do
92+
expect_no_offenses(<<~RUBY)
93+
{ unprocessable_entity: 'Invalid data' }
94+
RUBY
95+
end
96+
end
97+
98+
context 'with :payload_too_large' do
99+
it 'registers an offense when using :payload_too_large in render' do
100+
expect_offense(<<~RUBY)
101+
render json: { error: 'File too big' }, status: :payload_too_large
102+
^^^^^^^^^^^^^^^^^^ Use `:content_too_large` instead of `:payload_too_large`. The `:payload_too_large` status is deprecated.
103+
RUBY
104+
105+
expect_correction(<<~RUBY)
106+
render json: { error: 'File too big' }, status: :content_too_large
107+
RUBY
108+
end
109+
110+
it 'registers an offense when using :payload_too_large in head' do
111+
expect_offense(<<~RUBY)
112+
head :payload_too_large
113+
^^^^^^^^^^^^^^^^^^ Use `:content_too_large` instead of `:payload_too_large`. The `:payload_too_large` status is deprecated.
114+
RUBY
115+
116+
expect_correction(<<~RUBY)
117+
head :content_too_large
118+
RUBY
119+
end
120+
121+
it 'registers an offense when using :payload_too_large in redirect_to' do
122+
expect_offense(<<~RUBY)
123+
redirect_to some_path, status: :payload_too_large
124+
^^^^^^^^^^^^^^^^^^ Use `:content_too_large` instead of `:payload_too_large`. The `:payload_too_large` status is deprecated.
125+
RUBY
126+
127+
expect_correction(<<~RUBY)
128+
redirect_to some_path, status: :content_too_large
129+
RUBY
130+
end
131+
132+
it 'registers an offense when using :payload_too_large in assert_response' do
133+
expect_offense(<<~RUBY)
134+
assert_response :payload_too_large
135+
^^^^^^^^^^^^^^^^^^ Use `:content_too_large` instead of `:payload_too_large`. The `:payload_too_large` status is deprecated.
136+
RUBY
137+
138+
expect_correction(<<~RUBY)
139+
assert_response :content_too_large
140+
RUBY
141+
end
142+
143+
it 'registers an offense when using :payload_too_large in assert_redirected_to' do
144+
expect_offense(<<~RUBY)
145+
assert_redirected_to some_path, status: :payload_too_large
146+
^^^^^^^^^^^^^^^^^^ Use `:content_too_large` instead of `:payload_too_large`. The `:payload_too_large` status is deprecated.
147+
RUBY
148+
149+
expect_correction(<<~RUBY)
150+
assert_redirected_to some_path, status: :content_too_large
151+
RUBY
152+
end
153+
154+
it 'registers an offense when using :payload_too_large in ternary expression' do
155+
expect_offense(<<~RUBY)
156+
render json: { error: 'File too big' }, status: some_condition ? :payload_too_large : :ok
157+
^^^^^^^^^^^^^^^^^^ Use `:content_too_large` instead of `:payload_too_large`. The `:payload_too_large` status is deprecated.
158+
RUBY
159+
160+
expect_correction(<<~RUBY)
161+
render json: { error: 'File too big' }, status: some_condition ? :content_too_large : :ok
162+
RUBY
163+
end
164+
165+
it 'does not register an offense when using :content_too_large' do
166+
expect_no_offenses(<<~RUBY)
167+
render json: { error: 'File too big' }, status: :content_too_large
168+
RUBY
169+
end
170+
171+
it 'does not register an offense when using :payload_too_large in hash key' do
172+
expect_no_offenses(<<~RUBY)
173+
{ payload_too_large: 'File too big' }
174+
RUBY
175+
end
176+
end
177+
178+
context 'with mixed deprecated statuses' do
179+
it 'handles multiple deprecated statuses in the same code' do
180+
expect_offense(<<~RUBY)
181+
head :unprocessable_entity
182+
^^^^^^^^^^^^^^^^^^^^^ Use `:unprocessable_content` instead of `:unprocessable_entity`. The `:unprocessable_entity` status is deprecated.
183+
head :payload_too_large
184+
^^^^^^^^^^^^^^^^^^ Use `:content_too_large` instead of `:payload_too_large`. The `:payload_too_large` status is deprecated.
185+
RUBY
186+
187+
expect_correction(<<~RUBY)
188+
head :unprocessable_content
189+
head :content_too_large
190+
RUBY
191+
end
192+
end
193+
end
194+
end

0 commit comments

Comments
 (0)