1package Catalyst::Controller; 2 3use Moose; 4use Class::MOP; 5use Class::Load ':all'; 6use String::RewritePrefix; 7use Moose::Util qw/find_meta/; 8use List::Util qw/first uniq/; 9use namespace::clean -except => 'meta'; 10 11BEGIN { 12 extends qw/Catalyst::Component/; 13 with qw/MooseX::MethodAttributes::Role::AttrContainer::Inheritable/; 14} 15 16use MooseX::MethodAttributes; 17use Catalyst::Exception; 18use Catalyst::Utils; 19 20with 'Catalyst::Component::ApplicationAttribute'; 21 22has path_prefix => ( 23 is => 'rw', 24 isa => 'Str', 25 init_arg => 'path', 26 predicate => 'has_path_prefix', 27); 28 29has action_namespace => ( 30 is => 'rw', 31 isa => 'Str', 32 init_arg => 'namespace', 33 predicate => 'has_action_namespace', 34); 35 36has actions => ( 37 accessor => '_controller_actions', 38 isa => 'HashRef', 39 init_arg => undef, 40); 41 42has _action_role_args => ( 43 traits => [qw(Array)], 44 isa => 'ArrayRef[Str]', 45 init_arg => 'action_roles', 46 default => sub { [] }, 47 handles => { 48 _action_role_args => 'elements', 49 }, 50); 51 52has _action_roles => ( 53 traits => [qw(Array)], 54 isa => 'ArrayRef[RoleName]', 55 init_arg => undef, 56 lazy => 1, 57 builder => '_build__action_roles', 58 handles => { 59 _action_roles => 'elements', 60 }, 61); 62 63has action_args => (is => 'ro'); 64 65# ->config(actions => { '*' => ... 66has _all_actions_attributes => ( 67 is => 'ro', 68 isa => 'HashRef', 69 init_arg => undef, 70 lazy => 1, 71 builder => '_build__all_actions_attributes', 72); 73 74sub BUILD { 75 my ($self, $args) = @_; 76 my $action = delete $args->{action} || {}; 77 my $actions = delete $args->{actions} || {}; 78 my $attr_value = $self->merge_config_hashes($actions, $action); 79 $self->_controller_actions($attr_value); 80 81 # trigger lazy builder 82 $self->_all_actions_attributes; 83 $self->_action_roles; 84} 85 86sub _build__action_roles { 87 my $self = shift; 88 my @roles = $self->_expand_role_shortname($self->_action_role_args); 89 load_class($_) for @roles; 90 return \@roles; 91} 92 93sub _build__all_actions_attributes { 94 my ($self) = @_; 95 delete $self->_controller_actions->{'*'} || {}; 96} 97 98=head1 NAME 99 100Catalyst::Controller - Catalyst Controller base class 101 102=head1 SYNOPSIS 103 104 package MyApp::Controller::Search 105 use base qw/Catalyst::Controller/; 106 107 sub foo : Local { 108 my ($self,$c,@args) = @_; 109 ... 110 } # Dispatches to /search/foo 111 112=head1 DESCRIPTION 113 114Controllers are where the actions in the Catalyst framework 115reside. Each action is represented by a function with an attribute to 116identify what kind of action it is. See the L<Catalyst::Dispatcher> 117for more info about how Catalyst dispatches to actions. 118 119=cut 120 121#I think both of these could be attributes. doesn't really seem like they need 122#to be class data. i think that attributes +default would work just fine 123__PACKAGE__->mk_classdata($_) for qw/_dispatch_steps _action_class _action_role_prefix/; 124 125__PACKAGE__->_dispatch_steps( [qw/_BEGIN _AUTO _ACTION/] ); 126__PACKAGE__->_action_class('Catalyst::Action'); 127__PACKAGE__->_action_role_prefix([ 'Catalyst::ActionRole::' ]); 128 129 130sub _DISPATCH : Private { 131 my ( $self, $c ) = @_; 132 133 foreach my $disp ( @{ $self->_dispatch_steps } ) { 134 last unless $c->forward($disp); 135 } 136 137 $c->forward('_END'); 138} 139 140sub _BEGIN : Private { 141 my ( $self, $c ) = @_; 142 my $begin = ( $c->get_actions( 'begin', $c->namespace ) )[-1]; 143 return 1 unless $begin; 144 $begin->dispatch( $c ); 145 #If there is an error, all bets off 146 if( @{ $c->error }) { 147 return !@{ $c->error }; 148 } else { 149 return $c->state || 1; 150 } 151} 152 153sub _AUTO : Private { 154 my ( $self, $c ) = @_; 155 my @auto = $c->get_actions( 'auto', $c->namespace ); 156 foreach my $auto (@auto) { 157 # We FORCE the auto action user to explicitly return 158 # true. We need to do this since there's some auto 159 # users (Catalyst::Authentication::Credential::HTTP) that 160 # actually do a detach instead. 161 $c->state(0); 162 $auto->dispatch( $c ); 163 return 0 unless $c->state; 164 } 165 return $c->state || 1; 166} 167 168sub _ACTION : Private { 169 my ( $self, $c ) = @_; 170 if ( ref $c->action 171 && $c->action->can('execute') 172 && defined $c->req->action ) 173 { 174 $c->action->dispatch( $c ); 175 } 176 #If there is an error, all bets off 177 if( @{ $c->error }) { 178 return !@{ $c->error }; 179 } else { 180 return $c->state || 1; 181 } 182} 183 184sub _END : Private { 185 my ( $self, $c ) = @_; 186 my $end = ( $c->get_actions( 'end', $c->namespace ) )[-1]; 187 return 1 unless $end; 188 $end->dispatch( $c ); 189 return !@{ $c->error }; 190} 191 192sub action_for { 193 my ( $self, $name ) = @_; 194 my $app = ($self->isa('Catalyst') ? $self : $self->_application); 195 return $app->dispatcher->get_action($name, $self->action_namespace); 196} 197 198#my opinion is that this whole sub really should be a builder method, not 199#something that happens on every call. Anyone else disagree?? -- groditi 200## -- apparently this is all just waiting for app/ctx split 201around action_namespace => sub { 202 my $orig = shift; 203 my ( $self, $c ) = @_; 204 205 my $class = ref($self) || $self; 206 my $appclass = ref($c) || $c; 207 if( ref($self) ){ 208 return $self->$orig if $self->has_action_namespace; 209 } else { 210 return $class->config->{namespace} if exists $class->config->{namespace}; 211 } 212 213 my $case_s; 214 if( $c ){ 215 $case_s = $appclass->config->{case_sensitive}; 216 } else { 217 if ($self->isa('Catalyst')) { 218 $case_s = $class->config->{case_sensitive}; 219 } else { 220 if (ref $self) { 221 $case_s = ref($self->_application)->config->{case_sensitive}; 222 } else { 223 confess("Can't figure out case_sensitive setting"); 224 } 225 } 226 } 227 228 my $namespace = Catalyst::Utils::class2prefix($self->catalyst_component_name, $case_s) || ''; 229 $self->$orig($namespace) if ref($self); 230 return $namespace; 231}; 232 233#Once again, this is probably better written as a builder method 234around path_prefix => sub { 235 my $orig = shift; 236 my $self = shift; 237 if( ref($self) ){ 238 return $self->$orig if $self->has_path_prefix; 239 } else { 240 return $self->config->{path} if exists $self->config->{path}; 241 } 242 my $namespace = $self->action_namespace(@_); 243 $self->$orig($namespace) if ref($self); 244 return $namespace; 245}; 246 247sub get_action_methods { 248 my $self = shift; 249 my $meta = find_meta($self) || confess("No metaclass setup for $self"); 250 confess( 251 sprintf "Metaclass %s for %s cannot support register_actions.", 252 ref $meta, $meta->name, 253 ) unless $meta->can('get_nearest_methods_with_attributes'); 254 my @methods = $meta->get_nearest_methods_with_attributes; 255 256 # actions specified via config are also action_methods 257 push( 258 @methods, 259 map { 260 $meta->find_method_by_name($_) 261 || confess( sprintf 'Action "%s" is not available from controller %s', 262 $_, ref $self ) 263 } keys %{ $self->_controller_actions } 264 ) if ( ref $self ); 265 return uniq @methods; 266} 267 268 269sub register_actions { 270 my ( $self, $c ) = @_; 271 $self->register_action_methods( $c, $self->get_action_methods ); 272} 273 274sub register_action_methods { 275 my ( $self, $c, @methods ) = @_; 276 my $class = $self->catalyst_component_name; 277 #this is still not correct for some reason. 278 my $namespace = $self->action_namespace($c); 279 280 # FIXME - fugly 281 if (!blessed($self) && $self eq $c && scalar(@methods)) { 282 my @really_bad_methods = grep { ! /^_(DISPATCH|BEGIN|AUTO|ACTION|END)$/ } map { $_->name } @methods; 283 if (scalar(@really_bad_methods)) { 284 $c->log->warn("Action methods (" . join(', ', @really_bad_methods) . ") found defined in your application class, $self. This is deprecated, please move them into a Root controller."); 285 } 286 } 287 288 foreach my $method (@methods) { 289 my $name = $method->name; 290 # Horrible hack! All method metaclasses should have an attributes 291 # method, core Moose bug - see r13354. 292 my $attributes = $method->can('attributes') ? $method->attributes : []; 293 my $attrs = $self->_parse_attrs( $c, $name, @{ $attributes } ); 294 if ( $attrs->{Private} && ( keys %$attrs > 1 ) ) { 295 $c->log->warn( 'Bad action definition "' 296 . join( ' ', @{ $attributes } ) 297 . qq/" for "$class->$name"/ ) 298 if $c->debug; 299 next; 300 } 301 my $reverse = $namespace ? "${namespace}/${name}" : $name; 302 my $action = $self->create_action( 303 name => $name, 304 code => $method->body, 305 reverse => $reverse, 306 namespace => $namespace, 307 class => $class, 308 attributes => $attrs, 309 ); 310 311 $c->dispatcher->register( $c, $action ); 312 } 313} 314 315sub _apply_action_class_roles { 316 my ($self, $class, @roles) = @_; 317 318 load_class($_) for @roles; 319 my $meta = Moose::Meta::Class->initialize($class)->create_anon_class( 320 superclasses => [$class], 321 roles => \@roles, 322 cache => 1, 323 ); 324 $meta->add_method(meta => sub { $meta }); 325 326 return $meta->name; 327} 328 329sub action_class { 330 my $self = shift; 331 my %args = @_; 332 333 my $class = (exists $args{attributes}{ActionClass} 334 ? $args{attributes}{ActionClass}[0] 335 : $self->_action_class); 336 337 load_class($class); 338 return $class; 339} 340 341sub create_action { 342 my $self = shift; 343 my %args = @_; 344 345 my $class = $self->action_class(%args); 346 347 load_class($class); 348 Moose->init_meta(for_class => $class) 349 unless Class::MOP::does_metaclass_exist($class); 350 351 unless ($args{name} =~ /^_(DISPATCH|BEGIN|AUTO|ACTION|END)$/) { 352 my @roles = $self->gather_action_roles(%args); 353 push @roles, $self->gather_default_action_roles(%args); 354 355 $class = $self->_apply_action_class_roles($class, @roles) if @roles; 356 } 357 358 my $action_args = ( 359 ref($self) 360 ? $self->action_args 361 : $self->config->{action_args} 362 ); 363 364 my %extra_args = ( 365 %{ $action_args->{'*'} || {} }, 366 %{ $action_args->{ $args{name} } || {} }, 367 ); 368 369 return $class->new({ %extra_args, %args }); 370} 371 372sub gather_action_roles { 373 my ($self, %args) = @_; 374 return ( 375 (blessed $self ? $self->_action_roles : ()), 376 @{ $args{attributes}->{Does} || [] }, 377 ); 378} 379 380sub gather_default_action_roles { 381 my ($self, %args) = @_; 382 my @roles = (); 383 push @roles, 'Catalyst::ActionRole::HTTPMethods' 384 if $args{attributes}->{Method}; 385 386 push @roles, 'Catalyst::ActionRole::ConsumesContent' 387 if $args{attributes}->{Consumes}; 388 389 push @roles, 'Catalyst::ActionRole::Scheme' 390 if $args{attributes}->{Scheme}; 391 392 push @roles, 'Catalyst::ActionRole::QueryMatching' 393 if $args{attributes}->{Query}; 394 return @roles; 395} 396 397sub _parse_attrs { 398 my ( $self, $c, $name, @attrs ) = @_; 399 400 my %raw_attributes; 401 402 foreach my $attr (@attrs) { 403 404 # Parse out :Foo(bar) into Foo => bar etc (and arrayify) 405 406 if ( my ( $key, $value ) = ( $attr =~ /^(.*?)(?:\(\s*(.+?)?\s*\))?$/ ) ) 407 { 408 409 if ( defined $value ) { 410 ( $value =~ s/^'(.*)'$/$1/ ) || ( $value =~ s/^"(.*)"/$1/ ); 411 } 412 push( @{ $raw_attributes{$key} }, $value ); 413 } 414 } 415 416 my ($actions_config, $all_actions_config); 417 if( ref($self) ) { 418 $actions_config = $self->_controller_actions; 419 # No, you're not getting actions => { '*' => ... } with actions in MyApp. 420 $all_actions_config = $self->_all_actions_attributes; 421 } else { 422 my $cfg = $self->config; 423 $actions_config = $self->merge_config_hashes($cfg->{actions}, $cfg->{action}); 424 $all_actions_config = {}; 425 } 426 427 %raw_attributes = ( 428 %raw_attributes, 429 # Note we deep copy array refs here to stop crapping on config 430 # when attributes are parsed. RT#65463 431 exists $actions_config->{$name} ? map { ref($_) eq 'ARRAY' ? [ @$_ ] : $_ } %{ $actions_config->{$name } } : (), 432 ); 433 434 # Private actions with additional attributes will raise a warning and then 435 # be ignored. Adding '*' arguments to the default _DISPATCH / etc. methods, 436 # which are Private, will prevent those from being registered. They should 437 # probably be turned into :Actions instead, or we might want to otherwise 438 # disambiguate between those built-in internal actions and user-level 439 # Private ones. 440 %raw_attributes = (%{ $all_actions_config }, %raw_attributes) 441 unless $raw_attributes{Private}; 442 443 my %final_attributes; 444 445 while (my ($key, $value) = each %raw_attributes){ 446 my $new_attrs = $self->_parse_attr($c, $name, $key => $value ); 447 push @{ $final_attributes{$_} }, @{ $new_attrs->{$_} } for keys %$new_attrs; 448 } 449 450 return \%final_attributes; 451} 452 453sub _parse_attr { 454 my ($self, $c, $name, $key, $values) = @_; 455 456 my %final_attributes; 457 foreach my $value (ref($values) eq 'ARRAY' ? @$values : $values) { 458 my $meth = "_parse_${key}_attr"; 459 if ( my $code = $self->can($meth) ) { 460 my %new_attrs = $self->$code( $c, $name, $value ); 461 while (my ($new_key, $value) = each %new_attrs){ 462 my $new_attrs = $key eq $new_key ? 463 { $new_key => [$value] } : 464 $self->_parse_attr($c, $name, $new_key => $value ); 465 push @{ $final_attributes{$_} }, @{ $new_attrs->{$_} } for keys %$new_attrs; 466 } 467 } 468 else { 469 push( @{ $final_attributes{$key} }, $value ); 470 } 471 } 472 473 return \%final_attributes; 474} 475 476sub _parse_Global_attr { 477 my ( $self, $c, $name, $value ) = @_; 478 # _parse_attr will call _parse_Path_attr for us 479 return Path => "/$name"; 480} 481 482sub _parse_Absolute_attr { shift->_parse_Global_attr(@_); } 483 484sub _parse_Local_attr { 485 my ( $self, $c, $name, $value ) = @_; 486 # _parse_attr will call _parse_Path_attr for us 487 return Path => $name; 488} 489 490sub _parse_Relative_attr { shift->_parse_Local_attr(@_); } 491 492sub _parse_Path_attr { 493 my ( $self, $c, $name, $value ) = @_; 494 $value = '' if !defined $value; 495 if ( $value =~ m!^/! ) { 496 return ( 'Path', $value ); 497 } 498 elsif ( length $value ) { 499 return ( 'Path', join( '/', $self->path_prefix($c), $value ) ); 500 } 501 else { 502 return ( 'Path', $self->path_prefix($c) ); 503 } 504} 505 506sub _parse_Chained_attr { 507 my ($self, $c, $name, $value) = @_; 508 509 if (defined($value) && length($value)) { 510 if ($value eq '.') { 511 $value = '/'.$self->action_namespace($c); 512 } elsif (my ($rel, $rest) = $value =~ /^((?:\.{2}\/)+)(.*)$/) { 513 my @parts = split '/', $self->action_namespace($c); 514 my @levels = split '/', $rel; 515 516 $value = '/'.join('/', @parts[0 .. $#parts - @levels], $rest); 517 } elsif ($value !~ m/^\//) { 518 my $action_ns = $self->action_namespace($c); 519 520 if ($action_ns) { 521 $value = '/'.join('/', $action_ns, $value); 522 } else { 523 $value = '/'.$value; # special case namespace '' (root) 524 } 525 } 526 } else { 527 $value = '/' 528 } 529 530 return Chained => $value; 531} 532 533sub _parse_ChainedParent_attr { 534 my ($self, $c, $name, $value) = @_; 535 return $self->_parse_Chained_attr($c, $name, '../'.$name); 536} 537 538sub _parse_PathPrefix_attr { 539 my ( $self, $c ) = @_; 540 return PathPart => $self->path_prefix($c); 541} 542 543sub _parse_ActionClass_attr { 544 my ( $self, $c, $name, $value ) = @_; 545 my $appname = $self->_application; 546 $value = Catalyst::Utils::resolve_namespace($appname . '::Action', $self->_action_class, $value); 547 return ( 'ActionClass', $value ); 548} 549 550sub _parse_MyAction_attr { 551 my ( $self, $c, $name, $value ) = @_; 552 553 my $appclass = Catalyst::Utils::class2appclass($self); 554 $value = "+${appclass}::Action::${value}"; 555 556 return ( 'ActionClass', $value ); 557} 558 559sub _parse_Does_attr { 560 my ($self, $app, $name, $value) = @_; 561 return Does => $self->_expand_role_shortname($value); 562} 563 564sub _parse_GET_attr { Method => 'GET' } 565sub _parse_POST_attr { Method => 'POST' } 566sub _parse_PUT_attr { Method => 'PUT' } 567sub _parse_DELETE_attr { Method => 'DELETE' } 568sub _parse_OPTIONS_attr { Method => 'OPTIONS' } 569sub _parse_HEAD_attr { Method => 'HEAD' } 570sub _parse_PATCH_attr { Method => 'PATCH' } 571 572sub _expand_role_shortname { 573 my ($self, @shortnames) = @_; 574 my $app = $self->_application; 575 576 my $prefix = $self->can('_action_role_prefix') ? $self->_action_role_prefix : ['Catalyst::ActionRole::']; 577 my @prefixes = (qq{${app}::ActionRole::}, @$prefix); 578 579 return String::RewritePrefix->rewrite( 580 { '' => sub { 581 my $loaded = load_first_existing_class( 582 map { "$_$_[0]" } @prefixes 583 ); 584 return first { $loaded =~ /^$_/ } 585 sort { length $b <=> length $a } @prefixes; 586 }, 587 '~' => $prefixes[0], 588 '+' => '' }, 589 @shortnames, 590 ); 591} 592 593__PACKAGE__->meta->make_immutable; 594 5951; 596 597__END__ 598 599=head1 CONFIGURATION 600 601Like any other L<Catalyst::Component>, controllers have a config hash, 602accessible through $self->config from the controller actions. Some 603settings are in use by the Catalyst framework: 604 605=head2 namespace 606 607This specifies the internal namespace the controller should be bound 608to. By default the controller is bound to the URI version of the 609controller name. For instance controller 'MyApp::Controller::Foo::Bar' 610will be bound to 'foo/bar'. The default Root controller is an example 611of setting namespace to '' (the null string). 612 613=head2 path 614 615Sets 'path_prefix', as described below. 616 617=head2 action 618 619Allows you to set the attributes that the dispatcher creates actions out of. 620This allows you to do 'rails style routes', or override some of the 621attribute definitions of actions composed from Roles. 622You can set arguments globally (for all actions of the controller) and 623specifically (for a single action). 624 625 __PACKAGE__->config( 626 action => { 627 '*' => { Chained => 'base', Args => 0 }, 628 base => { Chained => '/', PathPart => '', CaptureArgs => 0 }, 629 }, 630 ); 631 632In the case above every sub in the package would be made into a Chain 633endpoint with a URI the same as the sub name for each sub, chained 634to the sub named C<base>. Ergo dispatch to C</example> would call the 635C<base> method, then the C<example> method. 636 637=head2 action_args 638 639Allows you to set constructor arguments on your actions. You can set arguments 640globally and specifically (as above). 641This is particularly useful when using C<ActionRole>s 642(L<Catalyst::Controller::ActionRole>) and custom C<ActionClass>es. 643 644 __PACKAGE__->config( 645 action_args => { 646 '*' => { globalarg1 => 'hello', globalarg2 => 'goodbye' }, 647 'specific_action' => { customarg => 'arg1' }, 648 }, 649 ); 650 651In the case above the action class associated with C<specific_action> would get 652passed the following arguments, in addition to the normal action constructor 653arguments, when it is instantiated: 654 655 (globalarg1 => 'hello', globalarg2 => 'goodbye', customarg => 'arg1') 656 657=head1 METHODS 658 659=head2 BUILDARGS ($app, @args) 660 661From L<Catalyst::Component::ApplicationAttribute>, stashes the application 662instance as $self->_application. 663 664=head2 $self->action_for($action_name) 665 666Returns the Catalyst::Action object (if any) for a given action in this 667controller or relative to it. You may refer to actions in controllers 668nested under the current controllers namespace, or in controllers 'up' 669from the current controller namespace. For example: 670 671 package MyApp::Controller::One::Two; 672 use base 'Catalyst::Controller'; 673 674 sub foo :Local { 675 my ($self, $c) = @_; 676 $self->action_for('foo'); # action 'foo' in Controller 'One::Two' 677 $self->action_for('three/bar'); # action 'bar' in Controller 'One::Two::Three' 678 $self->action_for('../boo'); # action 'boo' in Controller 'One' 679 } 680 681This returns 'undef' if there is no action matching the requested action 682name (after any path normalization) so you should check for this as needed. 683 684=head2 $self->action_namespace($c) 685 686Returns the private namespace for actions in this component. Defaults 687to a value from the controller name (for 688e.g. MyApp::Controller::Foo::Bar becomes "foo/bar") or can be 689overridden from the "namespace" config key. 690 691 692=head2 $self->path_prefix($c) 693 694Returns the default path prefix for :PathPrefix, :Local and 695relative :Path actions in this component. Defaults to the action_namespace or 696can be overridden from the "path" config key. 697 698=head2 $self->register_actions($c) 699 700Finds all applicable actions for this component, creates 701Catalyst::Action objects (using $self->create_action) for them and 702registers them with $c->dispatcher. 703 704=head2 $self->get_action_methods() 705 706Returns a list of L<Moose::Meta::Method> objects, doing the 707L<MooseX::MethodAttributes::Role::Meta::Method> role, which are the set of 708action methods for this package. 709 710=head2 $self->register_action_methods($c, @methods) 711 712Creates action objects for a set of action methods using C< create_action >, 713and registers them with the dispatcher. 714 715=head2 $self->action_class(%args) 716 717Used when a controller is creating an action to determine the correct base 718action class to use. 719 720=head2 $self->create_action(%args) 721 722Called with a hash of data to be use for construction of a new 723Catalyst::Action (or appropriate sub/alternative class) object. 724 725=head2 $self->gather_action_roles(\%action_args) 726 727Gathers the list of roles to apply to an action with the given %action_args. 728 729=head2 $self->gather_default_action_roles(\%action_args) 730 731returns a list of action roles to be applied based on core, builtin rules. 732Currently only the L<Catalyst::ActionRole::HTTPMethods> role is applied 733this way. 734 735=head2 $self->_application 736 737=head2 $self->_app 738 739Returns the application instance stored by C<new()> 740 741=head1 ACTION SUBROUTINE ATTRIBUTES 742 743Please see L<Catalyst::Manual::Intro> for more details 744 745Think of action attributes as a sort of way to record metadata about an action, 746similar to how annotations work in other languages you might have heard of. 747Generally L<Catalyst> uses these to influence how the dispatcher sees your 748action and when it will run it in response to an incoming request. They can 749also be used for other things. Here's a summary, but you should refer to the 750linked manual page for additional help. 751 752=head2 Global 753 754 sub homepage :Global { ... } 755 756A global action defined in any controller always runs relative to your root. 757So the above is the same as: 758 759 sub myaction :Path("/homepage") { ... } 760 761=head2 Absolute 762 763Status: Deprecated alias to L</Global>. 764 765=head2 Local 766 767Alias to "Path("$action_name"). The following two actions are the same: 768 769 sub myaction :Local { ... } 770 sub myaction :Path('myaction') { ... } 771 772=head2 Relative 773 774Status: Deprecated alias to L</Local> 775 776=head2 Path 777 778Handle various types of paths: 779 780 package MyApp::Controller::Baz { 781 782 ... 783 784 sub myaction1 :Path { ... } # -> /baz 785 sub myaction2 :Path('foo') { ... } # -> /baz/foo 786 sub myaction2 :Path('/bar') { ... } # -> /bar 787 } 788 789This is a general toolbox for attaching your action to a given path. 790 791 792=head2 Regex 793 794=head2 Regexp 795 796B<Status: Deprecated.> Use Chained methods or other techniques. 797If you really depend on this, install the standalone 798L<Catalyst::DispatchType::Regex> distribution. 799 800A global way to match a give regular expression in the incoming request path. 801 802=head2 LocalRegex 803 804=head2 LocalRegexp 805 806B<Status: Deprecated.> Use Chained methods or other techniques. 807If you really depend on this, install the standalone 808L<Catalyst::DispatchType::Regex> distribution. 809 810Like L</Regex> but scoped under the namespace of the containing controller 811 812=head2 Chained 813 814=head2 ChainedParent 815 816=head2 PathPrefix 817 818=head2 PathPart 819 820=head2 CaptureArgs 821 822Allowed values for CaptureArgs is a single integer (CaptureArgs(2), meaning two 823allowed) or you can declare a L<Moose>, L<MooseX::Types> or L<Type::Tiny> 824named constraint such as CaptureArgs(Int,Str) would require two args with 825the first being a Integer and the second a string. You may declare your own 826custom type constraints and import them into the controller namespace: 827 828 package MyApp::Controller::Root; 829 830 use Moose; 831 use MooseX::MethodAttributes; 832 use MyApp::Types qw/Int/; 833 834 extends 'Catalyst::Controller'; 835 836 sub chain_base :Chained(/) CaptureArgs(1) { } 837 838 sub any_priority_chain :Chained(chain_base) PathPart('') Args(1) { } 839 840 sub int_priority_chain :Chained(chain_base) PathPart('') Args(Int) { } 841 842See L<Catalyst::RouteMatching> for more. 843 844Please see L<Catalyst::DispatchType::Chained> for more. 845 846=head2 ActionClass 847 848Set the base class for the action, defaults to L</Catalyst::Action>. It is now 849preferred to use L</Does>. 850 851=head2 MyAction 852 853Set the ActionClass using a custom Action in your project namespace. 854 855The following is exactly the same: 856 857 sub foo_action1 : Local ActionClass('+MyApp::Action::Bar') { ... } 858 sub foo_action2 : Local MyAction('Bar') { ... } 859 860=head2 Does 861 862 package MyApp::Controller::Zoo; 863 864 sub foo : Local Does('Buzz') { ... } # Catalyst::ActionRole:: 865 sub bar : Local Does('~Buzz') { ... } # MyApp::ActionRole::Buzz 866 sub baz : Local Does('+MyApp::ActionRole::Buzz') { ... } 867 868=head2 GET 869 870=head2 POST 871 872=head2 PUT 873 874=head2 DELETE 875 876=head2 OPTION 877 878=head2 HEAD 879 880=head2 PATCH 881 882=head2 Method('...') 883 884Sets the give action path to match the specified HTTP method, or via one of the 885broadly accepted methods of overriding the 'true' method (see 886L<Catalyst::ActionRole::HTTPMethods>). 887 888=head2 Args 889 890When used with L</Path> indicates the number of arguments expected in 891the path. However if no Args value is set, assumed to 'slurp' all 892remaining path pars under this namespace. 893 894Allowed values for Args is a single integer (Args(2), meaning two allowed) or you 895can declare a L<Moose>, L<MooseX::Types> or L<Type::Tiny> named constraint such 896as Args(Int,Str) would require two args with the first being a Integer and the 897second a string. You may declare your own custom type constraints and import 898them into the controller namespace: 899 900 package MyApp::Controller::Root; 901 902 use Moose; 903 use MooseX::MethodAttributes; 904 use MyApp::Types qw/Tuple Int Str StrMatch UserId/; 905 906 extends 'Catalyst::Controller'; 907 908 sub user :Local Args(UserId) { 909 my ($self, $c, $int) = @_; 910 } 911 912 sub an_int :Local Args(Int) { 913 my ($self, $c, $int) = @_; 914 } 915 916 sub many_ints :Local Args(ArrayRef[Int]) { 917 my ($self, $c, @ints) = @_; 918 } 919 920 sub match :Local Args(StrMatch[qr{\d\d-\d\d-\d\d}]) { 921 my ($self, $c, $int) = @_; 922 } 923 924If you choose not to use imported type constraints (like L<Type::Tiny>, or <MooseX::Types> 925you may use L<Moose> 'stringy' types however just like when you use these types in your 926declared attributes you must quote them: 927 928 sub my_moose_type :Local Args('Int') { ... } 929 930If you use 'reference' type constraints (such as ArrayRef[Int]) that have an unknown 931number of allowed matches, we set this the same way "Args" is. Please keep in mind 932that actions with an undetermined number of args match at lower precedence than those 933with a fixed number. You may use reference types such as Tuple from L<Types::Standard> 934that allows you to fix the number of allowed args. For example Args(Tuple[Int,Int]) 935would be determined to be two args (or really the same as Args(Int,Int).) You may 936find this useful for creating custom subtypes with complex matching rules that you 937wish to reuse over many actions. 938 939See L<Catalyst::RouteMatching> for more. 940 941B<Note>: It is highly recommended to use L<Type::Tiny> for your type constraints over 942other options. L<Type::Tiny> exposed a better meta data interface which allows us to 943do more and better types of introspection driving tests and debugging. 944 945=head2 Consumes('...') 946 947Matches the current action against the content-type of the request. Typically 948this is used when the request is a POST or PUT and you want to restrict the 949submitted content type. For example, you might have an HTML for that either 950returns classic url encoded form data, or JSON when Javascript is enabled. In 951this case you may wish to match either incoming type to one of two different 952actions, for properly processing. 953 954Examples: 955 956 sub is_json : Chained('start') Consumes('application/json') { ... } 957 sub is_urlencoded : Chained('start') Consumes('application/x-www-form-urlencoded') { ... } 958 sub is_multipart : Chained('start') Consumes('multipart/form-data') { ... } 959 960To reduce boilerplate, we include the following content type shortcuts: 961 962Examples 963 964 sub is_json : Chained('start') Consume(JSON) { ... } 965 sub is_urlencoded : Chained('start') Consumes(UrlEncoded) { ... } 966 sub is_multipart : Chained('start') Consumes(Multipart) { ... } 967 968You may specify more than one match: 969 970 sub is_more_than_one 971 : Chained('start') 972 : Consumes('application/x-www-form-urlencoded') 973 : Consumes('multipart/form-data') 974 975 sub is_more_than_one 976 : Chained('start') 977 : Consumes(UrlEncoded) 978 : Consumes(Multipart) 979 980Since it is a common case the shortcut C<HTMLForm> matches both 981'application/x-www-form-urlencoded' and 'multipart/form-data'. Here's the full 982list of available shortcuts: 983 984 JSON => 'application/json', 985 JS => 'application/javascript', 986 PERL => 'application/perl', 987 HTML => 'text/html', 988 XML => 'text/XML', 989 Plain => 'text/plain', 990 UrlEncoded => 'application/x-www-form-urlencoded', 991 Multipart => 'multipart/form-data', 992 HTMLForm => ['application/x-www-form-urlencoded','multipart/form-data'], 993 994Please keep in mind that when dispatching, L<Catalyst> will match the first most 995relevant case, so if you use the C<Consumes> attribute, you should place your 996most accurate matches early in the Chain, and your 'catchall' actions last. 997 998See L<Catalyst::ActionRole::ConsumesContent> for more. 999 1000=head2 Scheme(...) 1001 1002Allows you to specify a URI scheme for the action or action chain. For example 1003you can required that a given path be C<https> or that it is a websocket endpoint 1004C<ws> or C<wss>. For an action chain you may currently only have one defined 1005Scheme. 1006 1007 package MyApp::Controller::Root; 1008 1009 use base 'Catalyst::Controller'; 1010 1011 sub is_http :Path(scheme) Scheme(http) Args(0) { 1012 my ($self, $c) = @_; 1013 $c->response->body("is_http"); 1014 } 1015 1016 sub is_https :Path(scheme) Scheme(https) Args(0) { 1017 my ($self, $c) = @_; 1018 $c->response->body("is_https"); 1019 } 1020 1021In the above example http://localhost/root/scheme would match the first 1022action (is_http) but https://localhost/root/scheme would match the second. 1023 1024As an added benefit, if an action or action chain defines a Scheme, when using 1025$c->uri_for the scheme of the generated URL will use what you define in the action 1026or action chain (the current behavior is to set the scheme based on the current 1027incoming request). This makes it easier to use uri_for on websites where some 1028paths are secure and others are not. You may also use this to other schemes 1029like websockets. 1030 1031See L<Catalyst::ActionRole::Scheme> for more. 1032 1033=head1 OPTIONAL METHODS 1034 1035=head2 _parse_[$name]_attr 1036 1037Allows you to customize parsing of subroutine attributes. 1038 1039 sub myaction1 :Path TwoArgs { ... } 1040 1041 sub _parse_TwoArgs_attr { 1042 my ( $self, $c, $name, $value ) = @_; 1043 # $self -> controller instance 1044 # 1045 return(Args => 2); 1046 } 1047 1048Please note that this feature does not let you actually assign new functions 1049to actions via subroutine attributes, but is really more for creating useful 1050aliases to existing core and extended attributes, and transforms based on 1051existing information (like from configuration). Code for actually doing 1052something meaningful with the subroutine attributes will be located in the 1053L<Catalyst::Action> classes (or your subclasses), L<Catalyst::Dispatcher> and 1054in subclasses of L<Catalyst::DispatchType>. Remember these methods only get 1055called basically once when the application is starting, not per request! 1056 1057=head1 AUTHORS 1058 1059Catalyst Contributors, see Catalyst.pm 1060 1061=head1 COPYRIGHT 1062 1063This library is free software. You can redistribute it and/or modify 1064it under the same terms as Perl itself. 1065 1066=cut 1067