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