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