use style;
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Controller.pm
CommitLineData
5ee249f2 1package Catalyst::Controller;
2
ae29b412 3use Moose;
a58af53d 4use Moose::Util qw/find_meta/;
4f4ab5b4 5use List::MoreUtils qw/uniq/;
ae29b412 6use namespace::clean -except => 'meta';
7
ba545c13 8BEGIN { extends qw/Catalyst::Component MooseX::MethodAttributes::Inheritable/; }
234763d4 9
cf37d21a 10use MooseX::MethodAttributes;
234763d4 11use Catalyst::Exception;
12use Catalyst::Utils;
ae29b412 13
14with 'Catalyst::Component::ApplicationAttribute';
15
aea897b2 16has path_prefix => (
17 is => 'rw',
18 isa => 'Str',
19 init_arg => 'path',
20 predicate => 'has_path_prefix',
21);
22
23has action_namespace => (
24 is => 'rw',
25 isa => 'Str',
26 init_arg => 'namespace',
27 predicate => 'has_action_namespace',
28);
29
30has actions => (
31 accessor => '_controller_actions',
32 isa => 'HashRef',
33 init_arg => undef,
34);
ae29b412 35
bf7c9c87 36has action_args => (is => 'ro');
37
bdd6684e 38# ->config(actions => { '*' => ...
39has _all_actions_attributes => (
40 is => 'ro',
41 isa => 'HashRef',
42 init_arg => undef,
43 lazy => 1,
44 builder => '_build__all_actions_attributes',
45);
46
7f22a5aa 47sub BUILD {
48 my ($self, $args) = @_;
ae29b412 49 my $action = delete $args->{action} || {};
50 my $actions = delete $args->{actions} || {};
7f22a5aa 51 my $attr_value = $self->merge_config_hashes($actions, $action);
52 $self->_controller_actions($attr_value);
5ee249f2 53
bdd6684e 54 # trigger lazy builder
55 $self->_all_actions_attributes;
56}
d0e5dfb5 57
bdd6684e 58sub _build__all_actions_attributes {
59 my ($self) = @_;
60 delete $self->_controller_actions->{'*'} || {};
61}
d0e5dfb5 62
5ee249f2 63=head1 NAME
64
65Catalyst::Controller - Catalyst Controller base class
66
67=head1 SYNOPSIS
68
234763d4 69 package MyApp::Controller::Search
a269e0c2 70 use base qw/Catalyst::Controller/;
234763d4 71
27ae4114 72 sub foo : Local {
85d9fce6 73 my ($self,$c,@args) = @_;
27ae4114 74 ...
234763d4 75 } # Dispatches to /search/foo
5ee249f2 76
77=head1 DESCRIPTION
78
a269e0c2 79Controllers are where the actions in the Catalyst framework
80reside. Each action is represented by a function with an attribute to
81identify what kind of action it is. See the L<Catalyst::Dispatcher>
82for more info about how Catalyst dispatches to actions.
234763d4 83
84=cut
85
ae29b412 86#I think both of these could be attributes. doesn't really seem like they need
87#to ble class data. i think that attributes +default would work just fine
7b41db70 88__PACKAGE__->mk_classdata($_) for qw/_dispatch_steps _action_class/;
234763d4 89
90__PACKAGE__->_dispatch_steps( [qw/_BEGIN _AUTO _ACTION/] );
7b41db70 91__PACKAGE__->_action_class('Catalyst::Action');
234763d4 92
234763d4 93
94sub _DISPATCH : Private {
95 my ( $self, $c ) = @_;
96
97 foreach my $disp ( @{ $self->_dispatch_steps } ) {
98 last unless $c->forward($disp);
99 }
100
101 $c->forward('_END');
102}
103
104sub _BEGIN : Private {
105 my ( $self, $c ) = @_;
106 my $begin = ( $c->get_actions( 'begin', $c->namespace ) )[-1];
107 return 1 unless $begin;
108 $begin->dispatch( $c );
109 return !@{ $c->error };
110}
111
112sub _AUTO : Private {
113 my ( $self, $c ) = @_;
114 my @auto = $c->get_actions( 'auto', $c->namespace );
115 foreach my $auto (@auto) {
116 $auto->dispatch( $c );
117 return 0 unless $c->state;
118 }
119 return 1;
120}
121
122sub _ACTION : Private {
123 my ( $self, $c ) = @_;
124 if ( ref $c->action
125 && $c->action->can('execute')
53119b78 126 && defined $c->req->action )
234763d4 127 {
128 $c->action->dispatch( $c );
129 }
130 return !@{ $c->error };
131}
132
133sub _END : Private {
134 my ( $self, $c ) = @_;
135 my $end = ( $c->get_actions( 'end', $c->namespace ) )[-1];
136 return 1 unless $end;
137 $end->dispatch( $c );
138 return !@{ $c->error };
139}
140
234763d4 141sub action_for {
142 my ( $self, $name ) = @_;
143 my $app = ($self->isa('Catalyst') ? $self : $self->_application);
144 return $app->dispatcher->get_action($name, $self->action_namespace);
145}
146
27ae4114 147#my opinion is that this whole sub really should be a builder method, not
ae29b412 148#something that happens on every call. Anyone else disagree?? -- groditi
149## -- apparently this is all just waiting for app/ctx split
150around action_namespace => sub {
151 my $orig = shift;
234763d4 152 my ( $self, $c ) = @_;
ae29b412 153
df960201 154 my $class = ref($self) || $self;
155 my $appclass = ref($c) || $c;
ae29b412 156 if( ref($self) ){
157 return $self->$orig if $self->has_action_namespace;
158 } else {
df960201 159 return $class->config->{namespace} if exists $class->config->{namespace};
234763d4 160 }
234763d4 161
ae29b412 162 my $case_s;
163 if( $c ){
df960201 164 $case_s = $appclass->config->{case_sensitive};
ae29b412 165 } else {
166 if ($self->isa('Catalyst')) {
df960201 167 $case_s = $class->config->{case_sensitive};
ae29b412 168 } else {
169 if (ref $self) {
df960201 170 $case_s = ref($self->_application)->config->{case_sensitive};
ae29b412 171 } else {
172 confess("Can't figure out case_sensitive setting");
173 }
174 }
234763d4 175 }
ae29b412 176
8f6cebb2 177 my $namespace = Catalyst::Utils::class2prefix($self->catalyst_component_name, $case_s) || '';
ae29b412 178 $self->$orig($namespace) if ref($self);
179 return $namespace;
180};
181
182#Once again, this is probably better written as a builder method
183around path_prefix => sub {
184 my $orig = shift;
185 my $self = shift;
186 if( ref($self) ){
187 return $self->$orig if $self->has_path_prefix;
188 } else {
189 return $self->config->{path} if exists $self->config->{path};
190 }
191 my $namespace = $self->action_namespace(@_);
192 $self->$orig($namespace) if ref($self);
193 return $namespace;
194};
234763d4 195
9ab7d83d 196sub get_action_methods {
197 my $self = shift;
2bf074ab 198 my $meta = find_meta($self) || confess("No metaclass setup for $self");
69048792 199 confess(
200 sprintf "Metaclass %s for %s cannot support register_actions.",
201 ref $meta, $meta->name,
202 ) unless $meta->can('get_nearest_methods_with_attributes');
cf37d21a 203 my @methods = $meta->get_nearest_methods_with_attributes;
fa649eb7 204
205 # actions specified via config are also action_methods
206 push(
207 @methods,
208 map {
d0e78355 209 $meta->find_method_by_name($_)
e87273a4 210 || confess( sprintf 'Action "%s" is not available from controller %s',
211 $_, ref $self )
bdd6684e 212 } keys %{ $self->_controller_actions }
fa649eb7 213 ) if ( ref $self );
4f4ab5b4 214 return uniq @methods;
9ab7d83d 215}
234763d4 216
fa649eb7 217
234763d4 218sub register_actions {
219 my ( $self, $c ) = @_;
9ab7d83d 220 $self->register_action_methods( $c, $self->get_action_methods );
221}
222
223sub register_action_methods {
224 my ( $self, $c, @methods ) = @_;
8f6cebb2 225 my $class = $self->catalyst_component_name;
ae29b412 226 #this is still not correct for some reason.
234763d4 227 my $namespace = $self->action_namespace($c);
234763d4 228
f3c5b1c9 229 # FIXME - fugly
a202886b 230 if (!blessed($self) && $self eq $c && scalar(@methods)) {
f3c5b1c9 231 my @really_bad_methods = grep { ! /^_(DISPATCH|BEGIN|AUTO|ACTION|END)$/ } map { $_->name } @methods;
232 if (scalar(@really_bad_methods)) {
233 $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.");
234 }
a202886b 235 }
d2598ac8 236
ba545c13 237 foreach my $method (@methods) {
238 my $name = $method->name;
d0f30dbc 239 # Horrible hack! All method metaclasses should have an attributes
240 # method, core Moose bug - see r13354.
10e970e4 241 my $attributes = $method->can('attributes') ? $method->attributes : [];
ba545c13 242 my $attrs = $self->_parse_attrs( $c, $name, @{ $attributes } );
234763d4 243 if ( $attrs->{Private} && ( keys %$attrs > 1 ) ) {
244 $c->log->debug( 'Bad action definition "'
ba545c13 245 . join( ' ', @{ $attributes } )
246 . qq/" for "$class->$name"/ )
234763d4 247 if $c->debug;
248 next;
249 }
bc677969 250 my $reverse = $namespace ? "${namespace}/${name}" : $name;
234763d4 251 my $action = $self->create_action(
ba545c13 252 name => $name,
253 code => $method->body,
234763d4 254 reverse => $reverse,
255 namespace => $namespace,
256 class => $class,
257 attributes => $attrs,
258 );
259
260 $c->dispatcher->register( $c, $action );
261 }
262}
263
f0a9b791 264sub action_class {
7b41db70 265 my $self = shift;
266 my %args = @_;
234763d4 267
268 my $class = (exists $args{attributes}{ActionClass}
f0a9b791 269 ? $args{attributes}{ActionClass}[0]
270 : $self->_action_class);
271
ae29b412 272 Class::MOP::load_class($class);
f0a9b791 273 return $class;
274}
275
276sub create_action {
277 my $self = shift;
278 my %args = @_;
a7e955ae 279
f0a9b791 280 my $class = $self->action_class(%args);
bf7c9c87 281 my $action_args = (
282 ref($self)
283 ? $self->action_args
284 : $self->config->{action_args}
285 );
f0a9b791 286
a7e955ae 287 my %extra_args = (
288 %{ $action_args->{'*'} || {} },
289 %{ $action_args->{ $args{name} } || {} },
290 );
291
292 return $class->new({ %extra_args, %args });
234763d4 293}
294
295sub _parse_attrs {
296 my ( $self, $c, $name, @attrs ) = @_;
297
298 my %raw_attributes;
299
300 foreach my $attr (@attrs) {
301
302 # Parse out :Foo(bar) into Foo => bar etc (and arrayify)
303
304 if ( my ( $key, $value ) = ( $attr =~ /^(.*?)(?:\(\s*(.+?)\s*\))?$/ ) )
305 {
306
307 if ( defined $value ) {
308 ( $value =~ s/^'(.*)'$/$1/ ) || ( $value =~ s/^"(.*)"/$1/ );
309 }
310 push( @{ $raw_attributes{$key} }, $value );
311 }
312 }
313
bdd6684e 314 my ($actions_config, $all_actions_config);
ae29b412 315 if( ref($self) ) {
bdd6684e 316 $actions_config = $self->_controller_actions;
317 # No, you're not getting actions => { '*' => ... } with actions in MyApp.
318 $all_actions_config = $self->_all_actions_attributes;
ae29b412 319 } else {
320 my $cfg = $self->config;
bdd6684e 321 $actions_config = $self->merge_config_hashes($cfg->{actions}, $cfg->{action});
322 $all_actions_config = {};
234763d4 323 }
324
ed9d06b6 325 %raw_attributes = (
326 %raw_attributes,
e95b2b49 327 # Note we deep copy array refs here to stop crapping on config
328 # when attributes are parsed. RT#65463
329 exists $actions_config->{$name} ? map { ref($_) eq 'ARRAY' ? [ @$_ ] : $_ } %{ $actions_config->{$name } } : (),
ed9d06b6 330 );
ae29b412 331
ed9d06b6 332 # Private actions with additional attributes will raise a warning and then
333 # be ignored. Adding '*' arguments to the default _DISPATCH / etc. methods,
334 # which are Private, will prevent those from being registered. They should
335 # probably be turned into :Actions instead, or we might want to otherwise
336 # disambiguate between those built-in internal actions and user-level
337 # Private ones.
bdd6684e 338 %raw_attributes = (%{ $all_actions_config }, %raw_attributes)
339 unless $raw_attributes{Private};
ae29b412 340
234763d4 341 my %final_attributes;
342
343 foreach my $key (keys %raw_attributes) {
344
345 my $raw = $raw_attributes{$key};
346
347 foreach my $value (ref($raw) eq 'ARRAY' ? @$raw : $raw) {
348
349 my $meth = "_parse_${key}_attr";
ae29b412 350 if ( my $code = $self->can($meth) ) {
351 ( $key, $value ) = $self->$code( $c, $name, $value );
234763d4 352 }
353 push( @{ $final_attributes{$key} }, $value );
354 }
355 }
356
357 return \%final_attributes;
358}
359
360sub _parse_Global_attr {
361 my ( $self, $c, $name, $value ) = @_;
362 return $self->_parse_Path_attr( $c, $name, "/$name" );
363}
364
365sub _parse_Absolute_attr { shift->_parse_Global_attr(@_); }
366
367sub _parse_Local_attr {
368 my ( $self, $c, $name, $value ) = @_;
369 return $self->_parse_Path_attr( $c, $name, $name );
370}
371
372sub _parse_Relative_attr { shift->_parse_Local_attr(@_); }
373
374sub _parse_Path_attr {
375 my ( $self, $c, $name, $value ) = @_;
53119b78 376 $value = '' if !defined $value;
234763d4 377 if ( $value =~ m!^/! ) {
378 return ( 'Path', $value );
379 }
380 elsif ( length $value ) {
381 return ( 'Path', join( '/', $self->path_prefix($c), $value ) );
382 }
383 else {
384 return ( 'Path', $self->path_prefix($c) );
385 }
386}
387
388sub _parse_Regex_attr {
389 my ( $self, $c, $name, $value ) = @_;
390 return ( 'Regex', $value );
391}
392
393sub _parse_Regexp_attr { shift->_parse_Regex_attr(@_); }
394
395sub _parse_LocalRegex_attr {
396 my ( $self, $c, $name, $value ) = @_;
397 unless ( $value =~ s/^\^// ) { $value = "(?:.*?)$value"; }
19c01ee1 398
399 my $prefix = $self->path_prefix( $c );
400 $prefix .= '/' if length( $prefix );
27ae4114 401
19c01ee1 402 return ( 'Regex', "^${prefix}${value}" );
234763d4 403}
404
405sub _parse_LocalRegexp_attr { shift->_parse_LocalRegex_attr(@_); }
406
f3107403 407sub _parse_Chained_attr {
408 my ($self, $c, $name, $value) = @_;
409
410 if (defined($value) && length($value)) {
411 if ($value eq '.') {
412 $value = '/'.$self->action_namespace($c);
fb56008f 413 } elsif (my ($rel, $rest) = $value =~ /^((?:\.{2}\/)+)(.*)$/) {
eb270c30 414 my @parts = split '/', $self->action_namespace($c);
fb56008f 415 my @levels = split '/', $rel;
416
417 $value = '/'.join('/', @parts[0 .. $#parts - @levels], $rest);
f3107403 418 } elsif ($value !~ m/^\//) {
419 my $action_ns = $self->action_namespace($c);
420
421 if ($action_ns) {
422 $value = '/'.join('/', $action_ns, $value);
423 } else {
424 $value = '/'.$value; # special case namespace '' (root)
425 }
426 }
427 } else {
428 $value = '/'
429 }
430
431 return Chained => $value;
432}
433
9356b981 434sub _parse_ChainedParent_attr {
435 my ($self, $c, $name, $value) = @_;
436 return $self->_parse_Chained_attr($c, $name, '../'.$name);
437}
438
e5d2cfdb 439sub _parse_PathPrefix_attr {
02825551 440 my ( $self, $c ) = @_;
441 return PathPart => $self->path_prefix($c);
e5d2cfdb 442}
443
234763d4 444sub _parse_ActionClass_attr {
445 my ( $self, $c, $name, $value ) = @_;
5d8129e9 446 my $appname = $self->_application;
447 $value = Catalyst::Utils::resolve_namespace($appname . '::Action', $self->_action_class, $value);
234763d4 448 return ( 'ActionClass', $value );
449}
450
9287719b 451sub _parse_MyAction_attr {
452 my ( $self, $c, $name, $value ) = @_;
453
454 my $appclass = Catalyst::Utils::class2appclass($self);
455 $value = "${appclass}::Action::${value}";
234763d4 456
9287719b 457 return ( 'ActionClass', $value );
458}
234763d4 459
ae29b412 460__PACKAGE__->meta->make_immutable;
461
234763d4 4621;
463
464__END__
465
466=head1 CONFIGURATION
467
a269e0c2 468Like any other L<Catalyst::Component>, controllers have a config hash,
469accessible through $self->config from the controller actions. Some
470settings are in use by the Catalyst framework:
234763d4 471
472=head2 namespace
473
a269e0c2 474This specifies the internal namespace the controller should be bound
475to. By default the controller is bound to the URI version of the
476controller name. For instance controller 'MyApp::Controller::Foo::Bar'
477will be bound to 'foo/bar'. The default Root controller is an example
478of setting namespace to '' (the null string).
234763d4 479
27ae4114 480=head2 path
234763d4 481
482Sets 'path_prefix', as described below.
483
0a2577a8 484=head2 action
485
486Allows you to set the attributes that the dispatcher creates actions out of.
487This allows you to do 'rails style routes', or override some of the
f4dda4a8 488attribute definitions of actions composed from Roles.
0a2577a8 489You can set arguments globally (for all actions of the controller) and
490specifically (for a single action).
491
492 __PACKAGE__->config(
493 action => {
494 '*' => { Chained => 'base', Args => 0 },
495 base => { Chained => '/', PathPart => '', CaptureArgs => 0 },
496 },
497 );
498
499In the case above every sub in the package would be made into a Chain
500endpoint with a URI the same as the sub name for each sub, chained
501to the sub named C<base>. Ergo dispatch to C</example> would call the
502C<base> method, then the C<example> method.
503
c8136648 504=head2 action_args
505
4d4e5de8 506Allows you to set constructor arguments on your actions. You can set arguments
0a2577a8 507globally and specifically (as above).
508This is particularly useful when using C<ActionRole>s
b939ae6b 509(L<Catalyst::Controller::ActionRole>) and custom C<ActionClass>es.
c8136648 510
b939ae6b 511 __PACKAGE__->config(
c8136648 512 action_args => {
b939ae6b 513 '*' => { globalarg1 => 'hello', globalarg2 => 'goodbye' },
514 'specific_action' => { customarg => 'arg1' },
cea3f28a 515 },
b939ae6b 516 );
cea3f28a 517
b939ae6b 518In the case above the action class associated with C<specific_action> would get
519passed the following arguments, in addition to the normal action constructor
520arguments, when it is instantiated:
521
522 (globalarg1 => 'hello', globalarg2 => 'goodbye', customarg => 'arg1')
c8136648 523
234763d4 524=head1 METHODS
525
c4d02967 526=head2 BUILDARGS ($app, @args)
234763d4 527
c4d02967 528From L<Catalyst::Component::ApplicationAttribute>, stashes the application
529instance as $self->_application.
234763d4 530
531=head2 $self->action_for('name')
532
a269e0c2 533Returns the Catalyst::Action object (if any) for a given method name
534in this component.
234763d4 535
234763d4 536=head2 $self->action_namespace($c)
537
a269e0c2 538Returns the private namespace for actions in this component. Defaults
539to a value from the controller name (for
540e.g. MyApp::Controller::Foo::Bar becomes "foo/bar") or can be
541overridden from the "namespace" config key.
234763d4 542
543
544=head2 $self->path_prefix($c)
545
e5d2cfdb 546Returns the default path prefix for :PathPrefix, :Local, :LocalRegex and
547relative :Path actions in this component. Defaults to the action_namespace or
a269e0c2 548can be overridden from the "path" config key.
234763d4 549
c4d02967 550=head2 $self->register_actions($c)
551
552Finds all applicable actions for this component, creates
553Catalyst::Action objects (using $self->create_action) for them and
554registers them with $c->dispatcher.
555
556=head2 $self->get_action_methods()
557
558Returns a list of L<Moose::Meta::Method> objects, doing the
559L<MooseX::MethodAttributes::Role::Meta::Method> role, which are the set of
560action methods for this package.
561
562=head2 $self->register_action_methods($c, @methods)
563
564Creates action objects for a set of action methods using C< create_action >,
565and registers them with the dispatcher.
566
f0a9b791 567=head2 $self->action_class(%args)
568
569Used when a controller is creating an action to determine the correct base
24d2dfaf 570action class to use.
f0a9b791 571
234763d4 572=head2 $self->create_action(%args)
573
a269e0c2 574Called with a hash of data to be use for construction of a new
575Catalyst::Action (or appropriate sub/alternative class) object.
234763d4 576
a269e0c2 577=head2 $self->_application
234763d4 578
579=head2 $self->_app
580
581Returns the application instance stored by C<new()>
5ee249f2 582
0bf7ab71 583=head1 AUTHORS
5ee249f2 584
0bf7ab71 585Catalyst Contributors, see Catalyst.pm
5ee249f2 586
587=head1 COPYRIGHT
588
536bee89 589This library is free software. You can redistribute it and/or modify
a269e0c2 590it under the same terms as Perl itself.
5ee249f2 591
592=cut