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