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