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