line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Catalyst::Plugin::ForwardChained; |
2
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
=head1 NAME |
4
|
|
|
|
|
|
|
|
5
|
|
|
|
|
|
|
Catalyst::Plugin::ForwardChained - Forwarding to "Chain"-Actions in Catalyst |
6
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
=head1 DESCRIPTION |
8
|
|
|
|
|
|
|
|
9
|
|
|
|
|
|
|
Forwarding to the end point of a couple of chain methods .. |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
In most cases: dont use - better user redirect instead |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
This is a hackaround, not a clean solution. |
14
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
Experimental. |
16
|
|
|
|
|
|
|
|
17
|
|
|
|
|
|
|
=head1 SYNOPSIS |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
# In your application class |
20
|
|
|
|
|
|
|
use Catalyst qw/ ForwardChained /; |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
# ... somwhere else: |
23
|
|
|
|
|
|
|
$c->forward_to_chained( [ qw/ chained endpoint /, [ qw/ args / ] ); |
24
|
|
|
|
|
|
|
$c->forward_to_chained( 'chained/endpoint', [ qw/ args / ] ); |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
|
27
|
|
|
|
|
|
|
=head2 Example 1 |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
Having some controller: |
30
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
package MyApp::Controller::Test; |
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# .. |
34
|
|
|
|
|
|
|
# to be clear : |
35
|
|
|
|
|
|
|
__PACKAGE__->config->{ namespace } = 'test'; |
36
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
# url would be "/one/*" |
38
|
|
|
|
|
|
|
sub my_index : PathPart( 'one' ) : Chained( '/' ) : CaptureArgs( 1 ) { |
39
|
|
|
|
|
|
|
# do some.. |
40
|
|
|
|
|
|
|
} |
41
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
# url would be "/one/*/two/*" |
43
|
|
|
|
|
|
|
sub my_other : PathPart( 'two') : Chained( 'my_index' ) : Args( 1 ) { |
44
|
|
|
|
|
|
|
# do some.. |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
You would use: |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# somewhere |
50
|
|
|
|
|
|
|
# this would call: "/namespace/one/111/two/222" |
51
|
|
|
|
|
|
|
$c->forward_to_chained( [ qw/ namespace two / ], [ "111", "222 ] ); |
52
|
|
|
|
|
|
|
|
53
|
|
|
|
|
|
|
# same as above |
54
|
|
|
|
|
|
|
$c->forward_to_chained( "namespace/two", [ "111", "222 ] ); |
55
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
|
57
|
|
|
|
|
|
|
=head2 Example 2 |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
it's not always obvious which path to choose when calling "forward_to_chained" .. |
60
|
|
|
|
|
|
|
|
61
|
|
|
|
|
|
|
An example testing controller |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
package MyApp::Controller::Testing; |
64
|
|
|
|
|
|
|
|
65
|
|
|
|
|
|
|
use strict; |
66
|
|
|
|
|
|
|
use warnings; |
67
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
use base qw/ Catalyst::Controller /; |
69
|
|
|
|
|
|
|
use Data::Dumper; |
70
|
|
|
|
|
|
|
|
71
|
|
|
|
|
|
|
__PACKAGE__->config->{ namespace } = 'testing'; |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
sub one : PathPart( 'testing/one' ) : Chained( '/' ) : CaptureArgs( 1 ) { |
74
|
|
|
|
|
|
|
my ( $self, $c, @args ) = @_; |
75
|
|
|
|
|
|
|
push @{ $c->stash->{ called } ||= [] }, { |
76
|
|
|
|
|
|
|
name => 'one', |
77
|
|
|
|
|
|
|
args => \@args |
78
|
|
|
|
|
|
|
}; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
|
81
|
|
|
|
|
|
|
sub two : Chained( 'one' ) : CaptureArgs( 1 ) { |
82
|
|
|
|
|
|
|
my ( $self, $c, @args ) = @_; |
83
|
|
|
|
|
|
|
push @{ $c->stash->{ called } ||= [] }, { |
84
|
|
|
|
|
|
|
name => 'two', |
85
|
|
|
|
|
|
|
args => \@args |
86
|
|
|
|
|
|
|
}; |
87
|
|
|
|
|
|
|
} |
88
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
sub three : Chained( 'two' ) { |
90
|
|
|
|
|
|
|
my ( $self, $c, @args ) = @_; |
91
|
|
|
|
|
|
|
push @{ $c->stash->{ called } ||= [] }, { |
92
|
|
|
|
|
|
|
name => 'three', |
93
|
|
|
|
|
|
|
args => \@args |
94
|
|
|
|
|
|
|
}; |
95
|
|
|
|
|
|
|
} |
96
|
|
|
|
|
|
|
|
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
sub right : PathPart( 'testing/right' ) : Chained( '/' ) : CaptureArgs( 0 ) { |
99
|
|
|
|
|
|
|
my ( $self, $c, @args ) = @_; |
100
|
|
|
|
|
|
|
push @{ $c->stash->{ called } ||= [] }, { |
101
|
|
|
|
|
|
|
name => 'right', |
102
|
|
|
|
|
|
|
args => \@args |
103
|
|
|
|
|
|
|
}; |
104
|
|
|
|
|
|
|
} |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
sub again : Chained( 'right' ) : Args( 1 ) { |
107
|
|
|
|
|
|
|
my ( $self, $c, @args ) = @_; |
108
|
|
|
|
|
|
|
push @{ $c->stash->{ called } ||= [] }, { |
109
|
|
|
|
|
|
|
name => 'again', |
110
|
|
|
|
|
|
|
args => \@args |
111
|
|
|
|
|
|
|
}; |
112
|
|
|
|
|
|
|
} |
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
|
115
|
|
|
|
|
|
|
sub chainor : Local { |
116
|
|
|
|
|
|
|
my ( $self, $c ) = @_; |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
# calling chained: |
119
|
|
|
|
|
|
|
|
120
|
|
|
|
|
|
|
# 1) WRONG: |
121
|
|
|
|
|
|
|
#$c->forward_to_chained( 'testing/one/arg1/two/arg2/three/arg3' ); |
122
|
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
# 2) WRONG: |
124
|
|
|
|
|
|
|
#$c->forward_to_chained( 'testing/one/two/three', [ qw/ arg1 arg2 arg3 arg4 / ] ); |
125
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
# 3) CORRECT: |
127
|
|
|
|
|
|
|
$c->forward_to_chained( 'testing/three', [qw/ arg1 arg2 arg3 arg4 /] ); |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
$c->forward_to_chained( 'testing/again', [qw/ arg /] ); |
130
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
$c->res->content_type( 'text/plain' ); |
132
|
|
|
|
|
|
|
$c->res->body( "Called: \n". Dumper( $c->stash->{ called } ) ); |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
1; |
136
|
|
|
|
|
|
|
|
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
would produce something like this: |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
Called: |
141
|
|
|
|
|
|
|
$VAR1 = [ |
142
|
|
|
|
|
|
|
{ |
143
|
|
|
|
|
|
|
'args' => [ |
144
|
|
|
|
|
|
|
'arg1' |
145
|
|
|
|
|
|
|
], |
146
|
|
|
|
|
|
|
'name' => 'one' |
147
|
|
|
|
|
|
|
}, |
148
|
|
|
|
|
|
|
{ |
149
|
|
|
|
|
|
|
'args' => [ |
150
|
|
|
|
|
|
|
'arg2' |
151
|
|
|
|
|
|
|
], |
152
|
|
|
|
|
|
|
'name' => 'two' |
153
|
|
|
|
|
|
|
}, |
154
|
|
|
|
|
|
|
{ |
155
|
|
|
|
|
|
|
'args' => [ |
156
|
|
|
|
|
|
|
'arg3', |
157
|
|
|
|
|
|
|
'arg4' |
158
|
|
|
|
|
|
|
], |
159
|
|
|
|
|
|
|
'name' => 'three' |
160
|
|
|
|
|
|
|
}, |
161
|
|
|
|
|
|
|
{ |
162
|
|
|
|
|
|
|
'args' => [], |
163
|
|
|
|
|
|
|
'name' => 'right' |
164
|
|
|
|
|
|
|
}, |
165
|
|
|
|
|
|
|
{ |
166
|
|
|
|
|
|
|
'args' => [ |
167
|
|
|
|
|
|
|
'arg' |
168
|
|
|
|
|
|
|
], |
169
|
|
|
|
|
|
|
'name' => 'again' |
170
|
|
|
|
|
|
|
} |
171
|
|
|
|
|
|
|
]; |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
and catalyst debug out: |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
.----------------------------------------------------------------+-----------. |
177
|
|
|
|
|
|
|
| Action | Time | |
178
|
|
|
|
|
|
|
+----------------------------------------------------------------+-----------+ |
179
|
|
|
|
|
|
|
| /begin | 0.064814s | |
180
|
|
|
|
|
|
|
| /testing/chainor | 0.002931s | |
181
|
|
|
|
|
|
|
| /testing/one | 0.000588s | |
182
|
|
|
|
|
|
|
| /testing/two | 0.000208s | |
183
|
|
|
|
|
|
|
| /testing/three | 0.000197s | |
184
|
|
|
|
|
|
|
| /testing/right | 0.000061s | |
185
|
|
|
|
|
|
|
| /testing/again | 0.000055s | |
186
|
|
|
|
|
|
|
| /end | 0.000495s | |
187
|
|
|
|
|
|
|
'----------------------------------------------------------------+-----------' |
188
|
|
|
|
|
|
|
|
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
=head1 METHODS |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
=cut |
193
|
|
|
|
|
|
|
|
194
|
1
|
|
|
1
|
|
1074
|
use strict; |
|
1
|
|
|
|
|
3
|
|
|
1
|
|
|
|
|
48
|
|
195
|
1
|
|
|
1
|
|
6
|
use warnings; |
|
1
|
|
|
|
|
4
|
|
|
1
|
|
|
|
|
41
|
|
196
|
|
|
|
|
|
|
|
197
|
1
|
|
|
1
|
|
19
|
use vars qw/ $VERSION /; |
|
1
|
|
|
|
|
2
|
|
|
1
|
|
|
|
|
61
|
|
198
|
1
|
|
|
1
|
|
564
|
use Catalyst::Exception; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
$VERSION = '0.03'; |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
=head2 forward_to_chained |
204
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
forwards to a certain chained action endpoint .. |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
$c->forward_to_chained( "some/path", [ qw/ arg1 arg2 arg3 / ] ); |
208
|
|
|
|
|
|
|
$c->forward_to_chained( [qw/ some path /], [ qw/ arg1 arg2 arg3 / ] ); |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
=cut |
211
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
sub forward_to_chained { |
213
|
|
|
|
|
|
|
my ( $c, $chained_ref, $args_ref ) = @_; |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
# transform from string to array-ref .. and back to clear things |
217
|
|
|
|
|
|
|
$chained_ref = [ grep { length } split( /\//, $chained_ref ) ] |
218
|
|
|
|
|
|
|
unless ref( $chained_ref ); |
219
|
|
|
|
|
|
|
my $search_chain = join( "/", @{ $chained_ref } ); |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# search chain parts in action hash .. |
222
|
|
|
|
|
|
|
my $actions_ref = $c->dispatcher->action_hash; |
223
|
|
|
|
|
|
|
my ( @chain, %seen ) = (); |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
# while defined the action path in the action ref... cycle through url |
226
|
|
|
|
|
|
|
SEARCH_CHAIN: |
227
|
|
|
|
|
|
|
while ( defined( my $action_ref = $actions_ref->{ $search_chain } ) && !$seen{ $search_chain }++ ) { |
228
|
|
|
|
|
|
|
|
229
|
|
|
|
|
|
|
# building our chain.. |
230
|
|
|
|
|
|
|
unshift @chain, $action_ref; |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
# found next part ... |
233
|
|
|
|
|
|
|
if ( defined $action_ref->{ attributes }->{ Chained } ) { |
234
|
|
|
|
|
|
|
$search_chain = $action_ref->{ attributes }->{ Chained }->[ -1 ]; # current part of "url" |
235
|
|
|
|
|
|
|
$search_chain =~ s~^\/+~~; # remove any leading "/" |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
# not further parts |
239
|
|
|
|
|
|
|
else { |
240
|
|
|
|
|
|
|
last SEARCH_CHAIN; |
241
|
|
|
|
|
|
|
} |
242
|
|
|
|
|
|
|
} |
243
|
|
|
|
|
|
|
|
244
|
|
|
|
|
|
|
# no chain found: bye bye |
245
|
|
|
|
|
|
|
Catalyst::Exception->throw( |
246
|
|
|
|
|
|
|
message => "Cant forward to chained action because cant find chain for '$search_chain'" ) |
247
|
|
|
|
|
|
|
if ( scalar @chain == 0 ); |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
# going to build up / setup new action.. and dispatch to this action |
251
|
|
|
|
|
|
|
|
252
|
|
|
|
|
|
|
# save orig captures .. |
253
|
|
|
|
|
|
|
my $captures_ref = $c->req->captures; |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
# .. setup new captures .. |
256
|
|
|
|
|
|
|
$args_ref ||= []; |
257
|
|
|
|
|
|
|
$args_ref = [ $args_ref ] unless ref( $args_ref ); |
258
|
|
|
|
|
|
|
$c->req->captures( $args_ref ); |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
# .. build up action chain and settle to catalyst .. |
261
|
|
|
|
|
|
|
my $action_chain = __Catalyst_ActionChain->from_chain( \@chain ); |
262
|
|
|
|
|
|
|
#$c->action( Catalyst::ActionChain->from_chain( \@chain ) ); |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
# .. dispatch to it .. |
265
|
|
|
|
|
|
|
$action_chain->dispatch( $c ); |
266
|
|
|
|
|
|
|
#$c->dispatcher->dispatch( $c ); |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
# .. and set orig captures back |
269
|
|
|
|
|
|
|
$c->req->captures( $captures_ref ); |
270
|
|
|
|
|
|
|
|
271
|
|
|
|
|
|
|
return ; |
272
|
|
|
|
|
|
|
} |
273
|
|
|
|
|
|
|
|
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
|
276
|
|
|
|
|
|
|
=head2 get_chained_action_endpoints |
277
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
returns array or arrayref of endpoints.. to help you find the one you need |
279
|
|
|
|
|
|
|
|
280
|
|
|
|
|
|
|
my @endpoints = $c->get_chained_action_endpoints; |
281
|
|
|
|
|
|
|
my $endpoints_ref = $c->get_chained_action_endpoints; |
282
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
=cut |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
sub get_chained_action_endpoints { |
286
|
|
|
|
|
|
|
my ( $c ) = @_; |
287
|
|
|
|
|
|
|
|
288
|
|
|
|
|
|
|
my $actions_ref = $c->dispatcher->action_hash; |
289
|
|
|
|
|
|
|
my @endpoints = |
290
|
|
|
|
|
|
|
sort |
291
|
|
|
|
|
|
|
grep { |
292
|
|
|
|
|
|
|
defined $actions_ref->{ $_ }->{ attributes } && |
293
|
|
|
|
|
|
|
ref $actions_ref->{ $_ }->{ attributes }->{ Chained } |
294
|
|
|
|
|
|
|
} |
295
|
|
|
|
|
|
|
grep { ! /(?:^|\/)_[A-Z]+$/ } keys %{ $actions_ref } |
296
|
|
|
|
|
|
|
; |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
return wantarray ? @endpoints : \@endpoints; |
299
|
|
|
|
|
|
|
} |
300
|
|
|
|
|
|
|
|
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
|
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
=head1 AUTHOR |
308
|
|
|
|
|
|
|
|
309
|
|
|
|
|
|
|
Ulrich Kautz, uk@fortrabbit.de |
310
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
=cut |
312
|
|
|
|
|
|
|
|
313
|
|
|
|
|
|
|
|
314
|
|
|
|
|
|
|
1; |
315
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
# |
317
|
|
|
|
|
|
|
# we require some small changes on the Catalyst::ActionChain::dispatch-method |
318
|
|
|
|
|
|
|
# to provide the request-arguments to the last chain-action .. |
319
|
|
|
|
|
|
|
# |
320
|
|
|
|
|
|
|
|
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
package __Catalyst_ActionChain; |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
use strict; |
325
|
|
|
|
|
|
|
use base qw/ Catalyst::ActionChain /; |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
sub dispatch { |
328
|
|
|
|
|
|
|
my ( $self, $c ) = @_; |
329
|
|
|
|
|
|
|
my @captures = @{$c->req->captures||[]}; |
330
|
|
|
|
|
|
|
my @chain = @{ $self->chain }; |
331
|
|
|
|
|
|
|
my $last = pop(@chain); |
332
|
|
|
|
|
|
|
foreach my $action ( @chain ) { |
333
|
|
|
|
|
|
|
my @args; |
334
|
|
|
|
|
|
|
if (my $cap = $action->attributes->{CaptureArgs}) { |
335
|
|
|
|
|
|
|
@args = splice(@captures, 0, $cap->[0]); |
336
|
|
|
|
|
|
|
} |
337
|
|
|
|
|
|
|
local $c->request->{arguments} = \@args; |
338
|
|
|
|
|
|
|
$action->dispatch( $c ); |
339
|
|
|
|
|
|
|
} |
340
|
|
|
|
|
|
|
|
341
|
|
|
|
|
|
|
# --- START CHANGES ---- |
342
|
|
|
|
|
|
|
my @args; |
343
|
|
|
|
|
|
|
if ( my $cap = $last->attributes->{Args} ) { |
344
|
|
|
|
|
|
|
@args = $#$cap > -1 |
345
|
|
|
|
|
|
|
? splice(@captures, 0, $cap->[0]) |
346
|
|
|
|
|
|
|
: @captures |
347
|
|
|
|
|
|
|
; |
348
|
|
|
|
|
|
|
} |
349
|
|
|
|
|
|
|
local $c->request->{arguments} = \@args; |
350
|
|
|
|
|
|
|
# --- END CHANGES ---- |
351
|
|
|
|
|
|
|
|
352
|
|
|
|
|
|
|
$last->dispatch( $c ); |
353
|
|
|
|
|
|
|
} |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
1; |