Merge master into gsoc_breadboard
[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
76ac74ec 156 # go to a component ( "View::Foo" or $c->component("...")
2f381252 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
2f381252 208 local $c->request->{arguments} = $args;
b456f8f3 209 local $c->request->{captures} = $captures;
ae0e35ee 210 local $c->{namespace} = $action->{'namespace'};
211 local $c->{action} = $action;
212
2f381252 213 $self->dispatch($c);
ae0e35ee 214}
215
216=head2 $self->go( $c, $command [, \@arguments ] )
217
218Documented in L<Catalyst>
219
220=cut
2f381252 221
ae0e35ee 222sub go {
223 my $self = shift;
224 $self->_do_visit('go', @_);
f87b7c21 225 Catalyst::Exception::Go->throw;
2f381252 226}
227
228=head2 $self->forward( $c, $command [, \@arguments ] )
229
230Documented in L<Catalyst>
231
232=cut
233
234sub forward {
235 my $self = shift;
6f3df815 236 no warnings 'recursion';
3ea37672 237 $self->_do_forward(forward => @_);
238}
239
240sub _do_forward {
241 my $self = shift;
242 my $opname = shift;
2f381252 243 my ( $c, $command ) = @_;
b456f8f3 244 my ( $action, $args, $captures ) = $self->_command2action(@_);
99fe1710 245
3ea37672 246 if (!$action) {
247 my $error .= qq/Couldn't $opname to command "$command": /
248 .qq/Invalid action or component./;
e540158b 249 $c->error($error);
250 $c->log->debug($error) if $c->debug;
251 return 0;
252 }
bd7d2e94 253
059c085b 254
12f0342e 255 local $c->request->{arguments} = $args;
6f3df815 256 no warnings 'recursion';
b8f669f3 257 $action->dispatch( $c );
3ea37672 258
1abd6db7 259 return $c->state;
260}
261
3ea37672 262=head2 $self->detach( $c, $command [, \@arguments ] )
263
264Documented in L<Catalyst>
265
266=cut
267
268sub detach {
269 my ( $self, $c, $command, @args ) = @_;
270 $self->_do_forward(detach => $c, $command, @args ) if $command;
f87b7c21 271 Catalyst::Exception::Detach->throw;
3ea37672 272}
273
adb53907 274sub _action_rel2abs {
e540158b 275 my ( $self, $c, $path ) = @_;
276
277 unless ( $path =~ m#^/# ) {
278 my $namespace = $c->stack->[-1]->namespace;
279 $path = "$namespace/$path";
280 }
281
282 $path =~ s#^/##;
283 return $path;
adb53907 284}
285
286sub _invoke_as_path {
e540158b 287 my ( $self, $c, $rel_path, $args ) = @_;
288
e540158b 289 my $path = $self->_action_rel2abs( $c, $rel_path );
290
291 my ( $tail, @extra_args );
292 while ( ( $path, $tail ) = ( $path =~ m#^(?:(.*)/)?(\w+)?$# ) )
293 { # allow $path to be empty
294 if ( my $action = $c->get_action( $tail, $path ) ) {
295 push @$args, @extra_args;
296 return $action;
297 }
298 else {
299 return
300 unless $path
301 ; # if a match on the global namespace failed then the whole lookup failed
302 }
303
304 unshift @extra_args, $tail;
305 }
adb53907 306}
307
02298d3a 308sub _find_component {
e540158b 309 my ( $self, $c, $component ) = @_;
adb53907 310
02298d3a 311 return blessed($component)
312 ? $component
313 : $c->component($component);
adb53907 314}
315
316sub _invoke_as_component {
02298d3a 317 my ( $self, $c, $component_or_class, $method ) = @_;
e540158b 318
02298d3a 319 my $component = $self->_find_component($c, $component_or_class);
320 my $component_class = blessed $component || return 0;
e540158b 321
02298d3a 322 if (my $code = $component_class->can('action_for')) {
323 my $possible_action = $component->$code($method);
84c28acb 324 return $possible_action if $possible_action;
325 }
326
02298d3a 327 if ( my $code = $component_class->can($method) ) {
c41cfce3 328 return $self->_method_action_class->new(
e540158b 329 {
330 name => $method,
331 code => $code,
02298d3a 332 reverse => "$component_class->$method",
333 class => $component_class,
e540158b 334 namespace => Catalyst::Utils::class2prefix(
df960201 335 $component_class, ref($c)->config->{case_sensitive}
e540158b 336 ),
337 }
338 );
339 }
340 else {
341 my $error =
02298d3a 342 qq/Couldn't forward to "$component_class". Does not implement "$method"/;
e540158b 343 $c->error($error);
344 $c->log->debug($error)
345 if $c->debug;
346 return 0;
347 }
adb53907 348}
349
b5ecfcf0 350=head2 $self->prepare_action($c)
fbcc39ad 351
4ab87e27 352Find an dispatch type that matches $c->req->path, and set args from it.
353
fbcc39ad 354=cut
355
356sub prepare_action {
357 my ( $self, $c ) = @_;
e63bdf38 358 my $req = $c->req;
359 my $path = $req->path;
360 my @path = split /\//, $req->path;
361 $req->args( \my @args );
fbcc39ad 362
61a9002d 363 unshift( @path, '' ); # Root action
78d760bb 364
b96f127f 365 DESCEND: while (@path) {
fbcc39ad 366 $path = join '/', @path;
5299fff8 367 $path =~ s#^/+##;
78d760bb 368
22f3a8dd 369 # Check out dispatch types to see if any will handle the path at
370 # this level
371
a2aac3b8 372 foreach my $type ( @{ $self->dispatch_types } ) {
2633d7dc 373 last DESCEND if $type->match( $c, $path );
66e28e3f 374 }
b96f127f 375
22f3a8dd 376 # If not, move the last part path to args
4082e678 377 my $arg = pop(@path);
378 $arg =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
379 unshift @args, $arg;
fbcc39ad 380 }
381
e63bdf38 382 s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg for grep { defined } @{$req->captures||[]};
66d7ad40 383
e63bdf38 384 $c->log->debug( 'Path is "' . $req->match . '"' )
076bfad3 385 if ( $c->debug && defined $req->match && length $req->match );
e3a13771 386
fbcc39ad 387 $c->log->debug( 'Arguments are "' . join( '/', @args ) . '"' )
388 if ( $c->debug && @args );
389}
390
b5ecfcf0 391=head2 $self->get_action( $action, $namespace )
1abd6db7 392
4ab87e27 393returns a named action from a given namespace.
394
1abd6db7 395=cut
396
397sub get_action {
bcd1002b 398 my ( $self, $name, $namespace ) = @_;
79a3189a 399 return unless $name;
3d0d6d21 400
2f381252 401 $namespace = join( "/", grep { length } split '/', ( defined $namespace ? $namespace : "" ) );
99fe1710 402
c41cfce3 403 return $self->_action_hash->{"${namespace}/${name}"};
1abd6db7 404}
405
b0ad47c1 406=head2 $self->get_action_by_path( $path );
068c0898 407
ab990582 408Returns the named action by its full private path.
3d0d6d21 409
068c0898 410=cut
3d0d6d21 411
412sub get_action_by_path {
413 my ( $self, $path ) = @_;
ea0e58d9 414 $path =~ s/^\///;
28928de9 415 $path = "/$path" unless $path =~ /\//;
c41cfce3 416 $self->_action_hash->{$path};
3d0d6d21 417}
418
b5ecfcf0 419=head2 $self->get_actions( $c, $action, $namespace )
a9dc674c 420
421=cut
422
423sub get_actions {
424 my ( $self, $c, $action, $namespace ) = @_;
425 return [] unless $action;
3d0d6d21 426
28928de9 427 $namespace = join( "/", grep { length } split '/', $namespace || "" );
a9dc674c 428
429 my @match = $self->get_containers($namespace);
430
684d10ed 431 return map { $_->get_action($action) } @match;
a9dc674c 432}
433
b5ecfcf0 434=head2 $self->get_containers( $namespace )
cfd04b0c 435
4ab87e27 436Return all the action containers for a given namespace, inclusive
437
cfd04b0c 438=cut
439
440sub get_containers {
441 my ( $self, $namespace ) = @_;
a13e21ab 442 $namespace ||= '';
443 $namespace = '' if $namespace eq '/';
cfd04b0c 444
a13e21ab 445 my @containers;
cfd04b0c 446
7f23827b 447 if ( length $namespace ) {
448 do {
c41cfce3 449 push @containers, $self->_container_hash->{$namespace};
7f23827b 450 } while ( $namespace =~ s#/[^/]+$## );
451 }
90ce41ba 452
c41cfce3 453 return reverse grep { defined } @containers, $self->_container_hash->{''};
cfd04b0c 454}
455
ea0e58d9 456=head2 $self->uri_for_action($action, \@captures)
457
458Takes a Catalyst::Action object and action parameters and returns a URI
459part such that if $c->req->path were this URI part, this action would be
460dispatched to with $c->req->captures set to the supplied arrayref.
461
462If the action object is not available for external dispatch or the dispatcher
463cannot determine an appropriate URI, this method will return undef.
464
465=cut
466
467sub uri_for_action {
468 my ( $self, $action, $captures) = @_;
469 $captures ||= [];
a2aac3b8 470 foreach my $dispatch_type ( @{ $self->dispatch_types } ) {
ea0e58d9 471 my $uri = $dispatch_type->uri_for_action( $action, $captures );
81e75875 472 return( $uri eq '' ? '/' : $uri )
473 if defined($uri);
ea0e58d9 474 }
475 return undef;
476}
477
8f5a2bd9 478=head2 expand_action
ae0e35ee 479
480expand an action into a full representation of the dispatch.
481mostly useful for chained, other actions will just return a
482single action.
483
484=cut
485
52f71256 486sub expand_action {
487 my ($self, $action) = @_;
488
a2aac3b8 489 foreach my $dispatch_type (@{ $self->dispatch_types }) {
52f71256 490 my $expanded = $dispatch_type->expand_action($action);
491 return $expanded if $expanded;
492 }
493
494 return $action;
495}
496
b5ecfcf0 497=head2 $self->register( $c, $action )
aad72cc9 498
4ab87e27 499Make sure all required dispatch types for this action are loaded, then
500pass the action to our dispatch types so they can register it if required.
501Also, set up the tree with the action containers.
502
aad72cc9 503=cut
504
79a3189a 505sub register {
506 my ( $self, $c, $action ) = @_;
507
c41cfce3 508 my $registered = $self->_registered_dispatch_types;
694d15f1 509
694d15f1 510 foreach my $key ( keys %{ $action->attributes } ) {
9a6ecf4f 511 next if $key eq 'Private';
694d15f1 512 my $class = "Catalyst::DispatchType::$key";
513 unless ( $registered->{$class} ) {
c41cfce3 514 # FIXME - Some error checking and re-throwing needed here, as
515 # we eat exceptions loading dispatch types.
068c0898 516 eval { Class::MOP::load_class($class) };
a2aac3b8 517 push( @{ $self->dispatch_types }, $class->new ) unless $@;
694d15f1 518 $registered->{$class} = 1;
519 }
520 }
521
a2aac3b8 522 my @dtypes = @{ $self->dispatch_types };
1315d253 523 my @normal_dtypes;
524 my @low_precedence_dtypes;
525
7b442de5 526 for my $type ( @dtypes ) {
c4586fd0 527 if ($type->_is_low_precedence) {
1315d253 528 push @low_precedence_dtypes, $type;
529 } else {
530 push @normal_dtypes, $type;
531 }
532 }
533
694d15f1 534 # Pass the action to our dispatch types so they can register it if reqd.
1315d253 535 my $was_registered = 0;
536 foreach my $type ( @normal_dtypes ) {
537 $was_registered = 1 if $type->register( $c, $action );
538 }
539
540 if (not $was_registered) {
541 foreach my $type ( @low_precedence_dtypes ) {
542 $type->register( $c, $action );
543 }
694d15f1 544 }
545
79a3189a 546 my $namespace = $action->namespace;
a13e21ab 547 my $name = $action->name;
c7116517 548
ad5e4650 549 my $container = $self->_find_or_create_action_container($namespace);
15e9b5dd 550
551 # Set the method value
a13e21ab 552 $container->add_action($action);
c7116517 553
c41cfce3 554 $self->_action_hash->{"$namespace/$name"} = $action;
555 $self->_container_hash->{$namespace} = $container;
15e9b5dd 556}
557
ad5e4650 558sub _find_or_create_action_container {
a13e21ab 559 my ( $self, $namespace ) = @_;
560
c41cfce3 561 my $tree ||= $self->_tree;
99fe1710 562
a13e21ab 563 return $tree->getNodeValue unless $namespace;
78d760bb 564
a13e21ab 565 my @namespace = split '/', $namespace;
566 return $self->_find_or_create_namespace_node( $tree, @namespace )
567 ->getNodeValue;
8505565b 568}
90ce41ba 569
8505565b 570sub _find_or_create_namespace_node {
a13e21ab 571 my ( $self, $parent, $part, @namespace ) = @_;
78d760bb 572
a13e21ab 573 return $parent unless $part;
8505565b 574
a13e21ab 575 my $child =
576 ( grep { $_->getNodeValue->part eq $part } $parent->getAllChildren )[0];
8505565b 577
a13e21ab 578 unless ($child) {
579 my $container = Catalyst::ActionContainer->new($part);
580 $parent->addChild( $child = Tree::Simple->new($container) );
581 }
99fe1710 582
a13e21ab 583 $self->_find_or_create_namespace_node( $child, @namespace );
1abd6db7 584}
585
4ab87e27 586=head2 $self->setup_actions( $class, $context )
587
965f3e35 588Loads all of the pre-load dispatch types, registers their actions and then
589loads all of the post-load dispatch types, and iterates over the tree of
8f59bbe2 590actions, displaying the debug information if appropriate.
1abd6db7 591
592=cut
593
594sub setup_actions {
11bd4e3e 595 my ( $self, $c ) = @_;
99fe1710 596
9e81ba44 597 my @classes =
ad5e4650 598 $self->_load_dispatch_types( @{ $self->preload_dispatch_types } );
c41cfce3 599 @{ $self->_registered_dispatch_types }{@classes} = (1) x @classes;
b96f127f 600
49070d25 601 foreach my $comp ( values %{ $c->components } ) {
602 $comp->register_actions($c) if $comp->can('register_actions');
1abd6db7 603 }
e494bd6b 604
ad5e4650 605 $self->_load_dispatch_types( @{ $self->postload_dispatch_types } );
6d030e6f 606
11bd4e3e 607 return unless $c->debug;
2eb2c42f 608 $self->_display_action_tables($c);
609}
610
611sub _display_action_tables {
612 my ($self, $c) = @_;
99fe1710 613
48d435ba 614 my $avail_width = Catalyst::Utils::term_width() - 12;
615 my $col1_width = ($avail_width * .25) < 20 ? 20 : int($avail_width * .25);
616 my $col2_width = ($avail_width * .50) < 36 ? 36 : int($avail_width * .50);
617 my $col3_width = $avail_width - $col1_width - $col2_width;
684d10ed 618 my $privates = Text::SimpleTable->new(
48d435ba 619 [ $col1_width, 'Private' ], [ $col2_width, 'Class' ], [ $col3_width, 'Method' ]
684d10ed 620 );
99fe1710 621
87b85407 622 my $has_private = 0;
1abd6db7 623 my $walker = sub {
624 my ( $walker, $parent, $prefix ) = @_;
625 $prefix .= $parent->getNodeValue || '';
626 $prefix .= '/' unless $prefix =~ /\/$/;
b7aebc12 627 my $node = $parent->getNodeValue->actions;
99fe1710 628
78d760bb 629 for my $action ( keys %{$node} ) {
b7aebc12 630 my $action_obj = $node->{$action};
b0bb11ec 631 next
632 if ( ( $action =~ /^_.*/ )
633 && ( !$c->config->{show_internal_actions} ) );
684d10ed 634 $privates->row( "$prefix$action", $action_obj->class, $action );
87b85407 635 $has_private = 1;
1abd6db7 636 }
99fe1710 637
1abd6db7 638 $walker->( $walker, $_, $prefix ) for $parent->getAllChildren;
639 };
99fe1710 640
c41cfce3 641 $walker->( $walker, $self->_tree, '' );
1cf0345b 642 $c->log->debug( "Loaded Private actions:\n" . $privates->draw . "\n" )
643 if $has_private;
99fe1710 644
a9cbd748 645 # List all public actions
a2aac3b8 646 $_->list($c) for @{ $self->dispatch_types };
1abd6db7 647}
648
ad5e4650 649sub _load_dispatch_types {
9e81ba44 650 my ( $self, @types ) = @_;
651
652 my @loaded;
9e81ba44 653 # Preload action types
654 for my $type (@types) {
5d8129e9 655 # first param is undef because we cannot get the appclass
656 my $class = Catalyst::Utils::resolve_namespace(undef, 'Catalyst::DispatchType', $type);
2efad04b 657
068c0898 658 eval { Class::MOP::load_class($class) };
9e81ba44 659 Catalyst::Exception->throw( message => qq/Couldn't load "$class"/ )
660 if $@;
a2aac3b8 661 push @{ $self->dispatch_types }, $class->new;
9e81ba44 662
663 push @loaded, $class;
664 }
665
a13e21ab 666 return @loaded;
9e81ba44 667}
668
e995c634 669=head2 $self->dispatch_type( $type )
670
671Get the DispatchType object of the relevant type, i.e. passing C<$type> of
672C<Chained> would return a L<Catalyst::DispatchType::Chained> object (assuming
b0ad47c1 673of course it's being used.)
e995c634 674
675=cut
676
7ffc9d9d 677sub dispatch_type {
678 my ($self, $name) = @_;
196932de 679
5d8129e9 680 # first param is undef because we cannot get the appclass
681 $name = Catalyst::Utils::resolve_namespace(undef, 'Catalyst::DispatchType', $name);
7ffc9d9d 682
a2aac3b8 683 for (@{ $self->dispatch_types }) {
7ffc9d9d 684 return $_ if ref($_) eq $name;
685 }
686 return undef;
687}
688
c41cfce3 689use Moose;
690
691# 5.70 backwards compatibility hacks.
692
693# Various plugins (e.g. Plugin::Server and Plugin::Authorization::ACL)
694# need the methods here which *should* be private..
695
2efad04b 696# You should be able to use get_actions or get_containers appropriately
697# instead of relying on these methods which expose implementation details
698# of the dispatcher..
699#
700# IRC backlog included below, please come ask if this doesn't work for you.
701#
702# <@t0m> 5.80, the state of. There are things in the dispatcher which have
703# been deprecated, that we yell at anyone for using, which there isn't
704# a good alternative for yet..
705# <@mst> er, get_actions/get_containers provides that doesn't it?
706# <@mst> DispatchTypes are loaded on demand anyway
707# <@t0m> I'm thinking of things like _tree which is aliased to 'tree' with
708# warnings otherwise shit breaks.. We're issuing warnings about the
709# correct set of things which you shouldn't be calling..
710# <@mst> right
711# <@mst> basically, I don't see there's a need for a replacement for anything
712# <@mst> it was never a good idea to call ->tree
713# <@mst> nothingmuch was the only one who did AFAIK
714# <@mst> and he admitted it was a hack ;)
715
c41cfce3 716# See also t/lib/TestApp/Plugin/AddDispatchTypes.pm
717
718# Alias _method_name to method_name, add a before modifier to warn..
b0ad47c1 719foreach my $public_method_name (qw/
720 tree
b0ad47c1 721 registered_dispatch_types
722 method_action_class
723 action_hash
c41cfce3 724 container_hash
725 /) {
726 my $private_method_name = '_' . $public_method_name;
727 my $meta = __PACKAGE__->meta; # Calling meta method here fine as we happen at compile time.
728 $meta->add_method($public_method_name, $meta->get_method($private_method_name));
729 {
730 my %package_hash; # Only warn once per method, per package. These are infrequent enough that
731 # I haven't provided a way to disable them, patches welcome.
732 $meta->add_before_method_modifier($public_method_name, sub {
3cd3bc6a 733 my $class = caller(2);
734 chomp($class);
b3f7d10b 735 $package_hash{$class}++ || do {
3cd3bc6a 736 warn("Class $class is calling the deprecated method\n"
737 . " Catalyst::Dispatcher::$public_method_name,\n"
dacd8b0e 738 . " this will be removed in Catalyst 5.9\n");
c41cfce3 739 };
740 });
741 }
742}
743# End 5.70 backwards compatibility hacks.
744
e5ecd5bc 745__PACKAGE__->meta->make_immutable;
746
059c085b 747=head2 meta
748
749Provided by Moose
750
2f381252 751=head1 AUTHORS
1abd6db7 752
2f381252 753Catalyst Contributors, see Catalyst.pm
1abd6db7 754
755=head1 COPYRIGHT
756
536bee89 757This library is free software. You can redistribute it and/or modify it under
1abd6db7 758the same terms as Perl itself.
759
760=cut
761
7621;