File Coverage

blib/lib/Catalyst/Plugin/RedirectTo.pm
Criterion Covered Total %
statement 28 36 77.7
branch 9 22 40.9
condition n/a
subroutine 5 5 100.0
pod 2 2 100.0
total 44 65 67.6


line stmt bran cond sub pod time code
1             package Catalyst::Plugin::RedirectTo;
2              
3 1     1   3552 use Moose::Role;
  1         3  
  1         10  
4 1     1   5990 use Carp;
  1         2  
  1         686  
5              
6             requires 'response', 'uri_for', 'uri_for_action';
7              
8             our $VERSION = '0.004';
9             our $DEFAULT_REDIRECT_STATUS = 303;
10              
11 3     3   10 sub _default_redirect_status { return $DEFAULT_REDIRECT_STATUS }
12              
13             my $normalize_status = sub {
14             my $c = shift;
15             my @args = @_;
16             my $code = ref($args[-1]) eq 'SCALAR' ?
17             ${ pop @args } : $c->_default_redirect_status;
18              
19             die "$code is not a redirect HTTP status" unless $code =~m/^3\d\d$/;
20              
21             return ($code, @args);
22             };
23              
24             sub redirect_to {
25 4     4 1 516000 my $c = shift;
26 4         21 my ($code, @args) = $normalize_status->($c, @_);
27 4         24 my $url = $c->uri_for(@args);
28 4 50       3883 carp "Could not create a URI from the given arguments" unless $url;
29 4         138 $c->response->redirect($url, $code);
30             }
31              
32             sub redirect_to_action {
33 1     1 1 24450 my $c = shift;
34 1         8 my ($code, $action_proto, @args) = $normalize_status->($c, @_);
35              
36             # If its already an action object just use it.
37 1 50       6 if(Scalar::Util::blessed($action_proto)) {
38 0         0 my $url = $c->uri_for($action_proto, @args);
39 0 0       0 carp "Could not create a URI from '$action_proto' with the given arguments" unless $url;
40 0         0 $c->response->redirect($url, $code);
41 0         0 return $url;
42             }
43              
44 1         4 my $url;
45 1 50       15 if($c->can('uri')) {
46 0         0 $url = $c->uri($action_proto, @args);
47             } else {
48 1         34 my $controller = $c->controller;
49 1         115 my $action;
50 1 50       6 if($action_proto =~/\//) {
51 1 50       6 my $path = $action_proto=~m/^\// ? $action_proto : $controller->action_for($action_proto)->private_path;
52 1 50       4 carp "$action_proto is not an action for controller ${\$controller->catalyst_component_name}" unless $path;
  0         0  
53 1 50       5 carp "$path is not a private path" unless $action = $c->dispatcher->get_action_by_path($path);
54             } else {
55 0 0       0 carp "$action_proto is not an action for controller ${\$controller->catalyst_component_name}"
  0         0  
56             unless $action = $controller->action_for($action_proto);
57             }
58 1 50       103 carp "Could not create a URI from '$action_proto' with the given arguments" unless $action;
59 1         14 $url = $c->uri_for($action, @args);
60 1 50       879 carp "Could not create a URI from '$action_proto' with the given arguments" unless $url;
61             }
62 1         34 $c->response->redirect($url, $code);
63 1         240 return $url;
64             }
65              
66             1;
67              
68             =head1 NAME
69              
70             Catalyst::Plugin::RedirectTo - Easier redirects to action objects or private paths.
71              
72             =head1 SYNOPSIS
73              
74             Use the plugin in your application class:
75              
76             package MyApp;
77             use Catalyst 'RedirectTo';
78              
79             MyApp->setup;
80              
81             Then you can use it in your controllers:
82              
83             package MyApp::Controller::Example;
84              
85             use base 'Catalyst::Controller';
86              
87             sub does_redirect_to :Local {
88             my ($self, $c) = @_;
89             $c->redirect_to( $self->action_for('target'), [100] );
90             }
91              
92             sub does_redirect_to_action :Local {
93             my ($self, $c) = @_;
94             $c->redirect_to_action( 'target', [100] );
95             }
96              
97             sub target :Local Args(1) {
98             my ($self, $c, $id) = @_;
99             $c->response->content_type('text/plain');
100             $c->response->body("This is the target action for $id");
101             }
102              
103             =head1 DESCRIPTION
104              
105             Currently if you want to setup a redirect in L<Catalyst> to an existing action the
106             proper form is somewhat verbose:
107              
108             $c->response->redirect(
109             $c->uri_for(
110             $c->controller(...)->action_for(...), \@args, \%q
111             )
112             );
113              
114             Which is verbose enough that i probably encourages people to do the wrong thing
115             and use a hard coded link path in the redirect request. This might later bite
116             you if you need to change your controllers and URL hierarchy.
117              
118             Also, for historical reasons the default redirect code is 302, which is considered
119             a temporary redirect, rather than 303 which is a better default for the common use
120             case of a form POST that generates a new resource. Which means to do the right
121             thing you really need:
122              
123             $c->response->redirect(
124             $c->uri_for(
125             $c->controller(...)->action_for(...), \@args, \%q
126             ),
127             303
128             );
129              
130             This plugin seeks to relieve some of the effort involved in doing the right thing.
131             It does this by creating a context method which encapulates the redirect response
132             setup (and sets a 303 by default, since that is the common case today) with a call
133             to 'uri_for' (or 'uri_for_action'). So instead of the above you can just do:
134              
135             $c->redirect_to($c->controller(...)->action_for(...), \@args, \%q);
136              
137             or even:
138              
139             $c->redirect_to_action('controller/action', \@args, \%q);
140              
141             Which hopefully is a good encapsulation of 'the right thing to do'!
142              
143             B<NOTE:> Please be aware that setting up a redirect does not automatically detach or
144             complete the action. You still should either return the redirect or call 'detach'
145             if you want to stop action processing.
146              
147             =head1 METHODS
148              
149             This plugin adds the following methods to your context
150              
151             =head2 redirect_to ($action_obj, \@args, \%query, \$code)
152              
153             Example:
154              
155             $c->redirect_to( $action_obj, \@args, \%query, \$code);
156              
157             Is shorthand for:
158              
159             $c->response->redirect(
160             $c->uri_for( $action_obj, \@args, \%query), $code);
161              
162             $code will default to 303 (not 302 as does $c->res->redirect) as this is commonly
163             supported in modern browsers, so unless you have a specific need for an alternative
164             response code, you should be able to just leave it off.
165              
166             For example:
167              
168             $c->redirect_to($self->action_for($action_name), \@args);
169              
170             Basically all the arguments to this method will be sent to ->uri_for, unless the
171             last argument is a scalar ref, in which case it will be used to set the HTTP status
172             code. $code must be '3xx' (a valid current or future redirect status).
173              
174             Does not detach or return the current action (just like the existing method)!
175              
176             B<NOTE:> Please notice that if you want to set a status code other than 303, that
177             code must be added to the argument list as a scalar ref. This is needed to
178             distinguish from an argument that gets passed to 'uri_for'.
179              
180             =head2 redirect_to_action
181              
182             Same as 'redirect_to' but submits the arguments to 'uri_for_action' instead. Please
183             B<NOTE> that if you also install L<Catalyst::Plugin::URI> we will use that for
184             action resolution (supports named Actions).
185              
186             =head1 AUTHOR
187              
188             John Napiorkowski L<email:jjnapiork@cpan.org>
189            
190             =head1 SEE ALSO
191            
192             L<Catalyst>, L<Catalyst::Response>
193              
194             =head1 COPYRIGHT & LICENSE
195            
196             Copyright 2018, John Napiorkowski L<email:jjnapiork@cpan.org>
197            
198             This library is free software; you can redistribute it and/or modify it under
199             the same terms as Perl itself.
200            
201             =cut