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