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