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