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