fixed reported c->state regression
[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;
e7399d8b 17use Class::Load qw(load_class try_load_class);
0ca510f0 18use Encode 2.21 'decode_utf8';
1abd6db7 19
792d99b4 20use namespace::clean -except => 'meta';
6f3df815 21
c41cfce3 22# Refactoring note:
23# do these belong as package vars or should we build these via a builder method?
24# See Catalyst-Plugin-Server for them being added to, which should be much less ugly.
25
6d030e6f 26# Preload these action types
9c1fc6d6 27our @PRELOAD = qw/Index Path/;
1abd6db7 28
2d1d8f91 29# Postload these action types
61a9002d 30our @POSTLOAD = qw/Default/;
2d1d8f91 31
c41cfce3 32# Note - see back-compat methods at end of file.
792d99b4 33has _tree => (is => 'rw', builder => '_build__tree');
5b8ac6cc 34has dispatch_types => (is => 'rw', default => sub { [] }, required => 1, lazy => 1);
c41cfce3 35has _registered_dispatch_types => (is => 'rw', default => sub { {} }, required => 1, lazy => 1);
36has _method_action_class => (is => 'rw', default => 'Catalyst::Action');
37has _action_hash => (is => 'rw', required => 1, lazy => 1, default => sub { {} });
38has _container_hash => (is => 'rw', required => 1, lazy => 1, default => sub { {} });
7ffc9d9d 39
11ff9b94 40my %dispatch_types = ( pre => \@PRELOAD, post => \@POSTLOAD );
41foreach my $type (keys %dispatch_types) {
42 has $type . "load_dispatch_types" => (
43 is => 'rw', required => 1, lazy => 1, default => sub { $dispatch_types{$type} },
44 traits => ['MooseX::Emulate::Class::Accessor::Fast::Meta::Role::Attribute'], # List assignment is CAF style
45 );
46}
083ee5d9 47
1abd6db7 48=head1 NAME
49
9c053379 50Catalyst::Dispatcher - The Catalyst Dispatcher
1abd6db7 51
52=head1 SYNOPSIS
53
54See L<Catalyst>.
55
56=head1 DESCRIPTION
57
4ab87e27 58This is the class that maps public urls to actions in your Catalyst
59application based on the attributes you set.
60
1abd6db7 61=head1 METHODS
62
b0ad47c1 63=head2 new
4ab87e27 64
65Construct a new dispatcher.
66
e7bb8d33 67=cut
68
792d99b4 69sub _build__tree {
70 my ($self) = @_;
9e81ba44 71
068c0898 72 my $container =
059c085b 73 Catalyst::ActionContainer->new( { part => '/', actions => {} } );
a13e21ab 74
792d99b4 75 return Tree::Simple->new($container, Tree::Simple->ROOT);
e7bb8d33 76}
77
78=head2 $self->preload_dispatch_types
79
80An arrayref of pre-loaded dispatchtype classes
81
82Entries are considered to be available as C<Catalyst::DispatchType::CLASS>
83To use a custom class outside the regular C<Catalyst> namespace, prefix
84it with a C<+>, like so:
85
86 +My::Dispatch::Type
87
88=head2 $self->postload_dispatch_types
89
90An arrayref of post-loaded dispatchtype classes
91
92Entries are considered to be available as C<Catalyst::DispatchType::CLASS>
93To use a custom class outside the regular C<Catalyst> namespace, prefix
94it with a C<+>, like so:
95
96 +My::Dispatch::Type
97
b5ecfcf0 98=head2 $self->dispatch($c)
1abd6db7 99
4ab87e27 100Delegate the dispatch to the action that matched the url, or return a
101message about unknown resource
102
1abd6db7 103=cut
104
105sub dispatch {
fbcc39ad 106 my ( $self, $c ) = @_;
e63bdf38 107 if ( my $action = $c->action ) {
108 $c->forward( join( '/', '', $action->namespace, '_DISPATCH' ) );
fbcc39ad 109 }
fbcc39ad 110 else {
1abd6db7 111 my $path = $c->req->path;
0ca510f0 112 $path =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
113 $path = decode_utf8($path);
114
1abd6db7 115 my $error = $path
116 ? qq/Unknown resource "$path"/
117 : "No default action defined";
118 $c->log->error($error) if $c->debug;
119 $c->error($error);
120 }
121}
122
2f381252 123# $self->_command2action( $c, $command [, \@arguments ] )
b456f8f3 124# $self->_command2action( $c, $command [, \@captures, \@arguments ] )
125# Search for an action, from the command and returns C<($action, $args, $captures)> on
2f381252 126# success. Returns C<(0)> on error.
1abd6db7 127
2f381252 128sub _command2action {
e72f8f51 129 my ( $self, $c, $command, @extra_params ) = @_;
99fe1710 130
1abd6db7 131 unless ($command) {
2f381252 132 $c->log->debug('Nothing to go to') if $c->debug;
1abd6db7 133 return 0;
134 }
99fe1710 135
b456f8f3 136 my (@args, @captures);
137
138 if ( ref( $extra_params[-2] ) eq 'ARRAY' ) {
e1e81442 139 @captures = @{ splice @extra_params, -2, 1 };
b456f8f3 140 }
068c0898 141
e72f8f51 142 if ( ref( $extra_params[-1] ) eq 'ARRAY' ) {
143 @args = @{ pop @extra_params }
144 } else {
2f381252 145 # this is a copy, it may take some abuse from
146 # ->_invoke_as_path if the path had trailing parts
e72f8f51 147 @args = @{ $c->request->arguments };
148 }
149
150 my $action;
151
2f381252 152 # go to a string path ("/foo/bar/gorch")
e31b525c 153 # or action object
7e95ba12 154 if (blessed($command) && $command->isa('Catalyst::Action')) {
e31b525c 155 $action = $command;
156 }
157 else {
158 $action = $self->_invoke_as_path( $c, "$command", \@args );
159 }
99fe1710 160
76ac74ec 161 # go to a component ( "View::Foo" or $c->component("...")
2f381252 162 # - a path or an object)
e72f8f51 163 unless ($action) {
164 my $method = @extra_params ? $extra_params[0] : "process";
165 $action = $self->_invoke_as_component( $c, $command, $method );
166 }
99fe1710 167
b456f8f3 168 return $action, \@args, \@captures;
2f381252 169}
170
ae0e35ee 171=head2 $self->visit( $c, $command [, \@arguments ] )
2f381252 172
173Documented in L<Catalyst>
174
175=cut
176
ae0e35ee 177sub visit {
2f381252 178 my $self = shift;
ae0e35ee 179 $self->_do_visit('visit', @_);
180}
181
182sub _do_visit {
183 my $self = shift;
184 my $opname = shift;
2f381252 185 my ( $c, $command ) = @_;
b456f8f3 186 my ( $action, $args, $captures ) = $self->_command2action(@_);
ae0e35ee 187 my $error = qq/Couldn't $opname("$command"): /;
2f381252 188
ae0e35ee 189 if (!$action) {
3ea37672 190 $error .= qq/Couldn't $opname to command "$command": /
191 .qq/Invalid action or component./;
ae0e35ee 192 }
193 elsif (!defined $action->namespace) {
194 $error .= qq/Action has no namespace: cannot $opname() to a plain /
382d317c 195 .qq/method or component, must be an :Action of some sort./
ae0e35ee 196 }
197 elsif (!$action->class->can('_DISPATCH')) {
198 $error .= qq/Action cannot _DISPATCH. /
199 .qq/Did you try to $opname() a non-controller action?/;
200 }
201 else {
202 $error = q();
203 }
204
205 if($error) {
2f381252 206 $c->error($error);
207 $c->log->debug($error) if $c->debug;
208 return 0;
209 }
210
52f71256 211 $action = $self->expand_action($action);
212
2f381252 213 local $c->request->{arguments} = $args;
b456f8f3 214 local $c->request->{captures} = $captures;
ae0e35ee 215 local $c->{namespace} = $action->{'namespace'};
216 local $c->{action} = $action;
217
2f381252 218 $self->dispatch($c);
ae0e35ee 219}
220
221=head2 $self->go( $c, $command [, \@arguments ] )
222
223Documented in L<Catalyst>
224
225=cut
2f381252 226
ae0e35ee 227sub go {
228 my $self = shift;
229 $self->_do_visit('go', @_);
f87b7c21 230 Catalyst::Exception::Go->throw;
2f381252 231}
232
233=head2 $self->forward( $c, $command [, \@arguments ] )
234
235Documented in L<Catalyst>
236
237=cut
238
239sub forward {
240 my $self = shift;
6f3df815 241 no warnings 'recursion';
cc7af078 242 return $self->_do_forward(forward => @_);
3ea37672 243}
244
245sub _do_forward {
246 my $self = shift;
247 my $opname = shift;
2f381252 248 my ( $c, $command ) = @_;
b456f8f3 249 my ( $action, $args, $captures ) = $self->_command2action(@_);
99fe1710 250
3ea37672 251 if (!$action) {
252 my $error .= qq/Couldn't $opname to command "$command": /
253 .qq/Invalid action or component./;
e540158b 254 $c->error($error);
255 $c->log->debug($error) if $c->debug;
256 return 0;
257 }
bd7d2e94 258
059c085b 259
12f0342e 260 local $c->request->{arguments} = $args;
6f3df815 261 no warnings 'recursion';
b8f669f3 262 $action->dispatch( $c );
3ea37672 263
cc7af078 264 #If there is an error, all bets off regarding state. Documentation
265 #Specifies that when you forward, if there's an error you must expect
266 #state to be 0.
267 if( @{ $c->error }) {
268 $c->state(0);
269 }
1abd6db7 270 return $c->state;
271}
272
3ea37672 273=head2 $self->detach( $c, $command [, \@arguments ] )
274
275Documented in L<Catalyst>
276
277=cut
278
279sub detach {
280 my ( $self, $c, $command, @args ) = @_;
281 $self->_do_forward(detach => $c, $command, @args ) if $command;
e459bd03 282 $c->state(0); # Needed in order to skip any auto functions
f87b7c21 283 Catalyst::Exception::Detach->throw;
3ea37672 284}
285
adb53907 286sub _action_rel2abs {
e540158b 287 my ( $self, $c, $path ) = @_;
288
289 unless ( $path =~ m#^/# ) {
290 my $namespace = $c->stack->[-1]->namespace;
291 $path = "$namespace/$path";
292 }
293
294 $path =~ s#^/##;
295 return $path;
adb53907 296}
297
298sub _invoke_as_path {
e540158b 299 my ( $self, $c, $rel_path, $args ) = @_;
300
e540158b 301 my $path = $self->_action_rel2abs( $c, $rel_path );
302
303 my ( $tail, @extra_args );
304 while ( ( $path, $tail ) = ( $path =~ m#^(?:(.*)/)?(\w+)?$# ) )
305 { # allow $path to be empty
306 if ( my $action = $c->get_action( $tail, $path ) ) {
307 push @$args, @extra_args;
308 return $action;
309 }
310 else {
311 return
312 unless $path
313 ; # if a match on the global namespace failed then the whole lookup failed
314 }
315
316 unshift @extra_args, $tail;
317 }
adb53907 318}
319
02298d3a 320sub _find_component {
e540158b 321 my ( $self, $c, $component ) = @_;
adb53907 322
02298d3a 323 # fugly, why doesn't ->component('MyApp') work?
324 return $c if ($component eq blessed($c));
325
326 return blessed($component)
327 ? $component
328 : $c->component($component);
adb53907 329}
330
331sub _invoke_as_component {
02298d3a 332 my ( $self, $c, $component_or_class, $method ) = @_;
e540158b 333
02298d3a 334 my $component = $self->_find_component($c, $component_or_class);
335 my $component_class = blessed $component || return 0;
e540158b 336
02298d3a 337 if (my $code = $component_class->can('action_for')) {
338 my $possible_action = $component->$code($method);
84c28acb 339 return $possible_action if $possible_action;
340 }
341
02298d3a 342 if ( my $code = $component_class->can($method) ) {
c41cfce3 343 return $self->_method_action_class->new(
e540158b 344 {
345 name => $method,
346 code => $code,
02298d3a 347 reverse => "$component_class->$method",
348 class => $component_class,
e540158b 349 namespace => Catalyst::Utils::class2prefix(
df960201 350 $component_class, ref($c)->config->{case_sensitive}
e540158b 351 ),
352 }
353 );
354 }
355 else {
356 my $error =
02298d3a 357 qq/Couldn't forward to "$component_class". Does not implement "$method"/;
e540158b 358 $c->error($error);
359 $c->log->debug($error)
360 if $c->debug;
361 return 0;
362 }
adb53907 363}
364
b5ecfcf0 365=head2 $self->prepare_action($c)
fbcc39ad 366
4ab87e27 367Find an dispatch type that matches $c->req->path, and set args from it.
368
fbcc39ad 369=cut
370
371sub prepare_action {
372 my ( $self, $c ) = @_;
e63bdf38 373 my $req = $c->req;
374 my $path = $req->path;
375 my @path = split /\//, $req->path;
376 $req->args( \my @args );
fbcc39ad 377
61a9002d 378 unshift( @path, '' ); # Root action
78d760bb 379
b96f127f 380 DESCEND: while (@path) {
fbcc39ad 381 $path = join '/', @path;
5299fff8 382 $path =~ s#^/+##;
78d760bb 383
22f3a8dd 384 # Check out dispatch types to see if any will handle the path at
385 # this level
386
a2aac3b8 387 foreach my $type ( @{ $self->dispatch_types } ) {
2633d7dc 388 last DESCEND if $type->match( $c, $path );
66e28e3f 389 }
b96f127f 390
22f3a8dd 391 # If not, move the last part path to args
4082e678 392 my $arg = pop(@path);
393 $arg =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
394 unshift @args, $arg;
fbcc39ad 395 }
396
e63bdf38 397 s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg for grep { defined } @{$req->captures||[]};
66d7ad40 398
0ca510f0 399 if($c->debug && defined $req->match && length $req->match) {
400 my $match = $req->match;
401 $match =~ s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg;
402 $match = decode_utf8($match);
403 $c->log->debug( 'Path is "' . $match . '"' )
404 }
e3a13771 405
0ca510f0 406 $c->log->debug( 'Arguments are "' . join( '/', map { decode_utf8 $_ } @args ) . '"' )
fbcc39ad 407 if ( $c->debug && @args );
408}
409
b5ecfcf0 410=head2 $self->get_action( $action, $namespace )
1abd6db7 411
4ab87e27 412returns a named action from a given namespace.
413
1abd6db7 414=cut
415
416sub get_action {
bcd1002b 417 my ( $self, $name, $namespace ) = @_;
79a3189a 418 return unless $name;
3d0d6d21 419
2f381252 420 $namespace = join( "/", grep { length } split '/', ( defined $namespace ? $namespace : "" ) );
99fe1710 421
c41cfce3 422 return $self->_action_hash->{"${namespace}/${name}"};
1abd6db7 423}
424
b0ad47c1 425=head2 $self->get_action_by_path( $path );
068c0898 426
ab990582 427Returns the named action by its full private path.
3d0d6d21 428
068c0898 429=cut
3d0d6d21 430
431sub get_action_by_path {
432 my ( $self, $path ) = @_;
ea0e58d9 433 $path =~ s/^\///;
28928de9 434 $path = "/$path" unless $path =~ /\//;
c41cfce3 435 $self->_action_hash->{$path};
3d0d6d21 436}
437
b5ecfcf0 438=head2 $self->get_actions( $c, $action, $namespace )
a9dc674c 439
440=cut
441
442sub get_actions {
443 my ( $self, $c, $action, $namespace ) = @_;
444 return [] unless $action;
3d0d6d21 445
28928de9 446 $namespace = join( "/", grep { length } split '/', $namespace || "" );
a9dc674c 447
448 my @match = $self->get_containers($namespace);
449
684d10ed 450 return map { $_->get_action($action) } @match;
a9dc674c 451}
452
b5ecfcf0 453=head2 $self->get_containers( $namespace )
cfd04b0c 454
4ab87e27 455Return all the action containers for a given namespace, inclusive
456
cfd04b0c 457=cut
458
459sub get_containers {
460 my ( $self, $namespace ) = @_;
a13e21ab 461 $namespace ||= '';
462 $namespace = '' if $namespace eq '/';
cfd04b0c 463
a13e21ab 464 my @containers;
cfd04b0c 465
7f23827b 466 if ( length $namespace ) {
467 do {
c41cfce3 468 push @containers, $self->_container_hash->{$namespace};
7f23827b 469 } while ( $namespace =~ s#/[^/]+$## );
470 }
90ce41ba 471
c41cfce3 472 return reverse grep { defined } @containers, $self->_container_hash->{''};
cfd04b0c 473}
474
ea0e58d9 475=head2 $self->uri_for_action($action, \@captures)
476
477Takes a Catalyst::Action object and action parameters and returns a URI
478part such that if $c->req->path were this URI part, this action would be
479dispatched to with $c->req->captures set to the supplied arrayref.
480
481If the action object is not available for external dispatch or the dispatcher
482cannot determine an appropriate URI, this method will return undef.
483
484=cut
485
486sub uri_for_action {
487 my ( $self, $action, $captures) = @_;
488 $captures ||= [];
a2aac3b8 489 foreach my $dispatch_type ( @{ $self->dispatch_types } ) {
ea0e58d9 490 my $uri = $dispatch_type->uri_for_action( $action, $captures );
81e75875 491 return( $uri eq '' ? '/' : $uri )
492 if defined($uri);
ea0e58d9 493 }
494 return undef;
495}
496
8f5a2bd9 497=head2 expand_action
ae0e35ee 498
499expand an action into a full representation of the dispatch.
500mostly useful for chained, other actions will just return a
501single action.
502
503=cut
504
52f71256 505sub expand_action {
506 my ($self, $action) = @_;
507
a2aac3b8 508 foreach my $dispatch_type (@{ $self->dispatch_types }) {
52f71256 509 my $expanded = $dispatch_type->expand_action($action);
510 return $expanded if $expanded;
511 }
512
513 return $action;
514}
515
b5ecfcf0 516=head2 $self->register( $c, $action )
aad72cc9 517
4ab87e27 518Make sure all required dispatch types for this action are loaded, then
519pass the action to our dispatch types so they can register it if required.
520Also, set up the tree with the action containers.
521
aad72cc9 522=cut
523
79a3189a 524sub register {
525 my ( $self, $c, $action ) = @_;
526
c41cfce3 527 my $registered = $self->_registered_dispatch_types;
694d15f1 528
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.
e7399d8b 535 # see also try_load_class
536 eval { load_class($class) };
5070f111 537 my $load_failed = $@;
6931f972 538 $self->_check_deprecated_dispatch_type( $key, $load_failed );
5070f111 539 push( @{ $self->dispatch_types }, $class->new ) unless $load_failed;
694d15f1 540 $registered->{$class} = 1;
541 }
542 }
543
a2aac3b8 544 my @dtypes = @{ $self->dispatch_types };
1315d253 545 my @normal_dtypes;
546 my @low_precedence_dtypes;
547
7b442de5 548 for my $type ( @dtypes ) {
c4586fd0 549 if ($type->_is_low_precedence) {
1315d253 550 push @low_precedence_dtypes, $type;
551 } else {
552 push @normal_dtypes, $type;
553 }
554 }
555
694d15f1 556 # Pass the action to our dispatch types so they can register it if reqd.
1315d253 557 my $was_registered = 0;
558 foreach my $type ( @normal_dtypes ) {
559 $was_registered = 1 if $type->register( $c, $action );
560 }
561
562 if (not $was_registered) {
563 foreach my $type ( @low_precedence_dtypes ) {
564 $type->register( $c, $action );
565 }
694d15f1 566 }
567
79a3189a 568 my $namespace = $action->namespace;
a13e21ab 569 my $name = $action->name;
c7116517 570
ad5e4650 571 my $container = $self->_find_or_create_action_container($namespace);
15e9b5dd 572
573 # Set the method value
a13e21ab 574 $container->add_action($action);
c7116517 575
c41cfce3 576 $self->_action_hash->{"$namespace/$name"} = $action;
577 $self->_container_hash->{$namespace} = $container;
15e9b5dd 578}
579
ad5e4650 580sub _find_or_create_action_container {
a13e21ab 581 my ( $self, $namespace ) = @_;
582
c41cfce3 583 my $tree ||= $self->_tree;
99fe1710 584
a13e21ab 585 return $tree->getNodeValue unless $namespace;
78d760bb 586
a13e21ab 587 my @namespace = split '/', $namespace;
588 return $self->_find_or_create_namespace_node( $tree, @namespace )
589 ->getNodeValue;
8505565b 590}
90ce41ba 591
8505565b 592sub _find_or_create_namespace_node {
a13e21ab 593 my ( $self, $parent, $part, @namespace ) = @_;
78d760bb 594
a13e21ab 595 return $parent unless $part;
8505565b 596
a13e21ab 597 my $child =
598 ( grep { $_->getNodeValue->part eq $part } $parent->getAllChildren )[0];
8505565b 599
a13e21ab 600 unless ($child) {
601 my $container = Catalyst::ActionContainer->new($part);
602 $parent->addChild( $child = Tree::Simple->new($container) );
603 }
99fe1710 604
a13e21ab 605 $self->_find_or_create_namespace_node( $child, @namespace );
1abd6db7 606}
607
4ab87e27 608=head2 $self->setup_actions( $class, $context )
609
965f3e35 610Loads all of the pre-load dispatch types, registers their actions and then
611loads all of the post-load dispatch types, and iterates over the tree of
8f59bbe2 612actions, displaying the debug information if appropriate.
1abd6db7 613
614=cut
615
616sub setup_actions {
11bd4e3e 617 my ( $self, $c ) = @_;
99fe1710 618
9e81ba44 619 my @classes =
ad5e4650 620 $self->_load_dispatch_types( @{ $self->preload_dispatch_types } );
c41cfce3 621 @{ $self->_registered_dispatch_types }{@classes} = (1) x @classes;
b96f127f 622
98d049ef 623 foreach my $comp ( values %{ $c->components } ) {
624 $comp = $comp->() if ref($comp) eq 'CODE';
49070d25 625 $comp->register_actions($c) if $comp->can('register_actions');
1abd6db7 626 }
e494bd6b 627
ad5e4650 628 $self->_load_dispatch_types( @{ $self->postload_dispatch_types } );
6d030e6f 629
11bd4e3e 630 return unless $c->debug;
2eb2c42f 631 $self->_display_action_tables($c);
632}
633
634sub _display_action_tables {
635 my ($self, $c) = @_;
99fe1710 636
48d435ba 637 my $avail_width = Catalyst::Utils::term_width() - 12;
638 my $col1_width = ($avail_width * .25) < 20 ? 20 : int($avail_width * .25);
639 my $col2_width = ($avail_width * .50) < 36 ? 36 : int($avail_width * .50);
640 my $col3_width = $avail_width - $col1_width - $col2_width;
684d10ed 641 my $privates = Text::SimpleTable->new(
48d435ba 642 [ $col1_width, 'Private' ], [ $col2_width, 'Class' ], [ $col3_width, 'Method' ]
684d10ed 643 );
99fe1710 644
87b85407 645 my $has_private = 0;
1abd6db7 646 my $walker = sub {
647 my ( $walker, $parent, $prefix ) = @_;
648 $prefix .= $parent->getNodeValue || '';
649 $prefix .= '/' unless $prefix =~ /\/$/;
b7aebc12 650 my $node = $parent->getNodeValue->actions;
99fe1710 651
78d760bb 652 for my $action ( keys %{$node} ) {
b7aebc12 653 my $action_obj = $node->{$action};
b0bb11ec 654 next
655 if ( ( $action =~ /^_.*/ )
656 && ( !$c->config->{show_internal_actions} ) );
684d10ed 657 $privates->row( "$prefix$action", $action_obj->class, $action );
87b85407 658 $has_private = 1;
1abd6db7 659 }
99fe1710 660
1abd6db7 661 $walker->( $walker, $_, $prefix ) for $parent->getAllChildren;
662 };
99fe1710 663
c41cfce3 664 $walker->( $walker, $self->_tree, '' );
1cf0345b 665 $c->log->debug( "Loaded Private actions:\n" . $privates->draw . "\n" )
666 if $has_private;
99fe1710 667
a9cbd748 668 # List all public actions
a2aac3b8 669 $_->list($c) for @{ $self->dispatch_types };
1abd6db7 670}
671
ad5e4650 672sub _load_dispatch_types {
9e81ba44 673 my ( $self, @types ) = @_;
674
675 my @loaded;
9e81ba44 676 # Preload action types
677 for my $type (@types) {
5d8129e9 678 # first param is undef because we cannot get the appclass
679 my $class = Catalyst::Utils::resolve_namespace(undef, 'Catalyst::DispatchType', $type);
2efad04b 680
e7399d8b 681 my ($success, $error) = try_load_class($class);
682 Catalyst::Exception->throw( message => $error ) if not $success;
a2aac3b8 683 push @{ $self->dispatch_types }, $class->new;
9e81ba44 684
685 push @loaded, $class;
686 }
687
a13e21ab 688 return @loaded;
9e81ba44 689}
690
e995c634 691=head2 $self->dispatch_type( $type )
692
693Get the DispatchType object of the relevant type, i.e. passing C<$type> of
694C<Chained> would return a L<Catalyst::DispatchType::Chained> object (assuming
b0ad47c1 695of course it's being used.)
e995c634 696
697=cut
698
7ffc9d9d 699sub dispatch_type {
700 my ($self, $name) = @_;
196932de 701
5d8129e9 702 # first param is undef because we cannot get the appclass
703 $name = Catalyst::Utils::resolve_namespace(undef, 'Catalyst::DispatchType', $name);
7ffc9d9d 704
a2aac3b8 705 for (@{ $self->dispatch_types }) {
7ffc9d9d 706 return $_ if ref($_) eq $name;
707 }
708 return undef;
709}
710
6931f972 711sub _check_deprecated_dispatch_type {
5070f111 712 my ($self, $key, $load_failed) = @_;
713
714 return unless $key =~ /^(Local)?Regexp?/;
715
716 # TODO: Should these throw an exception rather than just warning?
717 if ($load_failed) {
718 warn( "Attempt to use deprecated $key dispatch type.\n"
719 . " Use Chained methods or install the standalone\n"
720 . " Catalyst::DispatchType::Regex if necessary.\n" );
721 } elsif ( !defined $Catalyst::DispatchType::Regex::VERSION
28054612 722 || $Catalyst::DispatchType::Regex::VERSION le '5.90020' ) {
5070f111 723 # We loaded the old core version of the Regex module this will break
724 warn( "The $key DispatchType has been removed from Catalyst core.\n"
725 . " An old version of the core Catalyst::DispatchType::Regex\n"
726 . " has been loaded and will likely fail. Please remove\n"
8503ab9b 727 . " $INC{'Catalyst/DispatchType/Regex.pm'}\n"
5070f111 728 . " and use Chained methods or install the standalone\n"
729 . " Catalyst::DispatchType::Regex if necessary.\n" );
730 }
731}
732
c41cfce3 733use Moose;
734
735# 5.70 backwards compatibility hacks.
736
737# Various plugins (e.g. Plugin::Server and Plugin::Authorization::ACL)
738# need the methods here which *should* be private..
739
2efad04b 740# You should be able to use get_actions or get_containers appropriately
741# instead of relying on these methods which expose implementation details
742# of the dispatcher..
743#
744# IRC backlog included below, please come ask if this doesn't work for you.
745#
746# <@t0m> 5.80, the state of. There are things in the dispatcher which have
747# been deprecated, that we yell at anyone for using, which there isn't
748# a good alternative for yet..
749# <@mst> er, get_actions/get_containers provides that doesn't it?
750# <@mst> DispatchTypes are loaded on demand anyway
751# <@t0m> I'm thinking of things like _tree which is aliased to 'tree' with
752# warnings otherwise shit breaks.. We're issuing warnings about the
753# correct set of things which you shouldn't be calling..
754# <@mst> right
755# <@mst> basically, I don't see there's a need for a replacement for anything
756# <@mst> it was never a good idea to call ->tree
757# <@mst> nothingmuch was the only one who did AFAIK
758# <@mst> and he admitted it was a hack ;)
759
c41cfce3 760# See also t/lib/TestApp/Plugin/AddDispatchTypes.pm
761
762# Alias _method_name to method_name, add a before modifier to warn..
b0ad47c1 763foreach my $public_method_name (qw/
764 tree
b0ad47c1 765 registered_dispatch_types
766 method_action_class
767 action_hash
c41cfce3 768 container_hash
769 /) {
770 my $private_method_name = '_' . $public_method_name;
771 my $meta = __PACKAGE__->meta; # Calling meta method here fine as we happen at compile time.
772 $meta->add_method($public_method_name, $meta->get_method($private_method_name));
773 {
774 my %package_hash; # Only warn once per method, per package. These are infrequent enough that
775 # I haven't provided a way to disable them, patches welcome.
776 $meta->add_before_method_modifier($public_method_name, sub {
3cd3bc6a 777 my $class = caller(2);
778 chomp($class);
b3f7d10b 779 $package_hash{$class}++ || do {
3cd3bc6a 780 warn("Class $class is calling the deprecated method\n"
781 . " Catalyst::Dispatcher::$public_method_name,\n"
dacd8b0e 782 . " this will be removed in Catalyst 5.9\n");
c41cfce3 783 };
784 });
785 }
786}
787# End 5.70 backwards compatibility hacks.
788
e5ecd5bc 789__PACKAGE__->meta->make_immutable;
790
059c085b 791=head2 meta
792
793Provided by Moose
794
2f381252 795=head1 AUTHORS
1abd6db7 796
2f381252 797Catalyst Contributors, see Catalyst.pm
1abd6db7 798
799=head1 COPYRIGHT
800
536bee89 801This library is free software. You can redistribute it and/or modify it under
1abd6db7 802the same terms as Perl itself.
803
804=cut
805
8061;