clean up travis config for distar
[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';
cc7af078 242 return $self->_do_forward(forward => @_);
3ea37672 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
cc7af078 264 #If there is an error, all bets off regarding state. Documentation
265 #Specifies that when you forward, if there's an error you must expect
266 #state to be 0.
267 if( @{ $c->error }) {
268 $c->state(0);
269 }
1abd6db7 270 return $c->state;
271}
272
3ea37672 273=head2 $self->detach( $c, $command [, \@arguments ] )
274
275Documented in L<Catalyst>
276
277=cut
278
279sub detach {
280 my ( $self, $c, $command, @args ) = @_;
281 $self->_do_forward(detach => $c, $command, @args ) if $command;
e459bd03 282 $c->state(0); # Needed in order to skip any auto functions
f87b7c21 283 Catalyst::Exception::Detach->throw;
3ea37672 284}
285
adb53907 286sub _action_rel2abs {
e540158b 287 my ( $self, $c, $path ) = @_;
288
289 unless ( $path =~ m#^/# ) {
290 my $namespace = $c->stack->[-1]->namespace;
291 $path = "$namespace/$path";
292 }
293
294 $path =~ s#^/##;
295 return $path;
adb53907 296}
297
298sub _invoke_as_path {
e540158b 299 my ( $self, $c, $rel_path, $args ) = @_;
300
e540158b 301 my $path = $self->_action_rel2abs( $c, $rel_path );
302
303 my ( $tail, @extra_args );
304 while ( ( $path, $tail ) = ( $path =~ m#^(?:(.*)/)?(\w+)?$# ) )
305 { # allow $path to be empty
306 if ( my $action = $c->get_action( $tail, $path ) ) {
307 push @$args, @extra_args;
308 return $action;
309 }
310 else {
311 return
312 unless $path
313 ; # if a match on the global namespace failed then the whole lookup failed
314 }
315
316 unshift @extra_args, $tail;
317 }
adb53907 318}
319
02298d3a 320sub _find_component {
e540158b 321 my ( $self, $c, $component ) = @_;
adb53907 322
02298d3a 323 # fugly, why doesn't ->component('MyApp') work?
324 return $c if ($component eq blessed($c));
325
326 return blessed($component)
327 ? $component
328 : $c->component($component);
adb53907 329}
330
331sub _invoke_as_component {
02298d3a 332 my ( $self, $c, $component_or_class, $method ) = @_;
e540158b 333
02298d3a 334 my $component = $self->_find_component($c, $component_or_class);
335 my $component_class = blessed $component || return 0;
e540158b 336
02298d3a 337 if (my $code = $component_class->can('action_for')) {
338 my $possible_action = $component->$code($method);
84c28acb 339 return $possible_action if $possible_action;
340 }
341
02298d3a 342 if ( my $code = $component_class->can($method) ) {
c41cfce3 343 return $self->_method_action_class->new(
e540158b 344 {
345 name => $method,
346 code => $code,
02298d3a 347 reverse => "$component_class->$method",
348 class => $component_class,
e540158b 349 namespace => Catalyst::Utils::class2prefix(
df960201 350 $component_class, ref($c)->config->{case_sensitive}
e540158b 351 ),
352 }
353 );
354 }
355 else {
356 my $error =
02298d3a 357 qq/Couldn't forward to "$component_class". Does not implement "$method"/;
e540158b 358 $c->error($error);
359 $c->log->debug($error)
360 if $c->debug;
361 return 0;
362 }
adb53907 363}
364
b5ecfcf0 365=head2 $self->prepare_action($c)
fbcc39ad 366
4ab87e27 367Find an dispatch type that matches $c->req->path, and set args from it.
368
fbcc39ad 369=cut
370
371sub prepare_action {
372 my ( $self, $c ) = @_;
e63bdf38 373 my $req = $c->req;
374 my $path = $req->path;
375 my @path = split /\//, $req->path;
376 $req->args( \my @args );
fbcc39ad 377
61a9002d 378 unshift( @path, '' ); # Root action
78d760bb 379
b96f127f 380 DESCEND: while (@path) {
fbcc39ad 381 $path = join '/', @path;
5299fff8 382 $path =~ s#^/+##;
78d760bb 383
22f3a8dd 384 # Check out dispatch types to see if any will handle the path at
385 # this level
386
a2aac3b8 387 foreach my $type ( @{ $self->dispatch_types } ) {
2633d7dc 388 last DESCEND if $type->match( $c, $path );
66e28e3f 389 }
b96f127f 390
22f3a8dd 391 # If not, move the last part path to args
4082e678 392 my $arg = pop(@path);
393 $arg =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
394 unshift @args, $arg;
fbcc39ad 395 }
396
e63bdf38 397 s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg for grep { defined } @{$req->captures||[]};
66d7ad40 398
0ca510f0 399 if($c->debug && defined $req->match && length $req->match) {
400 my $match = $req->match;
401 $match =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
402 $match = decode_utf8($match);
403 $c->log->debug( 'Path is "' . $match . '"' )
404 }
e3a13771 405
0ca510f0 406 $c->log->debug( 'Arguments are "' . join( '/', map { decode_utf8 $_ } @args ) . '"' )
fbcc39ad 407 if ( $c->debug && @args );
408}
409
6e3dd95f 410=head2 $self->get_action( $action_name, $namespace )
1abd6db7 411
6e3dd95f 412returns a named action from a given namespace. C<$action_name>
413may be a relative path on that C<$namespace> such as
414
415 $self->get_action('../bar', 'foo/baz');
416
417In which case we look for the action at 'foo/bar'.
4ab87e27 418
1abd6db7 419=cut
420
421sub get_action {
bcd1002b 422 my ( $self, $name, $namespace ) = @_;
79a3189a 423 return unless $name;
3d0d6d21 424
2f381252 425 $namespace = join( "/", grep { length } split '/', ( defined $namespace ? $namespace : "" ) );
99fe1710 426
518b6198 427 return $self->get_action_by_path("${namespace}/${name}");
1abd6db7 428}
429
b0ad47c1 430=head2 $self->get_action_by_path( $path );
068c0898 431
ab990582 432Returns the named action by its full private path.
3d0d6d21 433
6e3dd95f 434This method performs some normalization on C<$path> so that if
435it includes '..' it will do the right thing (for example if
436C<$path> is '/foo/../bar' that is normalized to '/bar'.
437
068c0898 438=cut
3d0d6d21 439
440sub get_action_by_path {
441 my ( $self, $path ) = @_;
86de687d 442 $path =~s/[^\/]+\/\.\.\/// while $path=~m/[^\/]+\/\.\.\//;
ea0e58d9 443 $path =~ s/^\///;
28928de9 444 $path = "/$path" unless $path =~ /\//;
c41cfce3 445 $self->_action_hash->{$path};
3d0d6d21 446}
447
b5ecfcf0 448=head2 $self->get_actions( $c, $action, $namespace )
a9dc674c 449
450=cut
451
452sub get_actions {
453 my ( $self, $c, $action, $namespace ) = @_;
454 return [] unless $action;
3d0d6d21 455
28928de9 456 $namespace = join( "/", grep { length } split '/', $namespace || "" );
a9dc674c 457
458 my @match = $self->get_containers($namespace);
459
684d10ed 460 return map { $_->get_action($action) } @match;
a9dc674c 461}
462
b5ecfcf0 463=head2 $self->get_containers( $namespace )
cfd04b0c 464
4ab87e27 465Return all the action containers for a given namespace, inclusive
466
cfd04b0c 467=cut
468
469sub get_containers {
470 my ( $self, $namespace ) = @_;
a13e21ab 471 $namespace ||= '';
472 $namespace = '' if $namespace eq '/';
cfd04b0c 473
a13e21ab 474 my @containers;
cfd04b0c 475
7f23827b 476 if ( length $namespace ) {
477 do {
c41cfce3 478 push @containers, $self->_container_hash->{$namespace};
7f23827b 479 } while ( $namespace =~ s#/[^/]+$## );
480 }
90ce41ba 481
c41cfce3 482 return reverse grep { defined } @containers, $self->_container_hash->{''};
cfd04b0c 483}
484
ea0e58d9 485=head2 $self->uri_for_action($action, \@captures)
486
487Takes a Catalyst::Action object and action parameters and returns a URI
488part such that if $c->req->path were this URI part, this action would be
489dispatched to with $c->req->captures set to the supplied arrayref.
490
491If the action object is not available for external dispatch or the dispatcher
492cannot determine an appropriate URI, this method will return undef.
493
494=cut
495
496sub uri_for_action {
497 my ( $self, $action, $captures) = @_;
498 $captures ||= [];
a2aac3b8 499 foreach my $dispatch_type ( @{ $self->dispatch_types } ) {
ea0e58d9 500 my $uri = $dispatch_type->uri_for_action( $action, $captures );
81e75875 501 return( $uri eq '' ? '/' : $uri )
502 if defined($uri);
ea0e58d9 503 }
504 return undef;
505}
506
8f5a2bd9 507=head2 expand_action
ae0e35ee 508
509expand an action into a full representation of the dispatch.
510mostly useful for chained, other actions will just return a
511single action.
512
513=cut
514
52f71256 515sub expand_action {
516 my ($self, $action) = @_;
517
a2aac3b8 518 foreach my $dispatch_type (@{ $self->dispatch_types }) {
52f71256 519 my $expanded = $dispatch_type->expand_action($action);
520 return $expanded if $expanded;
521 }
522
523 return $action;
524}
525
b5ecfcf0 526=head2 $self->register( $c, $action )
aad72cc9 527
4ab87e27 528Make sure all required dispatch types for this action are loaded, then
529pass the action to our dispatch types so they can register it if required.
530Also, set up the tree with the action containers.
531
aad72cc9 532=cut
533
79a3189a 534sub register {
535 my ( $self, $c, $action ) = @_;
536
c41cfce3 537 my $registered = $self->_registered_dispatch_types;
694d15f1 538
694d15f1 539 foreach my $key ( keys %{ $action->attributes } ) {
9a6ecf4f 540 next if $key eq 'Private';
694d15f1 541 my $class = "Catalyst::DispatchType::$key";
542 unless ( $registered->{$class} ) {
c41cfce3 543 # FIXME - Some error checking and re-throwing needed here, as
544 # we eat exceptions loading dispatch types.
e7399d8b 545 # see also try_load_class
546 eval { load_class($class) };
5070f111 547 my $load_failed = $@;
6931f972 548 $self->_check_deprecated_dispatch_type( $key, $load_failed );
5070f111 549 push( @{ $self->dispatch_types }, $class->new ) unless $load_failed;
694d15f1 550 $registered->{$class} = 1;
551 }
552 }
553
a2aac3b8 554 my @dtypes = @{ $self->dispatch_types };
1315d253 555 my @normal_dtypes;
556 my @low_precedence_dtypes;
557
7b442de5 558 for my $type ( @dtypes ) {
c4586fd0 559 if ($type->_is_low_precedence) {
1315d253 560 push @low_precedence_dtypes, $type;
561 } else {
562 push @normal_dtypes, $type;
563 }
564 }
565
694d15f1 566 # Pass the action to our dispatch types so they can register it if reqd.
1315d253 567 my $was_registered = 0;
568 foreach my $type ( @normal_dtypes ) {
569 $was_registered = 1 if $type->register( $c, $action );
570 }
571
572 if (not $was_registered) {
573 foreach my $type ( @low_precedence_dtypes ) {
574 $type->register( $c, $action );
575 }
694d15f1 576 }
577
79a3189a 578 my $namespace = $action->namespace;
a13e21ab 579 my $name = $action->name;
c7116517 580
ad5e4650 581 my $container = $self->_find_or_create_action_container($namespace);
15e9b5dd 582
583 # Set the method value
a13e21ab 584 $container->add_action($action);
c7116517 585
c41cfce3 586 $self->_action_hash->{"$namespace/$name"} = $action;
587 $self->_container_hash->{$namespace} = $container;
15e9b5dd 588}
589
ad5e4650 590sub _find_or_create_action_container {
a13e21ab 591 my ( $self, $namespace ) = @_;
592
c41cfce3 593 my $tree ||= $self->_tree;
99fe1710 594
a13e21ab 595 return $tree->getNodeValue unless $namespace;
78d760bb 596
a13e21ab 597 my @namespace = split '/', $namespace;
598 return $self->_find_or_create_namespace_node( $tree, @namespace )
599 ->getNodeValue;
8505565b 600}
90ce41ba 601
8505565b 602sub _find_or_create_namespace_node {
a13e21ab 603 my ( $self, $parent, $part, @namespace ) = @_;
78d760bb 604
a13e21ab 605 return $parent unless $part;
8505565b 606
a13e21ab 607 my $child =
608 ( grep { $_->getNodeValue->part eq $part } $parent->getAllChildren )[0];
8505565b 609
a13e21ab 610 unless ($child) {
611 my $container = Catalyst::ActionContainer->new($part);
612 $parent->addChild( $child = Tree::Simple->new($container) );
613 }
99fe1710 614
a13e21ab 615 $self->_find_or_create_namespace_node( $child, @namespace );
1abd6db7 616}
617
4ab87e27 618=head2 $self->setup_actions( $class, $context )
619
965f3e35 620Loads all of the pre-load dispatch types, registers their actions and then
621loads all of the post-load dispatch types, and iterates over the tree of
8f59bbe2 622actions, displaying the debug information if appropriate.
1abd6db7 623
624=cut
625
626sub setup_actions {
11bd4e3e 627 my ( $self, $c ) = @_;
99fe1710 628
9e81ba44 629 my @classes =
ad5e4650 630 $self->_load_dispatch_types( @{ $self->preload_dispatch_types } );
c41cfce3 631 @{ $self->_registered_dispatch_types }{@classes} = (1) x @classes;
b96f127f 632
d7325e05 633 foreach my $comp ( map @{$_}{sort keys %$_}, $c->components ) {
98d049ef 634 $comp = $comp->() if ref($comp) eq 'CODE';
49070d25 635 $comp->register_actions($c) if $comp->can('register_actions');
1abd6db7 636 }
e494bd6b 637
ad5e4650 638 $self->_load_dispatch_types( @{ $self->postload_dispatch_types } );
6d030e6f 639
11bd4e3e 640 return unless $c->debug;
2eb2c42f 641 $self->_display_action_tables($c);
642}
643
644sub _display_action_tables {
645 my ($self, $c) = @_;
99fe1710 646
48d435ba 647 my $avail_width = Catalyst::Utils::term_width() - 12;
648 my $col1_width = ($avail_width * .25) < 20 ? 20 : int($avail_width * .25);
649 my $col2_width = ($avail_width * .50) < 36 ? 36 : int($avail_width * .50);
650 my $col3_width = $avail_width - $col1_width - $col2_width;
684d10ed 651 my $privates = Text::SimpleTable->new(
48d435ba 652 [ $col1_width, 'Private' ], [ $col2_width, 'Class' ], [ $col3_width, 'Method' ]
684d10ed 653 );
99fe1710 654
87b85407 655 my $has_private = 0;
1abd6db7 656 my $walker = sub {
657 my ( $walker, $parent, $prefix ) = @_;
658 $prefix .= $parent->getNodeValue || '';
659 $prefix .= '/' unless $prefix =~ /\/$/;
b7aebc12 660 my $node = $parent->getNodeValue->actions;
99fe1710 661
78d760bb 662 for my $action ( keys %{$node} ) {
b7aebc12 663 my $action_obj = $node->{$action};
b0bb11ec 664 next
665 if ( ( $action =~ /^_.*/ )
666 && ( !$c->config->{show_internal_actions} ) );
684d10ed 667 $privates->row( "$prefix$action", $action_obj->class, $action );
87b85407 668 $has_private = 1;
1abd6db7 669 }
99fe1710 670
1abd6db7 671 $walker->( $walker, $_, $prefix ) for $parent->getAllChildren;
672 };
99fe1710 673
c41cfce3 674 $walker->( $walker, $self->_tree, '' );
1cf0345b 675 $c->log->debug( "Loaded Private actions:\n" . $privates->draw . "\n" )
676 if $has_private;
99fe1710 677
a9cbd748 678 # List all public actions
a2aac3b8 679 $_->list($c) for @{ $self->dispatch_types };
1abd6db7 680}
681
ad5e4650 682sub _load_dispatch_types {
9e81ba44 683 my ( $self, @types ) = @_;
684
685 my @loaded;
9e81ba44 686 # Preload action types
687 for my $type (@types) {
5d8129e9 688 # first param is undef because we cannot get the appclass
689 my $class = Catalyst::Utils::resolve_namespace(undef, 'Catalyst::DispatchType', $type);
2efad04b 690
e7399d8b 691 my ($success, $error) = try_load_class($class);
692 Catalyst::Exception->throw( message => $error ) if not $success;
a2aac3b8 693 push @{ $self->dispatch_types }, $class->new;
9e81ba44 694
695 push @loaded, $class;
696 }
697
a13e21ab 698 return @loaded;
9e81ba44 699}
700
e995c634 701=head2 $self->dispatch_type( $type )
702
703Get the DispatchType object of the relevant type, i.e. passing C<$type> of
704C<Chained> would return a L<Catalyst::DispatchType::Chained> object (assuming
b0ad47c1 705of course it's being used.)
e995c634 706
707=cut
708
7ffc9d9d 709sub dispatch_type {
710 my ($self, $name) = @_;
196932de 711
5d8129e9 712 # first param is undef because we cannot get the appclass
713 $name = Catalyst::Utils::resolve_namespace(undef, 'Catalyst::DispatchType', $name);
7ffc9d9d 714
a2aac3b8 715 for (@{ $self->dispatch_types }) {
7ffc9d9d 716 return $_ if ref($_) eq $name;
717 }
718 return undef;
719}
720
6931f972 721sub _check_deprecated_dispatch_type {
5070f111 722 my ($self, $key, $load_failed) = @_;
723
724 return unless $key =~ /^(Local)?Regexp?/;
725
726 # TODO: Should these throw an exception rather than just warning?
727 if ($load_failed) {
728 warn( "Attempt to use deprecated $key dispatch type.\n"
729 . " Use Chained methods or install the standalone\n"
730 . " Catalyst::DispatchType::Regex if necessary.\n" );
731 } elsif ( !defined $Catalyst::DispatchType::Regex::VERSION
28054612 732 || $Catalyst::DispatchType::Regex::VERSION le '5.90020' ) {
5070f111 733 # We loaded the old core version of the Regex module this will break
734 warn( "The $key DispatchType has been removed from Catalyst core.\n"
735 . " An old version of the core Catalyst::DispatchType::Regex\n"
736 . " has been loaded and will likely fail. Please remove\n"
8503ab9b 737 . " $INC{'Catalyst/DispatchType/Regex.pm'}\n"
5070f111 738 . " and use Chained methods or install the standalone\n"
739 . " Catalyst::DispatchType::Regex if necessary.\n" );
740 }
741}
742
c41cfce3 743use Moose;
744
745# 5.70 backwards compatibility hacks.
746
747# Various plugins (e.g. Plugin::Server and Plugin::Authorization::ACL)
748# need the methods here which *should* be private..
749
2efad04b 750# You should be able to use get_actions or get_containers appropriately
751# instead of relying on these methods which expose implementation details
752# of the dispatcher..
753#
754# IRC backlog included below, please come ask if this doesn't work for you.
755#
756# <@t0m> 5.80, the state of. There are things in the dispatcher which have
757# been deprecated, that we yell at anyone for using, which there isn't
758# a good alternative for yet..
759# <@mst> er, get_actions/get_containers provides that doesn't it?
760# <@mst> DispatchTypes are loaded on demand anyway
761# <@t0m> I'm thinking of things like _tree which is aliased to 'tree' with
762# warnings otherwise shit breaks.. We're issuing warnings about the
763# correct set of things which you shouldn't be calling..
764# <@mst> right
765# <@mst> basically, I don't see there's a need for a replacement for anything
766# <@mst> it was never a good idea to call ->tree
767# <@mst> nothingmuch was the only one who did AFAIK
768# <@mst> and he admitted it was a hack ;)
769
c41cfce3 770# See also t/lib/TestApp/Plugin/AddDispatchTypes.pm
771
772# Alias _method_name to method_name, add a before modifier to warn..
b0ad47c1 773foreach my $public_method_name (qw/
774 tree
b0ad47c1 775 registered_dispatch_types
776 method_action_class
777 action_hash
c41cfce3 778 container_hash
779 /) {
780 my $private_method_name = '_' . $public_method_name;
781 my $meta = __PACKAGE__->meta; # Calling meta method here fine as we happen at compile time.
782 $meta->add_method($public_method_name, $meta->get_method($private_method_name));
783 {
784 my %package_hash; # Only warn once per method, per package. These are infrequent enough that
785 # I haven't provided a way to disable them, patches welcome.
786 $meta->add_before_method_modifier($public_method_name, sub {
3cd3bc6a 787 my $class = caller(2);
788 chomp($class);
b3f7d10b 789 $package_hash{$class}++ || do {
3cd3bc6a 790 warn("Class $class is calling the deprecated method\n"
791 . " Catalyst::Dispatcher::$public_method_name,\n"
dacd8b0e 792 . " this will be removed in Catalyst 5.9\n");
c41cfce3 793 };
794 });
795 }
796}
797# End 5.70 backwards compatibility hacks.
798
e5ecd5bc 799__PACKAGE__->meta->make_immutable;
800
059c085b 801=head2 meta
802
803Provided by Moose
804
2f381252 805=head1 AUTHORS
1abd6db7 806
2f381252 807Catalyst Contributors, see Catalyst.pm
1abd6db7 808
809=head1 COPYRIGHT
810
536bee89 811This library is free software. You can redistribute it and/or modify it under
1abd6db7 812the same terms as Perl itself.
813
814=cut
815
8161;