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