Skip to content

Commit a4824e5

Browse files
committed
Introduce UnprocessableContentStatus cop
1 parent 9934f34 commit a4824e5

File tree

5 files changed

+158
-0
lines changed

5 files changed

+158
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* [#1520](https://github.com/rubocop/rubocop-rails/pull/1520): Add `Rails/UnprocessableContentStatus` cop ([@tuxagon][])

config/default.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1232,6 +1232,14 @@ Rails/UnknownEnv:
12321232
- test
12331233
- production
12341234

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+
12351243
Rails/UnusedIgnoredColumns:
12361244
Description: 'Remove a column that does not exist from `ignored_columns`.'
12371245
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 :unprocessable_content instead of :unprocessable_entity
7+
# for HTTP status codes, as :unprocessable_entity is deprecated in Rack 3.1.
8+
#
9+
# @example
10+
# # bad
11+
# render json: { error: "Invalid data" }, status: :unprocessable_entity
12+
# head :unprocessable_entity
13+
#
14+
# # good
15+
# render json: { error: "Invalid data" }, status: :unprocessable_content
16+
# head :unprocessable_content
17+
#
18+
class UnprocessableContentStatus < Base
19+
extend AutoCorrector
20+
21+
MSG = 'Use `:unprocessable_content` instead of `:unprocessable_entity`. ' \
22+
'The `:unprocessable_entity` status is deprecated.'
23+
24+
def_node_matcher :unprocessable_entity_symbol?, <<~PATTERN
25+
(sym :unprocessable_entity)
26+
PATTERN
27+
28+
def_node_matcher :status_argument?, <<~PATTERN
29+
(pair (sym :status) (sym :unprocessable_entity))
30+
PATTERN
31+
32+
def on_sym(node)
33+
return unless rack_3_1_or_newer?
34+
35+
return unless unprocessable_entity_symbol?(node)
36+
return if in_hash_key_context?(node)
37+
38+
return unless status_related_context?(node)
39+
40+
add_offense(node) do |corrector|
41+
corrector.replace(node, ':unprocessable_content')
42+
end
43+
end
44+
45+
def on_pair(node)
46+
return unless rack_3_1_or_newer?
47+
48+
status_argument?(node) do
49+
add_offense(node.value) do |corrector|
50+
corrector.replace(node.value, ':unprocessable_content')
51+
end
52+
end
53+
end
54+
55+
private
56+
57+
def rack_3_1_or_newer?
58+
Gem::Version.new(Rack::VERSION) >= Gem::Version.new('3.1.0')
59+
rescue ArgumentError, NoMethodError
60+
false
61+
end
62+
63+
def in_hash_key_context?(node)
64+
node.parent&.pair_type? && node.parent.key == node
65+
end
66+
67+
def status_related_context?(node)
68+
parent = node.parent
69+
return false unless parent
70+
71+
if parent.send_type?
72+
method_name = parent.method_name
73+
return %i[head render redirect_to].include?(method_name)
74+
end
75+
76+
# Variable assignment or ternary expression
77+
%i[lvasgn if].include?(parent.type)
78+
end
79+
end
80+
end
81+
end
82+
end

lib/rubocop/cop/rails_cops.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,7 @@
136136
require_relative 'rails/uniq_before_pluck'
137137
require_relative 'rails/unique_validation_without_index'
138138
require_relative 'rails/unknown_env'
139+
require_relative 'rails/unprocessable_content_status'
139140
require_relative 'rails/unused_ignored_columns'
140141
require_relative 'rails/unused_render_content'
141142
require_relative 'rails/validation'
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RuboCop::Cop::Rails::UnprocessableContentStatus, :config do
4+
context 'when Rack is older than 3.1 or not available' do
5+
before do
6+
allow(cop).to receive(:rack_3_1_or_newer?).and_return(false)
7+
end
8+
9+
it 'does nothing' do
10+
expect_no_offenses(<<~RUBY)
11+
render json: { error: 'Invalid data' }, status: :unprocessable_entity
12+
RUBY
13+
end
14+
end
15+
16+
context 'when Rack is 3.1 or later' do
17+
before do
18+
allow(cop).to receive(:rack_3_1_or_newer?).and_return(true)
19+
end
20+
21+
it 'registers an offense when using :unprocessable_entity in hash argument' do
22+
expect_offense(<<~RUBY)
23+
render json: { error: 'Invalid data' }, status: :unprocessable_entity
24+
^^^^^^^^^^^^^^^^^^^^^ Use `:unprocessable_content` instead of `:unprocessable_entity`. The `:unprocessable_entity` status is deprecated.
25+
RUBY
26+
27+
expect_correction(<<~RUBY)
28+
render json: { error: 'Invalid data' }, status: :unprocessable_content
29+
RUBY
30+
end
31+
32+
it 'registers an offense when using :unprocessable_entity in head' do
33+
expect_offense(<<~RUBY)
34+
head :unprocessable_entity
35+
^^^^^^^^^^^^^^^^^^^^^ Use `:unprocessable_content` instead of `:unprocessable_entity`. The `:unprocessable_entity` status is deprecated.
36+
RUBY
37+
38+
expect_correction(<<~RUBY)
39+
head :unprocessable_content
40+
RUBY
41+
end
42+
43+
it 'registers an offense when using :unprocessable_entity in ternary expression' do
44+
expect_offense(<<~RUBY)
45+
render json: { error: 'Invalid data' }, status: :unprocessable_entity ? :unprocessable_content : :ok
46+
^^^^^^^^^^^^^^^^^^^^^ Use `:unprocessable_content` instead of `:unprocessable_entity`. The `:unprocessable_entity` status is deprecated.
47+
RUBY
48+
49+
expect_correction(<<~RUBY)
50+
render json: { error: 'Invalid data' }, status: :unprocessable_content ? :unprocessable_content : :ok
51+
RUBY
52+
end
53+
54+
it 'does not register an offense when using :unprocessable_content' do
55+
expect_no_offenses(<<~RUBY)
56+
render json: { error: 'Invalid data' }, status: :unprocessable_content
57+
RUBY
58+
end
59+
60+
it 'does not register an offense when using :unprocessable_entity in hash key' do
61+
expect_no_offenses(<<~RUBY)
62+
{ unprocessable_entity: 'Invalid data' }
63+
RUBY
64+
end
65+
end
66+
end

0 commit comments

Comments
 (0)