Skip to content

Commit c6729bb

Browse files
authored
Merge pull request #1541 from davidenglishmusic/redirect-back-or-to-cop
Add RedirectBackOrTo cop
2 parents 4b87f1f + 0989be8 commit c6729bb

File tree

5 files changed

+146
-0
lines changed

5 files changed

+146
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* [#1541](https://github.com/rubocop/rubocop-rails/pull/1541): Add new `Rails/RedirectBackOrTo` cop to suggest using `redirect_back_or_to` instead of `redirect_back` with `fallback_location`. ([@davidenglishmusic][])

config/default.yml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,14 @@ Rails/ReadWriteAttribute:
880880
Include:
881881
- '**/app/models/**/*.rb'
882882

883+
Rails/RedirectBackOrTo:
884+
Description: >-
885+
Use `redirect_back_or_to` instead of `redirect_back` with
886+
`fallback_location` option.
887+
Enabled: pending
888+
Severity: warning
889+
VersionAdded: '<<next>>'
890+
883891
Rails/RedundantActiveRecordAllMethod:
884892
Description: Detect redundant `all` used as a receiver for Active Record query methods.
885893
StyleGuide: 'https://rails.rubystyle.guide/#redundant-all'
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# frozen_string_literal: true
2+
3+
module RuboCop
4+
module Cop
5+
module Rails
6+
# Checks for uses of `redirect_back(fallback_location: ...)` and
7+
# suggests using `redirect_back_or_to(...)` instead.
8+
#
9+
# `redirect_back(fallback_location: ...)` was soft deprecated in Rails 7.0 and
10+
# `redirect_back_or_to` was introduced as a replacement.
11+
#
12+
# @example
13+
# # bad
14+
# redirect_back(fallback_location: root_path)
15+
#
16+
# # good
17+
# redirect_back_or_to(root_path)
18+
#
19+
# # bad
20+
# redirect_back(fallback_location: root_path, allow_other_host: false)
21+
#
22+
# # good
23+
# redirect_back_or_to(root_path, allow_other_host: false)
24+
#
25+
class RedirectBackOrTo < Base
26+
extend AutoCorrector
27+
extend TargetRailsVersion
28+
29+
minimum_target_rails_version 7.0
30+
31+
MSG = 'Use `redirect_back_or_to` instead of `redirect_back` with `:fallback_location` keyword argument.'
32+
RESTRICT_ON_SEND = %i[redirect_back].freeze
33+
34+
def_node_matcher :redirect_back_with_fallback_location, <<~PATTERN
35+
(send nil? :redirect_back
36+
(hash <$(pair (sym :fallback_location) $_) ...>)
37+
)
38+
PATTERN
39+
40+
def on_send(node)
41+
redirect_back_with_fallback_location(node) do |fallback_pair, fallback_value|
42+
add_offense(node.loc.selector) do |corrector|
43+
correct_redirect_back(corrector, node, fallback_pair, fallback_value)
44+
end
45+
end
46+
end
47+
48+
private
49+
50+
def correct_redirect_back(corrector, node, fallback_pair, fallback_value)
51+
corrector.replace(node.loc.selector, 'redirect_back_or_to')
52+
53+
hash_arg = node.first_argument
54+
55+
if hash_arg.pairs.one?
56+
corrector.replace(hash_arg, fallback_value.source)
57+
else
58+
remove_fallback_location_pair(corrector, hash_arg, fallback_pair)
59+
first_pair = hash_arg.pairs.find { |pair| pair != fallback_pair }
60+
corrector.insert_before(first_pair, "#{fallback_value.source}, ")
61+
end
62+
end
63+
64+
def remove_fallback_location_pair(corrector, hash_node, fallback_pair)
65+
pairs = hash_node.pairs
66+
index = pairs.index(fallback_pair)
67+
68+
if pairs.one?
69+
corrector.remove(fallback_pair)
70+
elsif index.zero?
71+
remove_first_pair(corrector, fallback_pair, pairs[1])
72+
else
73+
remove_non_first_pair(corrector, fallback_pair, pairs[index - 1])
74+
end
75+
end
76+
77+
def remove_first_pair(corrector, fallback_pair, next_pair)
78+
range = fallback_pair.source_range.join(next_pair.source_range.begin)
79+
corrector.remove(range)
80+
end
81+
82+
def remove_non_first_pair(corrector, fallback_pair, prev_pair)
83+
range = prev_pair.source_range.end.join(fallback_pair.source_range.end)
84+
corrector.remove(range)
85+
end
86+
end
87+
end
88+
end
89+
end

lib/rubocop/cop/rails_cops.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@
104104
require_relative 'rails/redundant_presence_validation_on_belongs_to'
105105
require_relative 'rails/redundant_receiver_in_with_options'
106106
require_relative 'rails/redundant_travel_back'
107+
require_relative 'rails/redirect_back_or_to'
107108
require_relative 'rails/reflection_class_name'
108109
require_relative 'rails/refute_methods'
109110
require_relative 'rails/relative_date_constant'
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# frozen_string_literal: true
2+
3+
RSpec.describe RuboCop::Cop::Rails::RedirectBackOrTo, :config do
4+
context 'Rails >= 7.0', :rails70 do
5+
it 'registers an offense and corrects when using redirect_back with a fallback_location' do
6+
expect_offense(<<~RUBY)
7+
redirect_back(fallback_location: root_path)
8+
^^^^^^^^^^^^^ Use `redirect_back_or_to` instead of `redirect_back` with `:fallback_location` keyword argument.
9+
RUBY
10+
11+
expect_correction(<<~RUBY)
12+
redirect_back_or_to(root_path)
13+
RUBY
14+
end
15+
16+
it 'registers an offense and corrects with additional options' do
17+
expect_offense(<<~RUBY)
18+
redirect_back(fallback_location: root_path, status: 303, allow_other_host: true)
19+
^^^^^^^^^^^^^ Use `redirect_back_or_to` instead of `redirect_back` with `:fallback_location` keyword argument.
20+
RUBY
21+
22+
expect_correction(<<~RUBY)
23+
redirect_back_or_to(root_path, status: 303, allow_other_host: true)
24+
RUBY
25+
end
26+
27+
it 'registers no offense when using redirect_back_or_to' do
28+
expect_no_offenses(<<~RUBY)
29+
redirect_back_or_to(root_path)
30+
RUBY
31+
end
32+
33+
it 'registers no offense when using redirect_back without fallback_location' do
34+
expect_no_offenses(<<~RUBY)
35+
redirect_back(allow_other_host: false)
36+
RUBY
37+
end
38+
end
39+
40+
context 'Rails <= 6.1', :rails61 do
41+
it 'does not register an offense' do
42+
expect_no_offenses(<<~RUBY)
43+
redirect_back(fallback_location: root_path)
44+
RUBY
45+
end
46+
end
47+
end

0 commit comments

Comments
 (0)