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