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