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