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