debugging output
[catagits/Catalyst-Runtime.git] / lib / Catalyst / DispatchType / Chained.pm
CommitLineData
5882c86e 1package Catalyst::DispatchType::Chained;
141459fa 2
3c0186f2 3use Moose;
e8b9f2a9 4extends 'Catalyst::DispatchType';
5
141459fa 6use Text::SimpleTable;
7use Catalyst::ActionChain;
39fc2ce1 8use Catalyst::Utils;
141459fa 9use URI;
dd97c1ac 10use Scalar::Util ();
141459fa 11
be5cb4e4 12has _endpoints => (
13 is => 'rw',
14 isa => 'ArrayRef',
15 required => 1,
16 default => sub{ [] },
17 );
18
19has _actions => (
20 is => 'rw',
21 isa => 'HashRef',
22 required => 1,
23 default => sub{ {} },
24 );
25
26has _children_of => (
27 is => 'rw',
28 isa => 'HashRef',
29 required => 1,
30 default => sub{ {} },
31 );
32
0fc2d522 33no Moose;
34
792b40ac 35# please don't perltidy this. hairy code within.
36
141459fa 37=head1 NAME
38
5882c86e 39Catalyst::DispatchType::Chained - Path Part DispatchType
141459fa 40
41=head1 SYNOPSIS
42
26dc649a 43Path part matching, allowing several actions to sequentially take care of processing a request:
44
05a90578 45 # root action - captures one argument after it
46 sub foo_setup : Chained('/') PathPart('foo') CaptureArgs(1) {
47 my ( $self, $c, $foo_arg ) = @_;
48 ...
49 }
50
51 # child action endpoint - takes one argument
52 sub bar : Chained('foo_setup') Args(1) {
53 my ( $self, $c, $bar_arg ) = @_;
54 ...
55 }
141459fa 56
57=head1 DESCRIPTION
58
26dc649a 59Dispatch type managing default behaviour. For more information on
60dispatch types, see:
61
62=over 4
63
b9b89145 64=item * L<Catalyst::Manual::Intro> for how they affect application authors
26dc649a 65
66=item * L<Catalyst::DispatchType> for implementation information.
67
68=back
05a90578 69
141459fa 70=head1 METHODS
71
72=head2 $self->list($c)
73
74Debug output for Path Part dispatch points
75
141459fa 76=cut
77
792b40ac 78sub list {
79 my ( $self, $c ) = @_;
80
be5cb4e4 81 return unless $self->_endpoints;
792b40ac 82
48d435ba 83 my $avail_width = Catalyst::Utils::term_width() - 9;
84 my $col1_width = ($avail_width * .50) < 35 ? 35 : int($avail_width * .50);
85 my $col2_width = $avail_width - $col1_width;
792b40ac 86 my $paths = Text::SimpleTable->new(
48d435ba 87 [ $col1_width, 'Path Spec' ], [ $col2_width, 'Private' ],
39fc2ce1 88 );
792b40ac 89
007a7ca0 90 my $has_unattached_actions;
91 my $unattached_actions = Text::SimpleTable->new(
48d435ba 92 [ $col1_width, 'Private' ], [ $col2_width, 'Missing parent' ],
007a7ca0 93 );
94
792b40ac 95 ENDPOINT: foreach my $endpoint (
96 sort { $a->reverse cmp $b->reverse }
be5cb4e4 97 @{ $self->_endpoints }
792b40ac 98 ) {
ffca3e96 99 my $args = $endpoint->list_extra_info->{Args};
792b40ac 100 my @parts = (defined($args) ? (("*") x $args) : '...');
d34667c3 101 my @parents = ();
792b40ac 102 my $parent = "DUMMY";
ba3f8a81 103 my $extra = $self->_list_extra_http_methods($endpoint);
e72a3cd6 104 my $consumes = $self->_list_extra_consumes($endpoint);
792b40ac 105 my $curr = $endpoint;
106 while ($curr) {
ffca3e96 107 if (my $cap = $curr->list_extra_info->{CaptureArgs}) {
108 unshift(@parts, (("*") x $cap));
792b40ac 109 }
77771155 110 if (my $pp = $curr->attributes->{PathPart}) {
792b40ac 111 unshift(@parts, $pp->[0])
112 if (defined $pp->[0] && length $pp->[0]);
113 }
5882c86e 114 $parent = $curr->attributes->{Chained}->[0];
be5cb4e4 115 $curr = $self->_actions->{$parent};
d34667c3 116 unshift(@parents, $curr) if $curr;
792b40ac 117 }
007a7ca0 118 if ($parent ne '/') {
119 $has_unattached_actions = 1;
59d5a638 120 $unattached_actions->row('/' . ($parents[0] || $endpoint)->reverse, $parent);
007a7ca0 121 next ENDPOINT;
122 }
d34667c3 123 my @rows;
124 foreach my $p (@parents) {
125 my $name = "/${p}";
ba3f8a81 126
127 if (defined(my $extra = $self->_list_extra_http_methods($p))) {
128 $name = "${extra} ${name}";
129 }
ffca3e96 130 if (defined(my $cap = $p->list_extra_info->{CaptureArgs})) {
131 $name .= ' ('.$cap.')';
d34667c3 132 }
e72a3cd6 133 if (defined(my $ct = $p->list_extra_info->{Consumes})) {
134 $name .= ' :'.$ct;
135 }
136
d34667c3 137 unless ($p eq $parents[0]) {
138 $name = "-> ${name}";
139 }
140 push(@rows, [ '', $name ]);
141 }
e72a3cd6 142 push(@rows, [ '', (@rows ? "=> " : '').($extra ? "$extra " : '')."/${endpoint}". ($consumes ? " :$consumes":"" ) ]);
f4624073 143 $rows[0][0] = join('/', '', @parts) || '/';
d34667c3 144 $paths->row(@$_) for @rows;
792b40ac 145 }
146
1cf0345b 147 $c->log->debug( "Loaded Chained actions:\n" . $paths->draw . "\n" );
007a7ca0 148 $c->log->debug( "Unattached Chained actions:\n", $unattached_actions->draw . "\n" )
149 if $has_unattached_actions;
792b40ac 150}
141459fa 151
ba3f8a81 152sub _list_extra_http_methods {
153 my ( $self, $action ) = @_;
154 return unless defined $action->list_extra_info->{HTTP_METHODS};
155 return join(', ', @{$action->list_extra_info->{HTTP_METHODS}});
e72a3cd6 156
ba3f8a81 157}
158
e72a3cd6 159sub _list_extra_consumes {
160 my ( $self, $action ) = @_;
161 return unless defined $action->list_extra_info->{CONSUMES};
162 return join(', ', @{$action->list_extra_info->{CONSUMES}});
163}
164
165
141459fa 166=head2 $self->match( $c, $path )
167
05a90578 168Calls C<recurse_match> to see if a chain matches the C<$path>.
141459fa 169
170=cut
171
172sub match {
173 my ( $self, $c, $path ) = @_;
174
e5ecd5bc 175 my $request = $c->request;
176 return 0 if @{$request->args};
141459fa 177
178 my @parts = split('/', $path);
179
6365b527 180 my ($chain, $captures, $parts) = $self->recurse_match($c, '/', \@parts);
634780e0 181
182 if ($parts && @$parts) {
183 for my $arg (@$parts) {
184 $arg =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
185 push @{$request->args}, $arg;
186 }
187 }
141459fa 188
189 return 0 unless $chain;
190
191 my $action = Catalyst::ActionChain->from_chain($chain);
192
e5ecd5bc 193 $request->action("/${action}");
194 $request->match("/${action}");
195 $request->captures($captures);
141459fa 196 $c->action($action);
197 $c->namespace( $action->namespace );
198
199 return 1;
200}
201
202=head2 $self->recurse_match( $c, $parent, \@path_parts )
203
05a90578 204Recursive search for a matching chain.
141459fa 205
206=cut
207
208sub recurse_match {
209 my ( $self, $c, $parent, $path_parts ) = @_;
be5cb4e4 210 my $children = $self->_children_of->{$parent};
141459fa 211 return () unless $children;
6b495723 212 my $best_action;
141459fa 213 my @captures;
1b04b972 214 TRY: foreach my $try_part (sort { length($b) <=> length($a) }
cdc97b63 215 keys %$children) {
1b04b972 216 # $b then $a to try longest part first
141459fa 217 my @parts = @$path_parts;
218 if (length $try_part) { # test and strip PathPart
219 next TRY unless
220 ($try_part eq join('/', # assemble equal number of parts
221 splice( # and strip them off @parts as well
792b40ac 222 @parts, 0, scalar(@{[split('/', $try_part)]})
223 ))); # @{[]} to avoid split to @_
141459fa 224 }
225 my @try_actions = @{$children->{$try_part}};
226 TRY_ACTION: foreach my $action (@try_actions) {
1c34f703 227 if (my $capture_attr = $action->attributes->{CaptureArgs}) {
3f91cb25 228 my $capture_count = $capture_attr->[0] || 0;
f505df49 229
230 # Short-circuit if not enough remaining parts
3f91cb25 231 next TRY_ACTION unless @parts >= $capture_count;
f505df49 232
141459fa 233 my @captures;
234 my @parts = @parts; # localise
7a7ac23c 235
1c34f703 236 # strip CaptureArgs into list
3f91cb25 237 push(@captures, splice(@parts, 0, $capture_count));
7a7ac23c 238
1279064a 239 # check if the action may fit, depending on a given test by the app
240 if ($action->can('match_captures')) { next TRY_ACTION unless $action->match_captures($c, \@captures) }
241
141459fa 242 # try the remaining parts against children of this action
0c78acb6 243 my ($actions, $captures, $action_parts, $n_pathparts) = $self->recurse_match(
141459fa 244 $c, '/'.$action->reverse, \@parts
245 );
2f381252 246 # No best action currently
247 # OR The action has less parts
248 # OR The action has equal parts but less captured data (ergo more defined)
249 if ($actions &&
250 (!$best_action ||
f4dbb497 251 $#$action_parts < $#{$best_action->{parts}} ||
2f381252 252 ($#$action_parts == $#{$best_action->{parts}} &&
0c78acb6 253 $#$captures < $#{$best_action->{captures}} &&
254 $n_pathparts > $best_action->{n_pathparts}))) {
255 my @pathparts = split /\//, $action->attributes->{PathPart}->[0];
6b495723 256 $best_action = {
257 actions => [ $action, @$actions ],
258 captures=> [ @captures, @$captures ],
0c78acb6 259 parts => $action_parts,
260 n_pathparts => scalar(@pathparts) + $n_pathparts,
261 };
6b495723 262 }
263 }
264 else {
7a7ac23c 265 {
266 local $c->req->{arguments} = [ @{$c->req->args}, @parts ];
267 next TRY_ACTION unless $action->match($c);
268 }
5c6d829d 269 my $args_attr = $action->attributes->{Args}->[0];
0c78acb6 270 my @pathparts = split /\//, $action->attributes->{PathPart}->[0];
953c176d 271 # No best action currently
272 # OR This one matches with fewer parts left than the current best action,
273 # And therefore is a better match
b0ad47c1 274 # OR No parts and this expects 0
953c176d 275 # The current best action might also be Args(0),
276 # but we couldn't chose between then anyway so we'll take the last seen
277
278 if (!$best_action ||
279 @parts < @{$best_action->{parts}} ||
a53e36d1 280 (!@parts && defined($args_attr) && $args_attr eq "0")){
6b495723 281 $best_action = {
282 actions => [ $action ],
283 captures=> [],
0c78acb6 284 parts => \@parts,
285 n_pathparts => scalar(@pathparts),
286 };
953c176d 287 }
141459fa 288 }
289 }
290 }
0c78acb6 291 return @$best_action{qw/actions captures parts n_pathparts/} if $best_action;
141459fa 292 return ();
293}
294
295=head2 $self->register( $c, $action )
296
05a90578 297Calls register_path for every Path attribute for the given $action.
141459fa 298
299=cut
300
2e29aec2 301sub _check_args_attr {
302 my ( $self, $action, $name ) = @_;
303
304 return unless exists $action->attributes->{$name};
305
306 if (@{$action->attributes->{$name}} > 1) {
307 Catalyst::Exception->throw(
308 "Multiple $name attributes not supported registering " . $action->reverse()
309 );
310 }
311 my $args = $action->attributes->{$name}->[0];
312 if (defined($args) and not (
313 Scalar::Util::looks_like_number($args) and
2782bef2 314 int($args) == $args and $args >= 0
2e29aec2 315 )) {
316 require Data::Dumper;
317 local $Data::Dumper::Terse = 1;
318 local $Data::Dumper::Indent = 0;
319 $args = Data::Dumper::Dumper($args);
320 Catalyst::Exception->throw(
321 "Invalid $name($args) for action " . $action->reverse() .
322 " (use '$name' or '$name(<number>)')"
323 );
324 }
325}
326
141459fa 327sub register {
328 my ( $self, $c, $action ) = @_;
329
1dc8af44 330 my @chained_attr = @{ $action->attributes->{Chained} || [] };
141459fa 331
1dc8af44 332 return 0 unless @chained_attr;
141459fa 333
2f381252 334 if (@chained_attr > 1) {
141459fa 335 Catalyst::Exception->throw(
5882c86e 336 "Multiple Chained attributes not supported registering ${action}"
141459fa 337 );
338 }
13c6b4cc 339 my $chained_to = $chained_attr[0];
141459fa 340
13c6b4cc 341 Catalyst::Exception->throw(
342 "Actions cannot chain to themselves registering /${action}"
343 ) if ($chained_to eq '/' . $action);
344
345 my $children = ($self->_children_of->{ $chained_to } ||= {});
141459fa 346
347 my @path_part = @{ $action->attributes->{PathPart} || [] };
348
09461385 349 my $part = $action->name;
141459fa 350
09461385 351 if (@path_part == 1 && defined $path_part[0]) {
352 $part = $path_part[0];
141459fa 353 } elsif (@path_part > 1) {
354 Catalyst::Exception->throw(
f3414019 355 "Multiple PathPart attributes not supported registering " . $action->reverse()
141459fa 356 );
357 }
358
8a6a6581 359 if ($part =~ m(^/)) {
360 Catalyst::Exception->throw(
f3414019 361 "Absolute parameters to PathPart not allowed registering " . $action->reverse()
8a6a6581 362 );
363 }
364
77771155 365 $action->attributes->{PathPart} = [ $part ];
792b40ac 366
141459fa 367 unshift(@{ $children->{$part} ||= [] }, $action);
368
be5cb4e4 369 $self->_actions->{'/'.$action->reverse} = $action;
792b40ac 370
2e29aec2 371 foreach my $name (qw(Args CaptureArgs)) {
372 $self->_check_args_attr($action, $name);
373 }
374
375 if (exists $action->attributes->{Args} and exists $action->attributes->{CaptureArgs}) {
376 Catalyst::Exception->throw(
bc71493a 377 "Combining Args and CaptureArgs attributes not supported registering " .
378 $action->reverse()
2e29aec2 379 );
dd97c1ac 380 }
381
1c34f703 382 unless ($action->attributes->{CaptureArgs}) {
be5cb4e4 383 unshift(@{ $self->_endpoints }, $action);
792b40ac 384 }
385
386 return 1;
141459fa 387}
388
389=head2 $self->uri_for_action($action, $captures)
390
05a90578 391Get the URI part for the action, using C<$captures> to fill
392the capturing parts.
141459fa 393
394=cut
395
396sub uri_for_action {
397 my ( $self, $action, $captures ) = @_;
398
5882c86e 399 return undef unless ($action->attributes->{Chained}
8b13f357 400 && !$action->attributes->{CaptureArgs});
792b40ac 401
402 my @parts = ();
403 my @captures = @$captures;
404 my $parent = "DUMMY";
405 my $curr = $action;
406 while ($curr) {
1c34f703 407 if (my $cap = $curr->attributes->{CaptureArgs}) {
84ac8ab9 408 return undef unless @captures >= ($cap->[0]||0); # not enough captures
8b13f357 409 if ($cap->[0]) {
f9155483 410 unshift(@parts, splice(@captures, -$cap->[0]));
8b13f357 411 }
792b40ac 412 }
77771155 413 if (my $pp = $curr->attributes->{PathPart}) {
792b40ac 414 unshift(@parts, $pp->[0])
8b13f357 415 if (defined($pp->[0]) && length($pp->[0]));
792b40ac 416 }
5882c86e 417 $parent = $curr->attributes->{Chained}->[0];
be5cb4e4 418 $curr = $self->_actions->{$parent};
141459fa 419 }
792b40ac 420
421 return undef unless $parent eq '/'; # fail for dangling action
422
423 return undef if @captures; # fail for too many captures
424
425 return join('/', '', @parts);
59d5a638 426
141459fa 427}
428
ae0e35ee 429=head2 $c->expand_action($action)
430
b0ad47c1 431Return a list of actions that represents a chained action. See
ae0e35ee 432L<Catalyst::Dispatcher> for more info. You probably want to
433use the expand_action it provides rather than this directly.
434
435=cut
436
52f71256 437sub expand_action {
438 my ($self, $action) = @_;
439
440 return unless $action->attributes && $action->attributes->{Chained};
441
442 my @chain;
443 my $curr = $action;
444
445 while ($curr) {
446 push @chain, $curr;
447 my $parent = $curr->attributes->{Chained}->[0];
448 $curr = $self->_actions->{$parent};
449 }
450
451 return Catalyst::ActionChain->from_chain([reverse @chain]);
452}
453
e5ecd5bc 454__PACKAGE__->meta->make_immutable;
f4de8c99 4551;
e5ecd5bc 456
05a90578 457=head1 USAGE
458
459=head2 Introduction
460
461The C<Chained> attribute allows you to chain public path parts together
67869327 462by their private names. A chain part's path can be specified with
463C<PathPart> and can be declared to expect an arbitrary number of
464arguments. The endpoint of the chain specifies how many arguments it
465gets through the C<Args> attribute. C<:Args(0)> would be none at all,
466C<:Args> without an integer would be unlimited. The path parts that
467aren't endpoints are using C<CaptureArgs> to specify how many parameters
468they expect to receive. As an example setup:
05a90578 469
470 package MyApp::Controller::Greeting;
471 use base qw/ Catalyst::Controller /;
472
473 # this is the beginning of our chain
474 sub hello : PathPart('hello') Chained('/') CaptureArgs(1) {
475 my ( $self, $c, $integer ) = @_;
476 $c->stash->{ message } = "Hello ";
477 $c->stash->{ arg_sum } = $integer;
478 }
479
480 # this is our endpoint, because it has no :CaptureArgs
481 sub world : PathPart('world') Chained('hello') Args(1) {
482 my ( $self, $c, $integer ) = @_;
483 $c->stash->{ message } .= "World!";
484 $c->stash->{ arg_sum } += $integer;
485
486 $c->response->body( join "<br/>\n" =>
487 $c->stash->{ message }, $c->stash->{ arg_sum } );
488 }
489
490The debug output provides a separate table for chained actions, showing
67869327 491the whole chain as it would match and the actions it contains. Here's an
492example of the startup output with our actions above:
05a90578 493
494 ...
495 [debug] Loaded Path Part actions:
496 .-----------------------+------------------------------.
497 | Path Spec | Private |
498 +-----------------------+------------------------------+
499 | /hello/*/world/* | /greeting/hello (1) |
500 | | => /greeting/world |
501 '-----------------------+------------------------------'
502 ...
503
67869327 504As you can see, Catalyst only deals with chains as whole paths and
505builds one for each endpoint, which are the actions with C<:Chained> but
506without C<:CaptureArgs>.
05a90578 507
508Let's assume this application gets a request at the path
67869327 509C</hello/23/world/12>. What happens then? First, Catalyst will dispatch
510to the C<hello> action and pass the value C<23> as an argument to it
511after the context. It does so because we have previously used
512C<:CaptureArgs(1)> to declare that it has one path part after itself as
513its argument. We told Catalyst that this is the beginning of the chain
514by specifying C<:Chained('/')>. Also note that instead of saying
515C<:PathPart('hello')> we could also just have said C<:PathPart>, as it
516defaults to the name of the action.
05a90578 517
518After C<hello> has run, Catalyst goes on to dispatch to the C<world>
67869327 519action. This is the last action to be called: Catalyst knows this is an
520endpoint because we did not specify a C<:CaptureArgs>
521attribute. Nevertheless we specify that this action expects an argument,
522but at this point we're using C<:Args(1)> to do that. We could also have
523said C<:Args> or left it out altogether, which would mean this action
524would get all arguments that are there. This action's C<:Chained>
525attribute says C<hello> and tells Catalyst that the C<hello> action in
526the current controller is its parent.
05a90578 527
528With this we have built a chain consisting of two public path parts.
67869327 529C<hello> captures one part of the path as its argument, and also
530specifies the path root as its parent. So this part is
531C</hello/$arg>. The next part is the endpoint C<world>, expecting one
532argument. It sums up to the path part C<world/$arg>. This leads to a
533complete chain of C</hello/$arg/world/$arg> which is matched against the
534requested paths.
535
536This example application would, if run and called by e.g.
537C</hello/23/world/12>, set the stash value C<message> to "Hello" and the
538value C<arg_sum> to "23". The C<world> action would then append "World!"
539to C<message> and add C<12> to the stash's C<arg_sum> value. For the
540sake of simplicity no view is shown. Instead we just put the values of
541the stash into our body. So the output would look like:
05a90578 542
543 Hello World!
544 35
545
67869327 546And our test server would have given us this debugging output for the
05a90578 547request:
548
549 ...
550 [debug] "GET" request for "hello/23/world/12" from "127.0.0.1"
551 [debug] Path is "/greeting/world"
552 [debug] Arguments are "12"
553 [info] Request took 0.164113s (6.093/s)
554 .------------------------------------------+-----------.
555 | Action | Time |
556 +------------------------------------------+-----------+
557 | /greeting/hello | 0.000029s |
558 | /greeting/world | 0.000024s |
559 '------------------------------------------+-----------'
560 ...
561
67869327 562What would be common uses of this dispatch technique? It gives the
563possibility to split up logic that contains steps that each depend on
564each other. An example would be, for example, a wiki path like
05a90578 565C</wiki/FooBarPage/rev/23/view>. This chain can be easily built with
566these actions:
567
568 sub wiki : PathPart('wiki') Chained('/') CaptureArgs(1) {
569 my ( $self, $c, $page_name ) = @_;
570 # load the page named $page_name and put the object
571 # into the stash
572 }
573
574 sub rev : PathPart('rev') Chained('wiki') CaptureArgs(1) {
575 my ( $self, $c, $revision_id ) = @_;
67869327 576 # use the page object in the stash to get at its
05a90578 577 # revision with number $revision_id
578 }
579
580 sub view : PathPart Chained('rev') Args(0) {
581 my ( $self, $c ) = @_;
67869327 582 # display the revision in our stash. Another option
05a90578 583 # would be to forward a compatible object to the action
584 # that displays the default wiki pages, unless we want
585 # a different interface here, for example restore
586 # functionality.
587 }
588
67869327 589It would now be possible to add other endpoints, for example C<restore>
590to restore this specific revision as the current state.
05a90578 591
67869327 592You don't have to put all the chained actions in one controller. The
593specification of the parent through C<:Chained> also takes an absolute
594action path as its argument. Just specify it with a leading C</>.
05a90578 595
596If you want, for example, to have actions for the public paths
67869327 597C</foo/12/edit> and C</foo/12>, just specify two actions with
05a90578 598C<:PathPart('foo')> and C<:Chained('/')>. The handler for the former
67869327 599path needs a C<:CaptureArgs(1)> attribute and a endpoint with
05a90578 600C<:PathPart('edit')> and C<:Chained('foo')>. For the latter path give
601the action just a C<:Args(1)> to mark it as endpoint. This sums up to
602this debugging output:
603
604 ...
605 [debug] Loaded Path Part actions:
606 .-----------------------+------------------------------.
607 | Path Spec | Private |
608 +-----------------------+------------------------------+
609 | /foo/* | /controller/foo_view |
610 | /foo/*/edit | /controller/foo_load (1) |
611 | | => /controller/edit |
612 '-----------------------+------------------------------'
613 ...
614
b0ad47c1 615Here's a more detailed specification of the attributes belonging to
05a90578 616C<:Chained>:
617
618=head2 Attributes
619
620=over 8
621
622=item PathPart
623
624Sets the name of this part of the chain. If it is specified without
625arguments, it takes the name of the action as default. So basically
626C<sub foo :PathPart> and C<sub foo :PathPart('foo')> are identical.
627This can also contain slashes to bind to a deeper level. An action
628with C<sub bar :PathPart('foo/bar') :Chained('/')> would bind to
629C</foo/bar/...>. If you don't specify C<:PathPart> it has the same
630effect as using C<:PathPart>, it would default to the action name.
631
d21a2b27 632=item PathPrefix
633
634Sets PathPart to the path_prefix of the current controller.
635
05a90578 636=item Chained
637
638Has to be specified for every child in the chain. Possible values are
d21a2b27 639absolute and relative private action paths or a single slash C</> to
640tell Catalyst that this is the root of a chain. The attribute
641C<:Chained> without arguments also defaults to the C</> behavior.
642Relative action paths may use C<../> to refer to actions in parent
643controllers.
05a90578 644
67869327 645Because you can specify an absolute path to the parent action, it
646doesn't matter to Catalyst where that parent is located. So, if your
647design requests it, you can redispatch a chain through any controller or
648namespace you want.
05a90578 649
650Another interesting possibility gives C<:Chained('.')>, which chains
67869327 651itself to an action with the path of the current controller's namespace.
05a90578 652For example:
653
654 # in MyApp::Controller::Foo
655 sub bar : Chained CaptureArgs(1) { ... }
656
657 # in MyApp::Controller::Foo::Bar
658 sub baz : Chained('.') Args(1) { ... }
659
660This builds up a chain like C</bar/*/baz/*>. The specification of C<.>
67869327 661as the argument to Chained here chains the C<baz> action to an action
662with the path of the current controller namespace, namely
663C</foo/bar>. That action chains directly to C</>, so the C</bar/*/baz/*>
664chain comes out as the end product.
05a90578 665
d21a2b27 666=item ChainedParent
667
668Chains an action to another action with the same name in the parent
669controller. For Example:
670
671 # in MyApp::Controller::Foo
672 sub bar : Chained CaptureArgs(1) { ... }
673
674 # in MyApp::Controller::Foo::Moo
675 sub bar : ChainedParent Args(1) { ... }
676
677This builds a chain like C</bar/*/bar/*>.
678
05a90578 679=item CaptureArgs
680
67869327 681Must be specified for every part of the chain that is not an
05a90578 682endpoint. With this attribute Catalyst knows how many of the following
67869327 683parts of the path (separated by C</>) this action wants to capture as
684its arguments. If it doesn't expect any, just specify
685C<:CaptureArgs(0)>. The captures get passed to the action's C<@_> right
686after the context, but you can also find them as array references in
05a90578 687C<$c-E<gt>request-E<gt>captures-E<gt>[$level]>. The C<$level> is the
688level of the action in the chain that captured the parts of the path.
689
67869327 690An action that is part of a chain (that is, one that has a C<:Chained>
691attribute) but has no C<:CaptureArgs> attribute is treated by Catalyst
692as a chain end.
05a90578 693
694=item Args
695
696By default, endpoints receive the rest of the arguments in the path. You
697can tell Catalyst through C<:Args> explicitly how many arguments your
698endpoint expects, just like you can with C<:CaptureArgs>. Note that this
67869327 699also affects whether this chain is invoked on a request. A chain with an
05a90578 700endpoint specifying one argument will only match if exactly one argument
701exists in the path.
702
703You can specify an exact number of arguments like C<:Args(3)>, including
704C<0>. If you just say C<:Args> without any arguments, it is the same as
67869327 705leaving it out altogether: The chain is matched regardless of the number
05a90578 706of path parts after the endpoint.
707
67869327 708Just as with C<:CaptureArgs>, the arguments get passed to the action in
05a90578 709C<@_> after the context object. They can also be reached through
710C<$c-E<gt>request-E<gt>arguments>.
711
712=back
713
67869327 714=head2 Auto actions, dispatching and forwarding
05a90578 715
716Note that the list of C<auto> actions called depends on the private path
67869327 717of the endpoint of the chain, not on the chained actions way. The
718C<auto> actions will be run before the chain dispatching begins. In
719every other aspect, C<auto> actions behave as documented.
05a90578 720
64111aca 721The C<forward>ing to other actions does just what you would expect. i.e.
e1207438 722only the target action is run. The actions that that action is chained
723to are not run.
724If you C<detach> out of a chain, the rest of the chain will not get
725called after the C<detach>.
05a90578 726
22faeff5 727=head2 match_captures
728
729A method which can optionally be implemented by actions to
730stop chain matching.
731
732See L<Catalyst::Action> for further details.
733
2f381252 734=head1 AUTHORS
141459fa 735
2f381252 736Catalyst Contributors, see Catalyst.pm
141459fa 737
738=head1 COPYRIGHT
739
536bee89 740This library is free software. You can redistribute it and/or modify it under
141459fa 741the same terms as Perl itself.
742
743=cut
744
7451;