Skip to content

Commit dbd2aaf

Browse files
authored
Feat make redirect status configurable (#1838)
* feat: make default redirect status configurable * chore: use configured redirect status in turbolinks redirect module * test: add specs for globally configurable redirect status
1 parent 10752ad commit dbd2aaf

File tree

3 files changed

+96
-12
lines changed

3 files changed

+96
-12
lines changed

spec/lucky/action_redirect_spec.cr

+37
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,26 @@ describe Lucky::Action do
5151
should_redirect(action, to: "/somewhere", status: 301)
5252
end
5353

54+
it "redirects with a globally configured custom status" do
55+
Lucky::Redirectable.temp_config(redirect_status: 303) do
56+
action = RedirectAction.new(build_context, params)
57+
action.redirect to: "/somewhere"
58+
should_redirect(action, to: "/somewhere", status: 303)
59+
60+
action = RedirectAction.new(build_context, params)
61+
action.redirect to: RedirectAction.route
62+
should_redirect(action, to: RedirectAction.path, status: 303)
63+
64+
action = RedirectAction.new(build_context, params)
65+
action.redirect to: RedirectAction
66+
should_redirect(action, to: RedirectAction.path, status: 303)
67+
68+
action = RedirectAction.new(build_context, params)
69+
action.redirect to: ActionWithPrefix
70+
should_redirect(action, to: "/prefix/redirect_test2", status: 303)
71+
end
72+
end
73+
5474
describe "#redirect_back" do
5575
it "redirects to referer if present" do
5676
request = build_request("POST")
@@ -83,6 +103,23 @@ describe Lucky::Action do
83103
should_redirect(action, to: RedirectAction.path, status: 301)
84104
end
85105

106+
it "redirects back with the globally configured status code" do
107+
Lucky::Redirectable.temp_config(redirect_status: 303) do
108+
request = build_request("POST")
109+
action = RedirectAction.new(build_context(request), params)
110+
action.redirect_back fallback: "/fallback"
111+
should_redirect(action, to: "/fallback", status: 303)
112+
113+
action = RedirectAction.new(build_context, params)
114+
action.redirect_back fallback: RedirectAction.route
115+
should_redirect(action, to: RedirectAction.path, status: 303)
116+
117+
action = RedirectAction.new(build_context, params)
118+
action.redirect_back fallback: RedirectAction
119+
should_redirect(action, to: RedirectAction.path, status: 303)
120+
end
121+
end
122+
86123
it "redirects to fallback if referer is external" do
87124
request = build_request("POST")
88125
request.headers["Referer"] = "https://external.com/coming/from"

src/lucky/redirectable.cr

+55-11
Original file line numberDiff line numberDiff line change
@@ -14,22 +14,42 @@
1414
# ```
1515
# redirect to: Users::Index, status: 301
1616
#
17-
# # or use the built in enum value
18-
# redirect to: Users::Index, status: :moved_permanently
17+
# # or use the built-in enum value
18+
# redirect to: Users::Index, status: HTTP::Status::MOVED_PERMANENTLY
1919
# ```
2020
#
21-
# You can find a list of all of the possible statuses [here](https://crystal-lang.org/api/latest/HTTP/Status.html).
21+
# Alternatively, the status code can also be configured globally through the `redirect_status` setting:
22+
#
23+
# ```
24+
# Lucky::Redirectable.configure do |config|
25+
# config.redirect_status = 303
26+
#
27+
# # or using a built-in enum value
28+
# config.redirect_status = HTTP::Status::SEE_OTHER.value
29+
# end
30+
# ```
31+
#
32+
# You can find a list of all possible statuses [here](https://crystal-lang.org/api/latest/HTTP/Status.html).
2233
#
2334
# Internally, all the different methods in this module eventually use the
2435
# method that takes a `String`. However, it's recommended you pass a
2536
# `Lucky::Action` class if possible because it guarantees runtime safety.
2637
module Lucky::Redirectable
38+
Habitat.create do
39+
setting redirect_status : Int32 = HTTP::Status::FOUND.value
40+
end
41+
2742
# Redirect back with a `Lucky::Action` fallback
2843
#
2944
# ```
3045
# redirect_back fallback: Users::Index
3146
# ```
32-
def redirect_back(*, fallback : Lucky::Action.class, status = 302, allow_external = false) : Lucky::TextResponse
47+
def redirect_back(
48+
*,
49+
fallback : Lucky::Action.class,
50+
status = Lucky::Redirectable.settings.redirect_status,
51+
allow_external = false
52+
) : Lucky::TextResponse
3353
redirect_back fallback: fallback.route, status: status, allow_external: allow_external
3454
end
3555

@@ -38,7 +58,12 @@ module Lucky::Redirectable
3858
# ```
3959
# redirect_back fallback: Users::Show.with(user.id)
4060
# ```
41-
def redirect_back(*, fallback : Lucky::RouteHelper, status = 302, allow_external = false) : Lucky::TextResponse
61+
def redirect_back(
62+
*,
63+
fallback : Lucky::RouteHelper,
64+
status = Lucky::Redirectable.settings.redirect_status,
65+
allow_external = false
66+
) : Lucky::TextResponse
4267
redirect_back fallback: fallback.path, status: status, allow_external: allow_external
4368
end
4469

@@ -47,7 +72,12 @@ module Lucky::Redirectable
4772
# ```
4873
# redirect_back fallback: "/users", status: HTTP::Status::MOVED_PERMANENTLY
4974
# ```
50-
def redirect_back(*, fallback : String, status : HTTP::Status, allow_external = false) : Lucky::TextResponse
75+
def redirect_back(
76+
*,
77+
fallback : String,
78+
status : HTTP::Status,
79+
allow_external = false
80+
) : Lucky::TextResponse
5181
redirect_back fallback: fallback, status: status.value, allow_external: allow_external
5282
end
5383

@@ -74,7 +104,12 @@ module Lucky::Redirectable
74104
# They can be explicitly allowed if necessary
75105
#
76106
# redirect_back fallback: "/home", allow_external: true
77-
def redirect_back(*, fallback : String, status : Int32 = 302, allow_external : Bool = false) : Lucky::TextResponse
107+
def redirect_back(
108+
*,
109+
fallback : String,
110+
status : Int32 = Lucky::Redirectable.settings.redirect_status,
111+
allow_external : Bool = false
112+
) : Lucky::TextResponse
78113
referer = request.headers["Referer"]?
79114

80115
if referer && (allow_external || allowed_host?(referer))
@@ -89,7 +124,10 @@ module Lucky::Redirectable
89124
# ```
90125
# redirect to: Users::Show.with(user.id), status: 301
91126
# ```
92-
def redirect(to route : Lucky::RouteHelper, status = 302) : Lucky::TextResponse
127+
def redirect(
128+
to route : Lucky::RouteHelper,
129+
status = Lucky::Redirectable.settings.redirect_status
130+
) : Lucky::TextResponse
93131
redirect to: route.path, status: status
94132
end
95133

@@ -98,14 +136,17 @@ module Lucky::Redirectable
98136
# ```
99137
# redirect to: Users::Index
100138
# ```
101-
def redirect(to action : Lucky::Action.class, status = 302) : Lucky::TextResponse
139+
def redirect(
140+
to action : Lucky::Action.class,
141+
status = Lucky::Redirectable.settings.redirect_status
142+
) : Lucky::TextResponse
102143
redirect to: action.route, status: status
103144
end
104145

105146
# Redirect to the given path, with a human friendly status
106147
#
107148
# ```
108-
# redirect to: "/users", status: :moved_permanently
149+
# redirect to: "/users", status: HTTP::Status::MOVED_PERMANENTLY
109150
# ```
110151
def redirect(to path : String, status : HTTP::Status) : Lucky::TextResponse
111152
redirect(path, status.value)
@@ -118,7 +159,10 @@ module Lucky::Redirectable
118159
# redirect to: "/users/1", status: 301
119160
# ```
120161
# Note: It's recommended to use the method above that accepts a human friendly version of the status
121-
def redirect(to path : String, status : Int32 = 302) : Lucky::TextResponse
162+
def redirect(
163+
to path : String,
164+
status : Int32 = Lucky::Redirectable.settings.redirect_status
165+
) : Lucky::TextResponse
122166
# flash messages are not consumed here, so keep them for the next action
123167
flash.keep
124168
context.response.headers.add "Location", path

src/lucky/redirectable_turbolinks_support.cr

+4-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@
55
# but Lucky::ErrorAction not have pipe support
66
module Lucky::RedirectableTurbolinksSupport
77
# Overrides Lucky::Redirectable redirect's method
8-
def redirect(to path : String, status : Int32 = 302) : Lucky::TextResponse
8+
def redirect(
9+
to path : String,
10+
status : Int32 = Lucky::Redirectable.settings.redirect_status
11+
) : Lucky::TextResponse
912
# flash messages are not consumed here, so keep them for the next action
1013
flash.keep
1114
if ajax? && request.method != "GET"

0 commit comments

Comments
 (0)