documention for cpan release
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Dispatcher.pm
CommitLineData
68a748b9 1package Catalyst::Dispatcher;
1abd6db7 2
059c085b 3use Moose;
068c0898 4use Class::MOP;
10954d1d 5with 'MooseX::Emulate::Class::Accessor::Fast';
059c085b 6
a2f2cde9 7use Catalyst::Exception;
f05af9ba 8use Catalyst::Utils;
fbcc39ad 9use Catalyst::Action;
b7aebc12 10use Catalyst::ActionContainer;
b96f127f 11use Catalyst::DispatchType::Default;
bcccee4e 12use Catalyst::DispatchType::Index;
39fc2ce1 13use Catalyst::Utils;
87b85407 14use Text::SimpleTable;
1abd6db7 15use Tree::Simple;
16use Tree::Simple::Visitor::FindByPath;
e7399d8b 17use Class::Load qw(load_class try_load_class);
0ca510f0 18use Encode 2.21 'decode_utf8';
1abd6db7 19
792d99b4 20use namespace::clean -except => 'meta';
6f3df815 21
c41cfce3 22# Refactoring note:
23# do these belong as package vars or should we build these via a builder method?
24# See Catalyst-Plugin-Server for them being added to, which should be much less ugly.
25
6d030e6f 26# Preload these action types
9c1fc6d6 27our @PRELOAD = qw/Index Path/;
1abd6db7 28
2d1d8f91 29# Postload these action types
61a9002d 30our @POSTLOAD = qw/Default/;
2d1d8f91 31
c41cfce3 32# Note - see back-compat methods at end of file.
792d99b4 33has _tree => (is => 'rw', builder => '_build__tree');
5b8ac6cc 34has dispatch_types => (is => 'rw', default => sub { [] }, required => 1, lazy => 1);
c41cfce3 35has _registered_dispatch_types => (is => 'rw', default => sub { {} }, required => 1, lazy => 1);
36has _method_action_class => (is => 'rw', default => 'Catalyst::Action');
37has _action_hash => (is => 'rw', required => 1, lazy => 1, default => sub { {} });
38has _container_hash => (is => 'rw', required => 1, lazy => 1, default => sub { {} });
7ffc9d9d 39
11ff9b94 40my %dispatch_types = ( pre => \@PRELOAD, post => \@POSTLOAD );
41foreach my $type (keys %dispatch_types) {
42 has $type . "load_dispatch_types" => (
43 is => 'rw', required => 1, lazy => 1, default => sub { $dispatch_types{$type} },
44 traits => ['MooseX::Emulate::Class::Accessor::Fast::Meta::Role::Attribute'], # List assignment is CAF style
45 );
46}
083ee5d9 47
1abd6db7 48=head1 NAME
49
9c053379 50Catalyst::Dispatcher - The Catalyst Dispatcher
1abd6db7 51
52=head1 SYNOPSIS
53
54See L<Catalyst>.
55
56=head1 DESCRIPTION
57
4ab87e27 58This is the class that maps public urls to actions in your Catalyst
59application based on the attributes you set.
60
1abd6db7 61=head1 METHODS
62
b0ad47c1 63=head2 new
4ab87e27 64
65Construct a new dispatcher.
66
e7bb8d33 67=cut
68
792d99b4 69sub _build__tree {
70 my ($self) = @_;
9e81ba44 71
068c0898 72 my $container =
059c085b 73 Catalyst::ActionContainer->new( { part => '/', actions => {} } );
a13e21ab 74
792d99b4 75 return Tree::Simple->new($container, Tree::Simple->ROOT);
e7bb8d33 76}
77
78=head2 $self->preload_dispatch_types
79
80An arrayref of pre-loaded dispatchtype classes
81
82Entries are considered to be available as C<Catalyst::DispatchType::CLASS>
83To use a custom class outside the regular C<Catalyst> namespace, prefix
84it with a C<+>, like so:
85
86 +My::Dispatch::Type
87
88=head2 $self->postload_dispatch_types
89
90An arrayref of post-loaded dispatchtype classes
91
92Entries are considered to be available as C<Catalyst::DispatchType::CLASS>
93To use a custom class outside the regular C<Catalyst> namespace, prefix
94it with a C<+>, like so:
95
96 +My::Dispatch::Type
97
b5ecfcf0 98=head2 $self->dispatch($c)
1abd6db7 99
4ab87e27 100Delegate the dispatch to the action that matched the url, or return a
101message about unknown resource
102
1abd6db7 103=cut
104
105sub dispatch {
fbcc39ad 106 my ( $self, $c ) = @_;
e63bdf38 107 if ( my $action = $c->action ) {
108 $c->forward( join( '/', '', $action->namespace, '_DISPATCH' ) );
fbcc39ad 109 }
fbcc39ad 110 else {
1abd6db7 111 my $path = $c->req->path;
0ca510f0 112 $path =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
113 $path = decode_utf8($path);
114
1abd6db7 115 my $error = $path
116 ? qq/Unknown resource "$path"/
117 : "No default action defined";
118 $c->log->error($error) if $c->debug;
119 $c->error($error);
120 }
121}
122
2f381252 123# $self->_command2action( $c, $command [, \@arguments ] )
b456f8f3 124# $self->_command2action( $c, $command [, \@captures, \@arguments ] )
125# Search for an action, from the command and returns C<($action, $args, $captures)> on
2f381252 126# success. Returns C<(0)> on error.
1abd6db7 127
2f381252 128sub _command2action {
e72f8f51 129 my ( $self, $c, $command, @extra_params ) = @_;
99fe1710 130
1abd6db7 131 unless ($command) {
2f381252 132 $c->log->debug('Nothing to go to') if $c->debug;
1abd6db7 133 return 0;
134 }
99fe1710 135
b456f8f3 136 my (@args, @captures);
137
138 if ( ref( $extra_params[-2] ) eq 'ARRAY' ) {
e1e81442 139 @captures = @{ splice @extra_params, -2, 1 };
b456f8f3 140 }
068c0898 141
e72f8f51 142 if ( ref( $extra_params[-1] ) eq 'ARRAY' ) {
143 @args = @{ pop @extra_params }
144 } else {
2f381252 145 # this is a copy, it may take some abuse from
146 # ->_invoke_as_path if the path had trailing parts
e72f8f51 147 @args = @{ $c->request->arguments };
148 }
149
150 my $action;
151
2f381252 152 # go to a string path ("/foo/bar/gorch")
e31b525c 153 # or action object
7e95ba12 154 if (blessed($command) && $command->isa('Catalyst::Action')) {
e31b525c 155 $action = $command;
156 }
157 else {
158 $action = $self->_invoke_as_path( $c, "$command", \@args );
159 }
99fe1710 160
76ac74ec 161 # go to a component ( "View::Foo" or $c->component("...")
2f381252 162 # - a path or an object)
e72f8f51 163 unless ($action) {
164 my $method = @extra_params ? $extra_params[0] : "process";
165 $action = $self->_invoke_as_component( $c, $command, $method );
166 }
99fe1710 167
b456f8f3 168 return $action, \@args, \@captures;
2f381252 169}
170
ae0e35ee 171=head2 $self->visit( $c, $command [, \@arguments ] )
2f381252 172
173Documented in L<Catalyst>
174
175=cut
176
ae0e35ee 177sub visit {
2f381252 178 my $self = shift;
ae0e35ee 179 $self->_do_visit('visit', @_);
180}
181
182sub _do_visit {
183 my $self = shift;
184 my $opname = shift;
2f381252 185 my ( $c, $command ) = @_;
b456f8f3 186 my ( $action, $args, $captures ) = $self->_command2action(@_);
ae0e35ee 187 my $error = qq/Couldn't $opname("$command"): /;
2f381252 188
ae0e35ee 189 if (!$action) {
3ea37672 190 $error .= qq/Couldn't $opname to command "$command": /
191 .qq/Invalid action or component./;
ae0e35ee 192 }
193 elsif (!defined $action->namespace) {
194 $error .= qq/Action has no namespace: cannot $opname() to a plain /
382d317c 195 .qq/method or component, must be an :Action of some sort./
ae0e35ee 196 }
197 elsif (!$action->class->can('_DISPATCH')) {
198 $error .= qq/Action cannot _DISPATCH. /
199 .qq/Did you try to $opname() a non-controller action?/;
200 }
201 else {
202 $error = q();
203 }
204
205 if($error) {
2f381252 206 $c->error($error);
207 $c->log->debug($error) if $c->debug;
208 return 0;
209 }
210
52f71256 211 $action = $self->expand_action($action);
212
2f381252 213 local $c->request->{arguments} = $args;
b456f8f3 214 local $c->request->{captures} = $captures;
ae0e35ee 215 local $c->{namespace} = $action->{'namespace'};
216 local $c->{action} = $action;
217
2f381252 218 $self->dispatch($c);
ae0e35ee 219}
220
221=head2 $self->go( $c, $command [, \@arguments ] )
222
223Documented in L<Catalyst>
224
225=cut
2f381252 226
ae0e35ee 227sub go {
228 my $self = shift;
229 $self->_do_visit('go', @_);
f87b7c21 230 Catalyst::Exception::Go->throw;
2f381252 231}
232
233=head2 $self->forward( $c, $command [, \@arguments ] )
234
235Documented in L<Catalyst>
236
237=cut
238
239sub forward {
240 my $self = shift;
6f3df815 241 no warnings 'recursion';
3ea37672 242 $self->_do_forward(forward => @_);
243}
244
245sub _do_forward {
246 my $self = shift;
247 my $opname = shift;
2f381252 248 my ( $c, $command ) = @_;
b456f8f3 249 my ( $action, $args, $captures ) = $self->_command2action(@_);
99fe1710 250
3ea37672 251 if (!$action) {
252 my $error .= qq/Couldn't $opname to command "$command": /
253 .qq/Invalid action or component./;
e540158b 254 $c->error($error);
255 $c->log->debug($error) if $c->debug;
256 return 0;
257 }
bd7d2e94 258
059c085b 259
12f0342e 260 local $c->request->{arguments} = $args;
6f3df815 261 no warnings 'recursion';
b8f669f3 262 $action->dispatch( $c );
3ea37672 263
1abd6db7 264 return $c->state;
265}
266
3ea37672 267=head2 $self->detach( $c, $command [, \@arguments ] )
268
269Documented in L<Catalyst>
270
271=cut
272
273sub detach {
274 my ( $self, $c, $command, @args ) = @_;
275 $self->_do_forward(detach => $c, $command, @args ) if $command;
e459bd03 276 $c->state(0); # Needed in order to skip any auto functions
f87b7c21 277 Catalyst::Exception::Detach->throw;
3ea37672 278}
279
adb53907 280sub _action_rel2abs {
e540158b 281 my ( $self, $c, $path ) = @_;
282
283 unless ( $path =~ m#^/# ) {
284 my $namespace = $c->stack->[-1]->namespace;
285 $path = "$namespace/$path";
286 }
287
288 $path =~ s#^/##;
289 return $path;
adb53907 290}
291
292sub _invoke_as_path {
e540158b 293 my ( $self, $c, $rel_path, $args ) = @_;
294
e540158b 295 my $path = $self->_action_rel2abs( $c, $rel_path );
296
297 my ( $tail, @extra_args );
298 while ( ( $path, $tail ) = ( $path =~ m#^(?:(.*)/)?(\w+)?$# ) )
299 { # allow $path to be empty
300 if ( my $action = $c->get_action( $tail, $path ) ) {
301 push @$args, @extra_args;
302 return $action;
303 }
304 else {
305 return
306 unless $path
307 ; # if a match on the global namespace failed then the whole lookup failed
308 }
309
310 unshift @extra_args, $tail;
311 }
adb53907 312}
313
02298d3a 314sub _find_component {
e540158b 315 my ( $self, $c, $component ) = @_;
adb53907 316
02298d3a 317 # fugly, why doesn't ->component('MyApp') work?
318 return $c if ($component eq blessed($c));
319
320 return blessed($component)
321 ? $component
322 : $c->component($component);
adb53907 323}
324
325sub _invoke_as_component {
02298d3a 326 my ( $self, $c, $component_or_class, $method ) = @_;
e540158b 327
02298d3a 328 my $component = $self->_find_component($c, $component_or_class);
329 my $component_class = blessed $component || return 0;
e540158b 330
02298d3a 331 if (my $code = $component_class->can('action_for')) {
332 my $possible_action = $component->$code($method);
84c28acb 333 return $possible_action if $possible_action;
334 }
335
02298d3a 336 if ( my $code = $component_class->can($method) ) {
c41cfce3 337 return $self->_method_action_class->new(
e540158b 338 {
339 name => $method,
340 code => $code,
02298d3a 341 reverse => "$component_class->$method",
342 class => $component_class,
e540158b 343 namespace => Catalyst::Utils::class2prefix(
df960201 344 $component_class, ref($c)->config->{case_sensitive}
e540158b 345 ),
346 }
347 );
348 }
349 else {
350 my $error =
02298d3a 351 qq/Couldn't forward to "$component_class". Does not implement "$method"/;
e540158b 352 $c->error($error);
353 $c->log->debug($error)
354 if $c->debug;
355 return 0;
356 }
adb53907 357}
358
b5ecfcf0 359=head2 $self->prepare_action($c)
fbcc39ad 360
4ab87e27 361Find an dispatch type that matches $c->req->path, and set args from it.
362
fbcc39ad 363=cut
364
365sub prepare_action {
366 my ( $self, $c ) = @_;
e63bdf38 367 my $req = $c->req;
368 my $path = $req->path;
369 my @path = split /\//, $req->path;
370 $req->args( \my @args );
fbcc39ad 371
61a9002d 372 unshift( @path, '' ); # Root action
78d760bb 373
b96f127f 374 DESCEND: while (@path) {
fbcc39ad 375 $path = join '/', @path;
5299fff8 376 $path =~ s#^/+##;
78d760bb 377
22f3a8dd 378 # Check out dispatch types to see if any will handle the path at
379 # this level
380
a2aac3b8 381 foreach my $type ( @{ $self->dispatch_types } ) {
2633d7dc 382 last DESCEND if $type->match( $c, $path );
66e28e3f 383 }
b96f127f 384
22f3a8dd 385 # If not, move the last part path to args
4082e678 386 my $arg = pop(@path);
387 $arg =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
388 unshift @args, $arg;
fbcc39ad 389 }
390
e63bdf38 391 s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg for grep { defined } @{$req->captures||[]};
66d7ad40 392
0ca510f0 393 if($c->debug && defined $req->match && length $req->match) {
394 my $match = $req->match;
395 $match =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
396 $match = decode_utf8($match);
397 $c->log->debug( 'Path is "' . $match . '"' )
398 }
e3a13771 399
0ca510f0 400 $c->log->debug( 'Arguments are "' . join( '/', map { decode_utf8 $_ } @args ) . '"' )
fbcc39ad 401 if ( $c->debug && @args );
402}
403
b5ecfcf0 404=head2 $self->get_action( $action, $namespace )
1abd6db7 405
4ab87e27 406returns a named action from a given namespace.
407
1abd6db7 408=cut
409
410sub get_action {
bcd1002b 411 my ( $self, $name, $namespace ) = @_;
79a3189a 412 return unless $name;
3d0d6d21 413
2f381252 414 $namespace = join( "/", grep { length } split '/', ( defined $namespace ? $namespace : "" ) );
99fe1710 415
c41cfce3 416 return $self->_action_hash->{"${namespace}/${name}"};
1abd6db7 417}
418
b0ad47c1 419=head2 $self->get_action_by_path( $path );
068c0898 420
ab990582 421Returns the named action by its full private path.
3d0d6d21 422
068c0898 423=cut
3d0d6d21 424
425sub get_action_by_path {
426 my ( $self, $path ) = @_;
ea0e58d9 427 $path =~ s/^\///;
28928de9 428 $path = "/$path" unless $path =~ /\//;
c41cfce3 429 $self->_action_hash->{$path};
3d0d6d21 430}
431
b5ecfcf0 432=head2 $self->get_actions( $c, $action, $namespace )
a9dc674c 433
434=cut
435
436sub get_actions {
437 my ( $self, $c, $action, $namespace ) = @_;
438 return [] unless $action;
3d0d6d21 439
28928de9 440 $namespace = join( "/", grep { length } split '/', $namespace || "" );
a9dc674c 441
442 my @match = $self->get_containers($namespace);
443
684d10ed 444 return map { $_->get_action($action) } @match;
a9dc674c 445}
446
b5ecfcf0 447=head2 $self->get_containers( $namespace )
cfd04b0c 448
4ab87e27 449Return all the action containers for a given namespace, inclusive
450
cfd04b0c 451=cut
452
453sub get_containers {
454 my ( $self, $namespace ) = @_;
a13e21ab 455 $namespace ||= '';
456 $namespace = '' if $namespace eq '/';
cfd04b0c 457
a13e21ab 458 my @containers;
cfd04b0c 459
7f23827b 460 if ( length $namespace ) {
461 do {
c41cfce3 462 push @containers, $self->_container_hash->{$namespace};
7f23827b 463 } while ( $namespace =~ s#/[^/]+$## );
464 }
90ce41ba 465
c41cfce3 466 return reverse grep { defined } @containers, $self->_container_hash->{''};
cfd04b0c 467}
468
ea0e58d9 469=head2 $self->uri_for_action($action, \@captures)
470
471Takes a Catalyst::Action object and action parameters and returns a URI
472part such that if $c->req->path were this URI part, this action would be
473dispatched to with $c->req->captures set to the supplied arrayref.
474
475If the action object is not available for external dispatch or the dispatcher
476cannot determine an appropriate URI, this method will return undef.
477
478=cut
479
480sub uri_for_action {
481 my ( $self, $action, $captures) = @_;
482 $captures ||= [];
a2aac3b8 483 foreach my $dispatch_type ( @{ $self->dispatch_types } ) {
ea0e58d9 484 my $uri = $dispatch_type->uri_for_action( $action, $captures );
81e75875 485 return( $uri eq '' ? '/' : $uri )
486 if defined($uri);
ea0e58d9 487 }
488 return undef;
489}
490
8f5a2bd9 491=head2 expand_action
ae0e35ee 492
493expand an action into a full representation of the dispatch.
494mostly useful for chained, other actions will just return a
495single action.
496
497=cut
498
52f71256 499sub expand_action {
500 my ($self, $action) = @_;
501
a2aac3b8 502 foreach my $dispatch_type (@{ $self->dispatch_types }) {
52f71256 503 my $expanded = $dispatch_type->expand_action($action);
504 return $expanded if $expanded;
505 }
506
507 return $action;
508}
509
b5ecfcf0 510=head2 $self->register( $c, $action )
aad72cc9 511
4ab87e27 512Make sure all required dispatch types for this action are loaded, then
513pass the action to our dispatch types so they can register it if required.
514Also, set up the tree with the action containers.
515
aad72cc9 516=cut
517
79a3189a 518sub register {
519 my ( $self, $c, $action ) = @_;
520
c41cfce3 521 my $registered = $self->_registered_dispatch_types;
694d15f1 522
694d15f1 523 foreach my $key ( keys %{ $action->attributes } ) {
9a6ecf4f 524 next if $key eq 'Private';
694d15f1 525 my $class = "Catalyst::DispatchType::$key";
526 unless ( $registered->{$class} ) {
c41cfce3 527 # FIXME - Some error checking and re-throwing needed here, as
528 # we eat exceptions loading dispatch types.
e7399d8b 529 # see also try_load_class
530 eval { load_class($class) };
5070f111 531 my $load_failed = $@;
6931f972 532 $self->_check_deprecated_dispatch_type( $key, $load_failed );
5070f111 533 push( @{ $self->dispatch_types }, $class->new ) unless $load_failed;
694d15f1 534 $registered->{$class} = 1;
535 }
536 }
537
a2aac3b8 538 my @dtypes = @{ $self->dispatch_types };
1315d253 539 my @normal_dtypes;
540 my @low_precedence_dtypes;
541
7b442de5 542 for my $type ( @dtypes ) {
c4586fd0 543 if ($type->_is_low_precedence) {
1315d253 544 push @low_precedence_dtypes, $type;
545 } else {
546 push @normal_dtypes, $type;
547 }
548 }
549
694d15f1 550 # Pass the action to our dispatch types so they can register it if reqd.
1315d253 551 my $was_registered = 0;
552 foreach my $type ( @normal_dtypes ) {
553 $was_registered = 1 if $type->register( $c, $action );
554 }
555
556 if (not $was_registered) {
557 foreach my $type ( @low_precedence_dtypes ) {
558 $type->register( $c, $action );
559 }
694d15f1 560 }
561
79a3189a 562 my $namespace = $action->namespace;
a13e21ab 563 my $name = $action->name;
c7116517 564
ad5e4650 565 my $container = $self->_find_or_create_action_container($namespace);
15e9b5dd 566
567 # Set the method value
a13e21ab 568 $container->add_action($action);
c7116517 569
c41cfce3 570 $self->_action_hash->{"$namespace/$name"} = $action;
571 $self->_container_hash->{$namespace} = $container;
15e9b5dd 572}
573
ad5e4650 574sub _find_or_create_action_container {
a13e21ab 575 my ( $self, $namespace ) = @_;
576
c41cfce3 577 my $tree ||= $self->_tree;
99fe1710 578
a13e21ab 579 return $tree->getNodeValue unless $namespace;
78d760bb 580
a13e21ab 581 my @namespace = split '/', $namespace;
582 return $self->_find_or_create_namespace_node( $tree, @namespace )
583 ->getNodeValue;
8505565b 584}
90ce41ba 585
8505565b 586sub _find_or_create_namespace_node {
a13e21ab 587 my ( $self, $parent, $part, @namespace ) = @_;
78d760bb 588
a13e21ab 589 return $parent unless $part;
8505565b 590
a13e21ab 591 my $child =
592 ( grep { $_->getNodeValue->part eq $part } $parent->getAllChildren )[0];
8505565b 593
a13e21ab 594 unless ($child) {
595 my $container = Catalyst::ActionContainer->new($part);
596 $parent->addChild( $child = Tree::Simple->new($container) );
597 }
99fe1710 598
a13e21ab 599 $self->_find_or_create_namespace_node( $child, @namespace );
1abd6db7 600}
601
4ab87e27 602=head2 $self->setup_actions( $class, $context )
603
965f3e35 604Loads all of the pre-load dispatch types, registers their actions and then
605loads all of the post-load dispatch types, and iterates over the tree of
8f59bbe2 606actions, displaying the debug information if appropriate.
1abd6db7 607
608=cut
609
610sub setup_actions {
11bd4e3e 611 my ( $self, $c ) = @_;
99fe1710 612
9e81ba44 613 my @classes =
ad5e4650 614 $self->_load_dispatch_types( @{ $self->preload_dispatch_types } );
c41cfce3 615 @{ $self->_registered_dispatch_types }{@classes} = (1) x @classes;
b96f127f 616
98d049ef 617 foreach my $comp ( values %{ $c->components } ) {
618 $comp = $comp->() if ref($comp) eq 'CODE';
49070d25 619 $comp->register_actions($c) if $comp->can('register_actions');
1abd6db7 620 }
e494bd6b 621
ad5e4650 622 $self->_load_dispatch_types( @{ $self->postload_dispatch_types } );
6d030e6f 623
11bd4e3e 624 return unless $c->debug;
2eb2c42f 625 $self->_display_action_tables($c);
626}
627
628sub _display_action_tables {
629 my ($self, $c) = @_;
99fe1710 630
48d435ba 631 my $avail_width = Catalyst::Utils::term_width() - 12;
632 my $col1_width = ($avail_width * .25) < 20 ? 20 : int($avail_width * .25);
633 my $col2_width = ($avail_width * .50) < 36 ? 36 : int($avail_width * .50);
634 my $col3_width = $avail_width - $col1_width - $col2_width;
684d10ed 635 my $privates = Text::SimpleTable->new(
48d435ba 636 [ $col1_width, 'Private' ], [ $col2_width, 'Class' ], [ $col3_width, 'Method' ]
684d10ed 637 );
99fe1710 638
87b85407 639 my $has_private = 0;
1abd6db7 640 my $walker = sub {
641 my ( $walker, $parent, $prefix ) = @_;
642 $prefix .= $parent->getNodeValue || '';
643 $prefix .= '/' unless $prefix =~ /\/$/;
b7aebc12 644 my $node = $parent->getNodeValue->actions;
99fe1710 645
78d760bb 646 for my $action ( keys %{$node} ) {
b7aebc12 647 my $action_obj = $node->{$action};
b0bb11ec 648 next
649 if ( ( $action =~ /^_.*/ )
650 && ( !$c->config->{show_internal_actions} ) );
684d10ed 651 $privates->row( "$prefix$action", $action_obj->class, $action );
87b85407 652 $has_private = 1;
1abd6db7 653 }
99fe1710 654
1abd6db7 655 $walker->( $walker, $_, $prefix ) for $parent->getAllChildren;
656 };
99fe1710 657
c41cfce3 658 $walker->( $walker, $self->_tree, '' );
1cf0345b 659 $c->log->debug( "Loaded Private actions:\n" . $privates->draw . "\n" )
660 if $has_private;
99fe1710 661
a9cbd748 662 # List all public actions
a2aac3b8 663 $_->list($c) for @{ $self->dispatch_types };
1abd6db7 664}
665
ad5e4650 666sub _load_dispatch_types {
9e81ba44 667 my ( $self, @types ) = @_;
668
669 my @loaded;
9e81ba44 670 # Preload action types
671 for my $type (@types) {
5d8129e9 672 # first param is undef because we cannot get the appclass
673 my $class = Catalyst::Utils::resolve_namespace(undef, 'Catalyst::DispatchType', $type);
2efad04b 674
e7399d8b 675 my ($success, $error) = try_load_class($class);
676 Catalyst::Exception->throw( message => $error ) if not $success;
a2aac3b8 677 push @{ $self->dispatch_types }, $class->new;
9e81ba44 678
679 push @loaded, $class;
680 }
681
a13e21ab 682 return @loaded;
9e81ba44 683}
684
e995c634 685=head2 $self->dispatch_type( $type )
686
687Get the DispatchType object of the relevant type, i.e. passing C<$type> of
688C<Chained> would return a L<Catalyst::DispatchType::Chained> object (assuming
b0ad47c1 689of course it's being used.)
e995c634 690
691=cut
692
7ffc9d9d 693sub dispatch_type {
694 my ($self, $name) = @_;
196932de 695
5d8129e9 696 # first param is undef because we cannot get the appclass
697 $name = Catalyst::Utils::resolve_namespace(undef, 'Catalyst::DispatchType', $name);
7ffc9d9d 698
a2aac3b8 699 for (@{ $self->dispatch_types }) {
7ffc9d9d 700 return $_ if ref($_) eq $name;
701 }
702 return undef;
703}
704
6931f972 705sub _check_deprecated_dispatch_type {
5070f111 706 my ($self, $key, $load_failed) = @_;
707
708 return unless $key =~ /^(Local)?Regexp?/;
709
710 # TODO: Should these throw an exception rather than just warning?
711 if ($load_failed) {
712 warn( "Attempt to use deprecated $key dispatch type.\n"
713 . " Use Chained methods or install the standalone\n"
714 . " Catalyst::DispatchType::Regex if necessary.\n" );
715 } elsif ( !defined $Catalyst::DispatchType::Regex::VERSION
28054612 716 || $Catalyst::DispatchType::Regex::VERSION le '5.90020' ) {
5070f111 717 # We loaded the old core version of the Regex module this will break
718 warn( "The $key DispatchType has been removed from Catalyst core.\n"
719 . " An old version of the core Catalyst::DispatchType::Regex\n"
720 . " has been loaded and will likely fail. Please remove\n"
8503ab9b 721 . " $INC{'Catalyst/DispatchType/Regex.pm'}\n"
5070f111 722 . " and use Chained methods or install the standalone\n"
723 . " Catalyst::DispatchType::Regex if necessary.\n" );
724 }
725}
726
c41cfce3 727use Moose;
728
729# 5.70 backwards compatibility hacks.
730
731# Various plugins (e.g. Plugin::Server and Plugin::Authorization::ACL)
732# need the methods here which *should* be private..
733
2efad04b 734# You should be able to use get_actions or get_containers appropriately
735# instead of relying on these methods which expose implementation details
736# of the dispatcher..
737#
738# IRC backlog included below, please come ask if this doesn't work for you.
739#
740# <@t0m> 5.80, the state of. There are things in the dispatcher which have
741# been deprecated, that we yell at anyone for using, which there isn't
742# a good alternative for yet..
743# <@mst> er, get_actions/get_containers provides that doesn't it?
744# <@mst> DispatchTypes are loaded on demand anyway
745# <@t0m> I'm thinking of things like _tree which is aliased to 'tree' with
746# warnings otherwise shit breaks.. We're issuing warnings about the
747# correct set of things which you shouldn't be calling..
748# <@mst> right
749# <@mst> basically, I don't see there's a need for a replacement for anything
750# <@mst> it was never a good idea to call ->tree
751# <@mst> nothingmuch was the only one who did AFAIK
752# <@mst> and he admitted it was a hack ;)
753
c41cfce3 754# See also t/lib/TestApp/Plugin/AddDispatchTypes.pm
755
756# Alias _method_name to method_name, add a before modifier to warn..
b0ad47c1 757foreach my $public_method_name (qw/
758 tree
b0ad47c1 759 registered_dispatch_types
760 method_action_class
761 action_hash
c41cfce3 762 container_hash
763 /) {
764 my $private_method_name = '_' . $public_method_name;
765 my $meta = __PACKAGE__->meta; # Calling meta method here fine as we happen at compile time.
766 $meta->add_method($public_method_name, $meta->get_method($private_method_name));
767 {
768 my %package_hash; # Only warn once per method, per package. These are infrequent enough that
769 # I haven't provided a way to disable them, patches welcome.
770 $meta->add_before_method_modifier($public_method_name, sub {
3cd3bc6a 771 my $class = caller(2);
772 chomp($class);
b3f7d10b 773 $package_hash{$class}++ || do {
3cd3bc6a 774 warn("Class $class is calling the deprecated method\n"
775 . " Catalyst::Dispatcher::$public_method_name,\n"
dacd8b0e 776 . " this will be removed in Catalyst 5.9\n");
c41cfce3 777 };
778 });
779 }
780}
781# End 5.70 backwards compatibility hacks.
782
e5ecd5bc 783__PACKAGE__->meta->make_immutable;
784
059c085b 785=head2 meta
786
787Provided by Moose
788
2f381252 789=head1 AUTHORS
1abd6db7 790
2f381252 791Catalyst Contributors, see Catalyst.pm
1abd6db7 792
793=head1 COPYRIGHT
794
536bee89 795This library is free software. You can redistribute it and/or modify it under
1abd6db7 796the same terms as Perl itself.
797
798=cut
799
8001;