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