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