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