Skip to content

Commit e465675

Browse files
authored
Merge pull request #970 from samrjenkins/add-unused-render-content-cop
Add new Rails/UnusedRenderContent cop
2 parents 1bcba4d + 8555ac5 commit e465675

File tree

5 files changed

+207
-0
lines changed

5 files changed

+207
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* [#967](https://github.com/rubocop/rubocop-rails/issues/967): Add new `Rails/UnusedRenderContent` cop. ([@samrjenkins][])

config/default.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1155,6 +1155,12 @@ Rails/UnusedIgnoredColumns:
11551155
Include:
11561156
- app/models/**/*.rb
11571157

1158+
Rails/UnusedRenderContent:
1159+
Description: 'Do not specify body content for a response with a non-content status code.'
1160+
Enabled: pending
1161+
Severity: warning
1162+
VersionAdded: '<<next>>'
1163+
11581164
Rails/Validation:
11591165
Description: 'Use validates :attribute, hash of validations.'
11601166
Enabled: true
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module Rails
6+
# If you try to render content along with a non-content status code (100-199, 204, 205, or 304),
7+
# it will be dropped from the response.
8+
#
9+
# This cop checks for uses of `render` which specify both body content and a non-content status.
10+
#
11+
# @example
12+
# # bad
13+
# render 'foo', status: :continue
14+
# render status: 100, plain: 'Ruby!'
15+
#
16+
# # good
17+
# render status: :continue
18+
# render status: 100
19+
class UnusedRenderContent < Base
20+
extend AutoCorrector
21+
include RangeHelp
22+
23+
MSG = 'Do not specify body content for a response with a non-content status code'
24+
RESTRICT_ON_SEND = %i[render].freeze
25+
NON_CONTENT_STATUS_CODES = Set[*100..199, 204, 205, 304] & ::Rack::Utils::SYMBOL_TO_STATUS_CODE.values
26+
NON_CONTENT_STATUSES = Set[
27+
*::Rack::Utils::SYMBOL_TO_STATUS_CODE.invert.fetch_values(*NON_CONTENT_STATUS_CODES)
28+
]
29+
BODY_OPTIONS = Set[
30+
:action,
31+
:body,
32+
:content_type,
33+
:file,
34+
:html,
35+
:inline,
36+
:json,
37+
:js,
38+
:layout,
39+
:plain,
40+
:raw,
41+
:template,
42+
:text,
43+
:xml
44+
]
45+
46+
def_node_matcher :non_content_status?, <<~PATTERN
47+
(pair
48+
(sym :status)
49+
{(sym NON_CONTENT_STATUSES) (int NON_CONTENT_STATUS_CODES)}
50+
)
51+
PATTERN
52+
53+
def_node_matcher :unused_render_content?, <<~PATTERN
54+
(send nil? :render {
55+
(hash <#non_content_status? $(pair (sym BODY_OPTIONS) _) ...>) |
56+
$({str sym} _) (hash <#non_content_status? ...>)
57+
})
58+
PATTERN
59+
60+
def on_send(node)
61+
unused_render_content?(node) do |unused_content_node|
62+
add_offense(unused_content_node)
63+
end
64+
end
65+
end
66+
end
67+
end
68+
end

lib/rubocop/cop/rails_cops.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@
129129
require_relative 'rails/unique_validation_without_index'
130130
require_relative 'rails/unknown_env'
131131
require_relative 'rails/unused_ignored_columns'
132+
require_relative 'rails/unused_render_content'
132133
require_relative 'rails/validation'
133134
require_relative 'rails/where_equals'
134135
require_relative 'rails/where_exists'
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RuboCop::Cop::Rails::UnusedRenderContent, :config do
4+
it 'does not register an offense when specifying body content with a status that takes a body' do
5+
expect_no_offenses(<<~RUBY)
6+
render status: :ok, plain: 'Ruby!'
7+
RUBY
8+
end
9+
10+
it 'does not register an offense when no body content is specified with a status that does not take a body' do
11+
expect_no_offenses(<<~RUBY)
12+
render status: :no_content
13+
RUBY
14+
end
15+
16+
it 'registers an offense when specifying status: :continue and a positional string argument' do
17+
expect_offense(<<~RUBY)
18+
render 'foo', status: :continue
19+
^^^^^ Do not specify body content for a response with a non-content status code
20+
RUBY
21+
end
22+
23+
it 'registers an offense when specifying status: :switching_protocols and a positional symbol argument across ' \
24+
'multiple lines' do
25+
expect_offense(<<~RUBY)
26+
render(
27+
:foo,
28+
^^^^ Do not specify body content for a response with a non-content status code
29+
status: :switching_protocols
30+
)
31+
RUBY
32+
end
33+
34+
it 'registers an offense when specifying status: :processing and an :action option as the last argument' do
35+
expect_offense(<<~RUBY)
36+
render status: :processing, action: :foo
37+
^^^^^^^^^^^^ Do not specify body content for a response with a non-content status code
38+
RUBY
39+
end
40+
41+
it 'registers an offense when specifying status: :early_hints and a :body option as the first argument' do
42+
expect_offense(<<~RUBY)
43+
render body: 'foo', status: :early_hints
44+
^^^^^^^^^^^ Do not specify body content for a response with a non-content status code
45+
RUBY
46+
end
47+
48+
it 'registers an offense when specifying status: :no_content and a :content_type option between other options' do
49+
expect_offense(<<~RUBY)
50+
render status: :no_content, content_type: 'foo', another: 'option'
51+
^^^^^^^^^^^^^^^^^^^ Do not specify body content for a response with a non-content status code
52+
RUBY
53+
end
54+
55+
it 'registers an offense when specifying status: :reset_content and a :file option' do
56+
expect_offense(<<~RUBY)
57+
render status: :reset_content, file: 'foo'
58+
^^^^^^^^^^^ Do not specify body content for a response with a non-content status code
59+
RUBY
60+
end
61+
62+
it 'registers an offense when specifying status: :not_modified and a :html option' do
63+
expect_offense(<<~RUBY)
64+
render status: :not_modified, html: 'foo'
65+
^^^^^^^^^^^ Do not specify body content for a response with a non-content status code
66+
RUBY
67+
end
68+
69+
it 'registers an offense when specifying status: 100 and a :inline option' do
70+
expect_offense(<<~RUBY)
71+
render status: 100, inline: 'foo'
72+
^^^^^^^^^^^^^ Do not specify body content for a response with a non-content status code
73+
RUBY
74+
end
75+
76+
it 'registers an offense when specifying status: 101 and a :json option' do
77+
expect_offense(<<~RUBY)
78+
render status: 101, json: 'foo'
79+
^^^^^^^^^^^ Do not specify body content for a response with a non-content status code
80+
RUBY
81+
end
82+
83+
it 'registers an offense when specifying status: 102 and a :js option' do
84+
expect_offense(<<~RUBY)
85+
render status: 102, js: 'foo'
86+
^^^^^^^^^ Do not specify body content for a response with a non-content status code
87+
RUBY
88+
end
89+
90+
it 'registers an offense when specifying status: 103 and a :layout option' do
91+
expect_offense(<<~RUBY)
92+
render status: 103, layout: 'foo'
93+
^^^^^^^^^^^^^ Do not specify body content for a response with a non-content status code
94+
RUBY
95+
end
96+
97+
it 'registers an offense when specifying status: 204 and a :plain option' do
98+
expect_offense(<<~RUBY)
99+
render status: 204, plain: 'foo'
100+
^^^^^^^^^^^^ Do not specify body content for a response with a non-content status code
101+
RUBY
102+
end
103+
104+
it 'registers an offense when specifying status: 205 and a :raw option' do
105+
expect_offense(<<~RUBY)
106+
render status: 205, raw: 'foo'
107+
^^^^^^^^^^ Do not specify body content for a response with a non-content status code
108+
RUBY
109+
end
110+
111+
it 'registers an offense when specifying status: 304 and a :template option' do
112+
expect_offense(<<~RUBY)
113+
render status: 304, template: 'foo'
114+
^^^^^^^^^^^^^^^ Do not specify body content for a response with a non-content status code
115+
RUBY
116+
end
117+
118+
it 'registers an offense when specifying status: 304 and a :text option' do
119+
expect_offense(<<~RUBY)
120+
render status: 304, text: 'foo'
121+
^^^^^^^^^^^ Do not specify body content for a response with a non-content status code
122+
RUBY
123+
end
124+
125+
it 'registers an offense when specifying status: 304 and a :xml option' do
126+
expect_offense(<<~RUBY)
127+
render status: 304, xml: 'foo'
128+
^^^^^^^^^^ Do not specify body content for a response with a non-content status code
129+
RUBY
130+
end
131+
end

0 commit comments

Comments
 (0)