forwarding to "AppTest" without action should go to the "process" method
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Dispatcher.pm
CommitLineData
68a748b9 1package Catalyst::Dispatcher;
1abd6db7 2
059c085b 3use Moose;
068c0898 4use Class::MOP;
10954d1d 5with 'MooseX::Emulate::Class::Accessor::Fast';
059c085b 6
a2f2cde9 7use Catalyst::Exception;
f05af9ba 8use Catalyst::Utils;
fbcc39ad 9use Catalyst::Action;
b7aebc12 10use Catalyst::ActionContainer;
b96f127f 11use Catalyst::DispatchType::Default;
bcccee4e 12use Catalyst::DispatchType::Index;
39fc2ce1 13use Catalyst::Utils;
87b85407 14use Text::SimpleTable;
1abd6db7 15use Tree::Simple;
16use Tree::Simple::Visitor::FindByPath;
17
792d99b4 18use namespace::clean -except => 'meta';
6f3df815 19
c41cfce3 20# Refactoring note:
21# do these belong as package vars or should we build these via a builder method?
22# See Catalyst-Plugin-Server for them being added to, which should be much less ugly.
23
6d030e6f 24# Preload these action types
61a9002d 25our @PRELOAD = qw/Index Path Regex/;
1abd6db7 26
2d1d8f91 27# Postload these action types
61a9002d 28our @POSTLOAD = qw/Default/;
2d1d8f91 29
c41cfce3 30# Note - see back-compat methods at end of file.
792d99b4 31has _tree => (is => 'rw', builder => '_build__tree');
5b8ac6cc 32has dispatch_types => (is => 'rw', default => sub { [] }, required => 1, lazy => 1);
c41cfce3 33has _registered_dispatch_types => (is => 'rw', default => sub { {} }, required => 1, lazy => 1);
34has _method_action_class => (is => 'rw', default => 'Catalyst::Action');
35has _action_hash => (is => 'rw', required => 1, lazy => 1, default => sub { {} });
36has _container_hash => (is => 'rw', required => 1, lazy => 1, default => sub { {} });
7ffc9d9d 37
11ff9b94 38my %dispatch_types = ( pre => \@PRELOAD, post => \@POSTLOAD );
39foreach my $type (keys %dispatch_types) {
40 has $type . "load_dispatch_types" => (
41 is => 'rw', required => 1, lazy => 1, default => sub { $dispatch_types{$type} },
42 traits => ['MooseX::Emulate::Class::Accessor::Fast::Meta::Role::Attribute'], # List assignment is CAF style
43 );
44}
083ee5d9 45
1abd6db7 46=head1 NAME
47
9c053379 48Catalyst::Dispatcher - The Catalyst Dispatcher
1abd6db7 49
50=head1 SYNOPSIS
51
52See L<Catalyst>.
53
54=head1 DESCRIPTION
55
4ab87e27 56This is the class that maps public urls to actions in your Catalyst
57application based on the attributes you set.
58
1abd6db7 59=head1 METHODS
60
b0ad47c1 61=head2 new
4ab87e27 62
63Construct a new dispatcher.
64
e7bb8d33 65=cut
66
792d99b4 67sub _build__tree {
68 my ($self) = @_;
9e81ba44 69
068c0898 70 my $container =
059c085b 71 Catalyst::ActionContainer->new( { part => '/', actions => {} } );
a13e21ab 72
792d99b4 73 return Tree::Simple->new($container, Tree::Simple->ROOT);
e7bb8d33 74}
75
76=head2 $self->preload_dispatch_types
77
78An arrayref of pre-loaded dispatchtype classes
79
80Entries are considered to be available as C<Catalyst::DispatchType::CLASS>
81To use a custom class outside the regular C<Catalyst> namespace, prefix
82it with a C<+>, like so:
83
84 +My::Dispatch::Type
85
86=head2 $self->postload_dispatch_types
87
88An arrayref of post-loaded dispatchtype classes
89
90Entries are considered to be available as C<Catalyst::DispatchType::CLASS>
91To use a custom class outside the regular C<Catalyst> namespace, prefix
92it with a C<+>, like so:
93
94 +My::Dispatch::Type
95
b5ecfcf0 96=head2 $self->dispatch($c)
1abd6db7 97
4ab87e27 98Delegate the dispatch to the action that matched the url, or return a
99message about unknown resource
100
1abd6db7 101=cut
102
103sub dispatch {
fbcc39ad 104 my ( $self, $c ) = @_;
e63bdf38 105 if ( my $action = $c->action ) {
106 $c->forward( join( '/', '', $action->namespace, '_DISPATCH' ) );
fbcc39ad 107 }
fbcc39ad 108 else {
1abd6db7 109 my $path = $c->req->path;
110 my $error = $path
111 ? qq/Unknown resource "$path"/
112 : "No default action defined";
113 $c->log->error($error) if $c->debug;
114 $c->error($error);
115 }
116}
117
2f381252 118# $self->_command2action( $c, $command [, \@arguments ] )
b456f8f3 119# $self->_command2action( $c, $command [, \@captures, \@arguments ] )
120# Search for an action, from the command and returns C<($action, $args, $captures)> on
2f381252 121# success. Returns C<(0)> on error.
1abd6db7 122
2f381252 123sub _command2action {
e72f8f51 124 my ( $self, $c, $command, @extra_params ) = @_;
99fe1710 125
1abd6db7 126 unless ($command) {
2f381252 127 $c->log->debug('Nothing to go to') if $c->debug;
1abd6db7 128 return 0;
129 }
99fe1710 130
b456f8f3 131 my (@args, @captures);
132
133 if ( ref( $extra_params[-2] ) eq 'ARRAY' ) {
e1e81442 134 @captures = @{ splice @extra_params, -2, 1 };
b456f8f3 135 }
068c0898 136
e72f8f51 137 if ( ref( $extra_params[-1] ) eq 'ARRAY' ) {
138 @args = @{ pop @extra_params }
139 } else {
2f381252 140 # this is a copy, it may take some abuse from
141 # ->_invoke_as_path if the path had trailing parts
e72f8f51 142 @args = @{ $c->request->arguments };
143 }
144
145 my $action;
146
2f381252 147 # go to a string path ("/foo/bar/gorch")
e31b525c 148 # or action object
7e95ba12 149 if (blessed($command) && $command->isa('Catalyst::Action')) {
e31b525c 150 $action = $command;
151 }
152 else {
153 $action = $self->_invoke_as_path( $c, "$command", \@args );
154 }
99fe1710 155
2f381252 156 # go to a component ( "MyApp::*::Foo" or $c->component("...")
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
330b9123 322 if( $component_or_class eq blessed($c->application) ){
323 my $possible_action = $c->application->action_for($method);
324 return $possible_action if $possible_action;
bbc249c9 325 if( my $code = $c->application->can($method) ){
326 return $self->_method_action_class->new(
327 {
328 name => $method,
329 code => $code,
330 reverse => "$component_or_class->$method",
331 class => $component_or_class,
332 namespace => Catalyst::Utils::class2prefix(
333 $component_or_class
334 ),
335 }
336 );
337 }
330b9123 338 }
02298d3a 339 my $component = $self->_find_component($c, $component_or_class);
340 my $component_class = blessed $component || return 0;
e540158b 341
02298d3a 342 if (my $code = $component_class->can('action_for')) {
343 my $possible_action = $component->$code($method);
84c28acb 344 return $possible_action if $possible_action;
345 }
346
02298d3a 347 if ( my $code = $component_class->can($method) ) {
c41cfce3 348 return $self->_method_action_class->new(
e540158b 349 {
350 name => $method,
351 code => $code,
02298d3a 352 reverse => "$component_class->$method",
353 class => $component_class,
40a21eff 354 namespace => Catalyst::Utils::class2prefix( $component_class ),
e540158b 355 }
356 );
357 }
358 else {
359 my $error =
02298d3a 360 qq/Couldn't forward to "$component_class". Does not implement "$method"/;
e540158b 361 $c->error($error);
362 $c->log->debug($error)
363 if $c->debug;
364 return 0;
365 }
adb53907 366}
367
b5ecfcf0 368=head2 $self->prepare_action($c)
fbcc39ad 369
4ab87e27 370Find an dispatch type that matches $c->req->path, and set args from it.
371
fbcc39ad 372=cut
373
374sub prepare_action {
375 my ( $self, $c ) = @_;
e63bdf38 376 my $req = $c->req;
377 my $path = $req->path;
378 my @path = split /\//, $req->path;
379 $req->args( \my @args );
fbcc39ad 380
61a9002d 381 unshift( @path, '' ); # Root action
78d760bb 382
b96f127f 383 DESCEND: while (@path) {
fbcc39ad 384 $path = join '/', @path;
5299fff8 385 $path =~ s#^/+##;
78d760bb 386
22f3a8dd 387 # Check out dispatch types to see if any will handle the path at
388 # this level
389
a2aac3b8 390 foreach my $type ( @{ $self->dispatch_types } ) {
2633d7dc 391 last DESCEND if $type->match( $c, $path );
66e28e3f 392 }
b96f127f 393
22f3a8dd 394 # If not, move the last part path to args
4082e678 395 my $arg = pop(@path);
396 $arg =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
397 unshift @args, $arg;
fbcc39ad 398 }
399
e63bdf38 400 s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg for grep { defined } @{$req->captures||[]};
66d7ad40 401
e63bdf38 402 $c->log->debug( 'Path is "' . $req->match . '"' )
076bfad3 403 if ( $c->debug && defined $req->match && length $req->match );
e3a13771 404
fbcc39ad 405 $c->log->debug( 'Arguments are "' . join( '/', @args ) . '"' )
406 if ( $c->debug && @args );
407}
408
b5ecfcf0 409=head2 $self->get_action( $action, $namespace )
1abd6db7 410
4ab87e27 411returns a named action from a given namespace.
412
1abd6db7 413=cut
414
415sub get_action {
bcd1002b 416 my ( $self, $name, $namespace ) = @_;
79a3189a 417 return unless $name;
3d0d6d21 418
2f381252 419 $namespace = join( "/", grep { length } split '/', ( defined $namespace ? $namespace : "" ) );
99fe1710 420
c41cfce3 421 return $self->_action_hash->{"${namespace}/${name}"};
1abd6db7 422}
423
b0ad47c1 424=head2 $self->get_action_by_path( $path );
068c0898 425
ab990582 426Returns the named action by its full private path.
3d0d6d21 427
068c0898 428=cut
3d0d6d21 429
430sub get_action_by_path {
431 my ( $self, $path ) = @_;
ea0e58d9 432 $path =~ s/^\///;
28928de9 433 $path = "/$path" unless $path =~ /\//;
c41cfce3 434 $self->_action_hash->{$path};
3d0d6d21 435}
436
b5ecfcf0 437=head2 $self->get_actions( $c, $action, $namespace )
a9dc674c 438
439=cut
440
441sub get_actions {
442 my ( $self, $c, $action, $namespace ) = @_;
443 return [] unless $action;
3d0d6d21 444
28928de9 445 $namespace = join( "/", grep { length } split '/', $namespace || "" );
a9dc674c 446
447 my @match = $self->get_containers($namespace);
448
684d10ed 449 return map { $_->get_action($action) } @match;
a9dc674c 450}
451
b5ecfcf0 452=head2 $self->get_containers( $namespace )
cfd04b0c 453
4ab87e27 454Return all the action containers for a given namespace, inclusive
455
cfd04b0c 456=cut
457
458sub get_containers {
459 my ( $self, $namespace ) = @_;
a13e21ab 460 $namespace ||= '';
461 $namespace = '' if $namespace eq '/';
cfd04b0c 462
a13e21ab 463 my @containers;
cfd04b0c 464
7f23827b 465 if ( length $namespace ) {
466 do {
c41cfce3 467 push @containers, $self->_container_hash->{$namespace};
7f23827b 468 } while ( $namespace =~ s#/[^/]+$## );
469 }
90ce41ba 470
c41cfce3 471 return reverse grep { defined } @containers, $self->_container_hash->{''};
cfd04b0c 472}
473
ea0e58d9 474=head2 $self->uri_for_action($action, \@captures)
475
476Takes a Catalyst::Action object and action parameters and returns a URI
477part such that if $c->req->path were this URI part, this action would be
478dispatched to with $c->req->captures set to the supplied arrayref.
479
480If the action object is not available for external dispatch or the dispatcher
481cannot determine an appropriate URI, this method will return undef.
482
483=cut
484
485sub uri_for_action {
486 my ( $self, $action, $captures) = @_;
487 $captures ||= [];
a2aac3b8 488 foreach my $dispatch_type ( @{ $self->dispatch_types } ) {
ea0e58d9 489 my $uri = $dispatch_type->uri_for_action( $action, $captures );
81e75875 490 return( $uri eq '' ? '/' : $uri )
491 if defined($uri);
ea0e58d9 492 }
493 return undef;
494}
495
8f5a2bd9 496=head2 expand_action
ae0e35ee 497
498expand an action into a full representation of the dispatch.
499mostly useful for chained, other actions will just return a
500single action.
501
502=cut
503
52f71256 504sub expand_action {
505 my ($self, $action) = @_;
506
a2aac3b8 507 foreach my $dispatch_type (@{ $self->dispatch_types }) {
52f71256 508 my $expanded = $dispatch_type->expand_action($action);
509 return $expanded if $expanded;
510 }
511
512 return $action;
513}
514
b5ecfcf0 515=head2 $self->register( $c, $action )
aad72cc9 516
4ab87e27 517Make sure all required dispatch types for this action are loaded, then
518pass the action to our dispatch types so they can register it if required.
519Also, set up the tree with the action containers.
520
aad72cc9 521=cut
522
79a3189a 523sub register {
524 my ( $self, $c, $action ) = @_;
525
c41cfce3 526 my $registered = $self->_registered_dispatch_types;
694d15f1 527
e63bdf38 528 #my $priv = 0; #seems to be unused
694d15f1 529 foreach my $key ( keys %{ $action->attributes } ) {
9a6ecf4f 530 next if $key eq 'Private';
694d15f1 531 my $class = "Catalyst::DispatchType::$key";
532 unless ( $registered->{$class} ) {
c41cfce3 533 # FIXME - Some error checking and re-throwing needed here, as
534 # we eat exceptions loading dispatch types.
068c0898 535 eval { Class::MOP::load_class($class) };
a2aac3b8 536 push( @{ $self->dispatch_types }, $class->new ) unless $@;
694d15f1 537 $registered->{$class} = 1;
538 }
539 }
540
a2aac3b8 541 my @dtypes = @{ $self->dispatch_types };
1315d253 542 my @normal_dtypes;
543 my @low_precedence_dtypes;
544
7b442de5 545 for my $type ( @dtypes ) {
c4586fd0 546 if ($type->_is_low_precedence) {
1315d253 547 push @low_precedence_dtypes, $type;
548 } else {
549 push @normal_dtypes, $type;
550 }
551 }
552
694d15f1 553 # Pass the action to our dispatch types so they can register it if reqd.
1315d253 554 my $was_registered = 0;
555 foreach my $type ( @normal_dtypes ) {
556 $was_registered = 1 if $type->register( $c, $action );
557 }
558
559 if (not $was_registered) {
560 foreach my $type ( @low_precedence_dtypes ) {
561 $type->register( $c, $action );
562 }
694d15f1 563 }
564
79a3189a 565 my $namespace = $action->namespace;
a13e21ab 566 my $name = $action->name;
c7116517 567
ad5e4650 568 my $container = $self->_find_or_create_action_container($namespace);
15e9b5dd 569
570 # Set the method value
a13e21ab 571 $container->add_action($action);
c7116517 572
c41cfce3 573 $self->_action_hash->{"$namespace/$name"} = $action;
574 $self->_container_hash->{$namespace} = $container;
15e9b5dd 575}
576
ad5e4650 577sub _find_or_create_action_container {
a13e21ab 578 my ( $self, $namespace ) = @_;
579
c41cfce3 580 my $tree ||= $self->_tree;
99fe1710 581
a13e21ab 582 return $tree->getNodeValue unless $namespace;
78d760bb 583
a13e21ab 584 my @namespace = split '/', $namespace;
585 return $self->_find_or_create_namespace_node( $tree, @namespace )
586 ->getNodeValue;
8505565b 587}
90ce41ba 588
8505565b 589sub _find_or_create_namespace_node {
a13e21ab 590 my ( $self, $parent, $part, @namespace ) = @_;
78d760bb 591
a13e21ab 592 return $parent unless $part;
8505565b 593
a13e21ab 594 my $child =
595 ( grep { $_->getNodeValue->part eq $part } $parent->getAllChildren )[0];
8505565b 596
a13e21ab 597 unless ($child) {
598 my $container = Catalyst::ActionContainer->new($part);
599 $parent->addChild( $child = Tree::Simple->new($container) );
600 }
99fe1710 601
a13e21ab 602 $self->_find_or_create_namespace_node( $child, @namespace );
1abd6db7 603}
604
4ab87e27 605=head2 $self->setup_actions( $class, $context )
606
8f5a2bd9 607Loads all of the preload dispatch types, registers their actions and then
8f59bbe2 608loads all of the postload dispatch types, and iterates over the tree of
609actions, displaying the debug information if appropriate.
1abd6db7 610
611=cut
612
613sub setup_actions {
11bd4e3e 614 my ( $self, $c ) = @_;
99fe1710 615
9e81ba44 616 my @classes =
ad5e4650 617 $self->_load_dispatch_types( @{ $self->preload_dispatch_types } );
c41cfce3 618 @{ $self->_registered_dispatch_types }{@classes} = (1) x @classes;
b96f127f 619
49070d25 620 foreach my $comp ( values %{ $c->components } ) {
621 $comp->register_actions($c) if $comp->can('register_actions');
1abd6db7 622 }
e494bd6b 623
ad5e4650 624 $self->_load_dispatch_types( @{ $self->postload_dispatch_types } );
6d030e6f 625
11bd4e3e 626 return unless $c->debug;
2eb2c42f 627 $self->_display_action_tables($c);
628}
629
630sub _display_action_tables {
631 my ($self, $c) = @_;
99fe1710 632
48d435ba 633 my $avail_width = Catalyst::Utils::term_width() - 12;
634 my $col1_width = ($avail_width * .25) < 20 ? 20 : int($avail_width * .25);
635 my $col2_width = ($avail_width * .50) < 36 ? 36 : int($avail_width * .50);
636 my $col3_width = $avail_width - $col1_width - $col2_width;
684d10ed 637 my $privates = Text::SimpleTable->new(
48d435ba 638 [ $col1_width, 'Private' ], [ $col2_width, 'Class' ], [ $col3_width, 'Method' ]
684d10ed 639 );
99fe1710 640
87b85407 641 my $has_private = 0;
1abd6db7 642 my $walker = sub {
643 my ( $walker, $parent, $prefix ) = @_;
644 $prefix .= $parent->getNodeValue || '';
645 $prefix .= '/' unless $prefix =~ /\/$/;
b7aebc12 646 my $node = $parent->getNodeValue->actions;
99fe1710 647
78d760bb 648 for my $action ( keys %{$node} ) {
b7aebc12 649 my $action_obj = $node->{$action};
b0bb11ec 650 next
651 if ( ( $action =~ /^_.*/ )
652 && ( !$c->config->{show_internal_actions} ) );
684d10ed 653 $privates->row( "$prefix$action", $action_obj->class, $action );
87b85407 654 $has_private = 1;
1abd6db7 655 }
99fe1710 656
1abd6db7 657 $walker->( $walker, $_, $prefix ) for $parent->getAllChildren;
658 };
99fe1710 659
c41cfce3 660 $walker->( $walker, $self->_tree, '' );
1cf0345b 661 $c->log->debug( "Loaded Private actions:\n" . $privates->draw . "\n" )
662 if $has_private;
99fe1710 663
a9cbd748 664 # List all public actions
a2aac3b8 665 $_->list($c) for @{ $self->dispatch_types };
1abd6db7 666}
667
ad5e4650 668sub _load_dispatch_types {
9e81ba44 669 my ( $self, @types ) = @_;
670
671 my @loaded;
9e81ba44 672 # Preload action types
673 for my $type (@types) {
5d8129e9 674 # first param is undef because we cannot get the appclass
675 my $class = Catalyst::Utils::resolve_namespace(undef, 'Catalyst::DispatchType', $type);
2efad04b 676
068c0898 677 eval { Class::MOP::load_class($class) };
9e81ba44 678 Catalyst::Exception->throw( message => qq/Couldn't load "$class"/ )
679 if $@;
a2aac3b8 680 push @{ $self->dispatch_types }, $class->new;
9e81ba44 681
682 push @loaded, $class;
683 }
684
a13e21ab 685 return @loaded;
9e81ba44 686}
687
e995c634 688=head2 $self->dispatch_type( $type )
689
690Get the DispatchType object of the relevant type, i.e. passing C<$type> of
691C<Chained> would return a L<Catalyst::DispatchType::Chained> object (assuming
b0ad47c1 692of course it's being used.)
e995c634 693
694=cut
695
7ffc9d9d 696sub dispatch_type {
697 my ($self, $name) = @_;
196932de 698
5d8129e9 699 # first param is undef because we cannot get the appclass
700 $name = Catalyst::Utils::resolve_namespace(undef, 'Catalyst::DispatchType', $name);
7ffc9d9d 701
a2aac3b8 702 for (@{ $self->dispatch_types }) {
7ffc9d9d 703 return $_ if ref($_) eq $name;
704 }
705 return undef;
706}
707
c41cfce3 708use Moose;
709
710# 5.70 backwards compatibility hacks.
711
712# Various plugins (e.g. Plugin::Server and Plugin::Authorization::ACL)
713# need the methods here which *should* be private..
714
2efad04b 715# You should be able to use get_actions or get_containers appropriately
716# instead of relying on these methods which expose implementation details
717# of the dispatcher..
718#
719# IRC backlog included below, please come ask if this doesn't work for you.
720#
721# <@t0m> 5.80, the state of. There are things in the dispatcher which have
722# been deprecated, that we yell at anyone for using, which there isn't
723# a good alternative for yet..
724# <@mst> er, get_actions/get_containers provides that doesn't it?
725# <@mst> DispatchTypes are loaded on demand anyway
726# <@t0m> I'm thinking of things like _tree which is aliased to 'tree' with
727# warnings otherwise shit breaks.. We're issuing warnings about the
728# correct set of things which you shouldn't be calling..
729# <@mst> right
730# <@mst> basically, I don't see there's a need for a replacement for anything
731# <@mst> it was never a good idea to call ->tree
732# <@mst> nothingmuch was the only one who did AFAIK
733# <@mst> and he admitted it was a hack ;)
734
c41cfce3 735# See also t/lib/TestApp/Plugin/AddDispatchTypes.pm
736
737# Alias _method_name to method_name, add a before modifier to warn..
b0ad47c1 738foreach my $public_method_name (qw/
739 tree
b0ad47c1 740 registered_dispatch_types
741 method_action_class
742 action_hash
c41cfce3 743 container_hash
744 /) {
745 my $private_method_name = '_' . $public_method_name;
746 my $meta = __PACKAGE__->meta; # Calling meta method here fine as we happen at compile time.
747 $meta->add_method($public_method_name, $meta->get_method($private_method_name));
748 {
749 my %package_hash; # Only warn once per method, per package. These are infrequent enough that
750 # I haven't provided a way to disable them, patches welcome.
751 $meta->add_before_method_modifier($public_method_name, sub {
3cd3bc6a 752 my $class = caller(2);
753 chomp($class);
b3f7d10b 754 $package_hash{$class}++ || do {
3cd3bc6a 755 warn("Class $class is calling the deprecated method\n"
756 . " Catalyst::Dispatcher::$public_method_name,\n"
757 . " this will be removed in Catalyst 5.9X\n");
c41cfce3 758 };
759 });
760 }
761}
762# End 5.70 backwards compatibility hacks.
763
e5ecd5bc 764__PACKAGE__->meta->make_immutable;
765
059c085b 766=head2 meta
767
768Provided by Moose
769
2f381252 770=head1 AUTHORS
1abd6db7 771
2f381252 772Catalyst Contributors, see Catalyst.pm
1abd6db7 773
774=head1 COPYRIGHT
775
536bee89 776This library is free software. You can redistribute it and/or modify it under
1abd6db7 777the same terms as Perl itself.
778
779=cut
780
7811;