case_sensitive config setting removed
[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
16has path_prefix =>
17 (
18 is => 'rw',
19 isa => 'Str',
7f22a5aa 20 init_arg => 'path',
ae29b412 21 predicate => 'has_path_prefix',
22 );
23
24has action_namespace =>
25 (
26 is => 'rw',
27 isa => 'Str',
7f22a5aa 28 init_arg => 'namespace',
ae29b412 29 predicate => 'has_action_namespace',
30 );
31
b138a0d0 32has actions =>
ae29b412 33 (
b138a0d0 34 accessor => '_controller_actions',
ae29b412 35 isa => 'HashRef',
7f22a5aa 36 init_arg => undef,
ae29b412 37 );
38
7f22a5aa 39sub BUILD {
40 my ($self, $args) = @_;
ae29b412 41 my $action = delete $args->{action} || {};
42 my $actions = delete $args->{actions} || {};
7f22a5aa 43 my $attr_value = $self->merge_config_hashes($actions, $action);
44 $self->_controller_actions($attr_value);
45}
5ee249f2 46
d0e5dfb5 47
48
5ee249f2 49=head1 NAME
50
51Catalyst::Controller - Catalyst Controller base class
52
53=head1 SYNOPSIS
54
234763d4 55 package MyApp::Controller::Search
a269e0c2 56 use base qw/Catalyst::Controller/;
234763d4 57
27ae4114 58 sub foo : Local {
85d9fce6 59 my ($self,$c,@args) = @_;
27ae4114 60 ...
234763d4 61 } # Dispatches to /search/foo
5ee249f2 62
63=head1 DESCRIPTION
64
a269e0c2 65Controllers are where the actions in the Catalyst framework
66reside. Each action is represented by a function with an attribute to
67identify what kind of action it is. See the L<Catalyst::Dispatcher>
68for more info about how Catalyst dispatches to actions.
234763d4 69
70=cut
71
ae29b412 72#I think both of these could be attributes. doesn't really seem like they need
73#to ble class data. i think that attributes +default would work just fine
234763d4 74__PACKAGE__->mk_classdata($_) for qw/_dispatch_steps _action_class/;
75
76__PACKAGE__->_dispatch_steps( [qw/_BEGIN _AUTO _ACTION/] );
77__PACKAGE__->_action_class('Catalyst::Action');
78
234763d4 79
80sub _DISPATCH : Private {
81 my ( $self, $c ) = @_;
82
83 foreach my $disp ( @{ $self->_dispatch_steps } ) {
84 last unless $c->forward($disp);
85 }
86
87 $c->forward('_END');
88}
89
90sub _BEGIN : Private {
91 my ( $self, $c ) = @_;
92 my $begin = ( $c->get_actions( 'begin', $c->namespace ) )[-1];
93 return 1 unless $begin;
94 $begin->dispatch( $c );
95 return !@{ $c->error };
96}
97
98sub _AUTO : Private {
99 my ( $self, $c ) = @_;
100 my @auto = $c->get_actions( 'auto', $c->namespace );
101 foreach my $auto (@auto) {
102 $auto->dispatch( $c );
103 return 0 unless $c->state;
104 }
105 return 1;
106}
107
108sub _ACTION : Private {
109 my ( $self, $c ) = @_;
110 if ( ref $c->action
111 && $c->action->can('execute')
53119b78 112 && defined $c->req->action )
234763d4 113 {
114 $c->action->dispatch( $c );
115 }
116 return !@{ $c->error };
117}
118
119sub _END : Private {
120 my ( $self, $c ) = @_;
121 my $end = ( $c->get_actions( 'end', $c->namespace ) )[-1];
122 return 1 unless $end;
123 $end->dispatch( $c );
124 return !@{ $c->error };
125}
126
234763d4 127sub action_for {
128 my ( $self, $name ) = @_;
129 my $app = ($self->isa('Catalyst') ? $self : $self->_application);
130 return $app->dispatcher->get_action($name, $self->action_namespace);
131}
132
27ae4114 133#my opinion is that this whole sub really should be a builder method, not
ae29b412 134#something that happens on every call. Anyone else disagree?? -- groditi
135## -- apparently this is all just waiting for app/ctx split
136around action_namespace => sub {
137 my $orig = shift;
234763d4 138 my ( $self, $c ) = @_;
ae29b412 139
df960201 140 my $class = ref($self) || $self;
ae29b412 141 if( ref($self) ){
142 return $self->$orig if $self->has_action_namespace;
143 } else {
df960201 144 return $class->config->{namespace} if exists $class->config->{namespace};
234763d4 145 }
234763d4 146
40a21eff 147 my $namespace = Catalyst::Utils::class2prefix($self->catalyst_component_name) || '';
ae29b412 148 $self->$orig($namespace) if ref($self);
149 return $namespace;
150};
151
152#Once again, this is probably better written as a builder method
153around path_prefix => sub {
154 my $orig = shift;
155 my $self = shift;
156 if( ref($self) ){
157 return $self->$orig if $self->has_path_prefix;
158 } else {
159 return $self->config->{path} if exists $self->config->{path};
160 }
161 my $namespace = $self->action_namespace(@_);
162 $self->$orig($namespace) if ref($self);
163 return $namespace;
164};
234763d4 165
9ab7d83d 166sub get_action_methods {
167 my $self = shift;
2bf074ab 168 my $meta = find_meta($self) || confess("No metaclass setup for $self");
d0e5dfb5 169 confess("Metaclass "
fa649eb7 170 . ref($meta) . " for "
171 . $meta->name
172 . " cannot support register_actions." )
173 unless $meta->can('get_nearest_methods_with_attributes');
cf37d21a 174 my @methods = $meta->get_nearest_methods_with_attributes;
fa649eb7 175
176 # actions specified via config are also action_methods
177 push(
178 @methods,
179 map {
d0e78355 180 $meta->find_method_by_name($_)
1b79e199 181 || confess( 'Action "'
fa649eb7 182 . $_
183 . '" is not available from controller '
184 . ( ref $self ) )
185 } keys %{ $self->_controller_actions }
186 ) if ( ref $self );
4f4ab5b4 187 return uniq @methods;
9ab7d83d 188}
234763d4 189
fa649eb7 190
234763d4 191sub register_actions {
192 my ( $self, $c ) = @_;
9ab7d83d 193 $self->register_action_methods( $c, $self->get_action_methods );
194}
195
196sub register_action_methods {
197 my ( $self, $c, @methods ) = @_;
8f6cebb2 198 my $class = $self->catalyst_component_name;
ae29b412 199 #this is still not correct for some reason.
234763d4 200 my $namespace = $self->action_namespace($c);
234763d4 201
f3c5b1c9 202 # FIXME - fugly
a202886b 203 if (!blessed($self) && $self eq $c && scalar(@methods)) {
f3c5b1c9 204 my @really_bad_methods = grep { ! /^_(DISPATCH|BEGIN|AUTO|ACTION|END)$/ } map { $_->name } @methods;
205 if (scalar(@really_bad_methods)) {
206 $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.");
207 }
a202886b 208 }
d2598ac8 209
ba545c13 210 foreach my $method (@methods) {
211 my $name = $method->name;
212 my $attributes = $method->attributes;
ba545c13 213 my $attrs = $self->_parse_attrs( $c, $name, @{ $attributes } );
234763d4 214 if ( $attrs->{Private} && ( keys %$attrs > 1 ) ) {
215 $c->log->debug( 'Bad action definition "'
ba545c13 216 . join( ' ', @{ $attributes } )
217 . qq/" for "$class->$name"/ )
234763d4 218 if $c->debug;
219 next;
220 }
bc677969 221 my $reverse = $namespace ? "${namespace}/${name}" : $name;
234763d4 222 my $action = $self->create_action(
ba545c13 223 name => $name,
224 code => $method->body,
234763d4 225 reverse => $reverse,
226 namespace => $namespace,
227 class => $class,
228 attributes => $attrs,
229 );
230
231 $c->dispatcher->register( $c, $action );
232 }
233}
234
235sub create_action {
236 my $self = shift;
237 my %args = @_;
238
239 my $class = (exists $args{attributes}{ActionClass}
240 ? $args{attributes}{ActionClass}[0]
241 : $self->_action_class);
242
ae29b412 243 Class::MOP::load_class($class);
234763d4 244 return $class->new( \%args );
245}
246
247sub _parse_attrs {
248 my ( $self, $c, $name, @attrs ) = @_;
249
250 my %raw_attributes;
251
252 foreach my $attr (@attrs) {
253
254 # Parse out :Foo(bar) into Foo => bar etc (and arrayify)
255
256 if ( my ( $key, $value ) = ( $attr =~ /^(.*?)(?:\(\s*(.+?)\s*\))?$/ ) )
257 {
258
259 if ( defined $value ) {
260 ( $value =~ s/^'(.*)'$/$1/ ) || ( $value =~ s/^"(.*)"/$1/ );
261 }
262 push( @{ $raw_attributes{$key} }, $value );
263 }
264 }
265
ae29b412 266 #I know that the original behavior was to ignore action if actions was set
267 # but i actually think this may be a little more sane? we can always remove
268 # the merge behavior quite easily and go back to having actions have
269 # presedence over action by modifying the keys. i honestly think this is
270 # superior while mantaining really high degree of compat
271 my $actions;
272 if( ref($self) ) {
637be086 273 $actions = $self->_controller_actions;
ae29b412 274 } else {
275 my $cfg = $self->config;
276 $actions = $self->merge_config_hashes($cfg->{actions}, $cfg->{action});
234763d4 277 }
278
ae29b412 279 %raw_attributes = ((exists $actions->{'*'} ? %{$actions->{'*'}} : ()),
280 %raw_attributes,
281 (exists $actions->{$name} ? %{$actions->{$name}} : ()));
282
283
234763d4 284 my %final_attributes;
285
286 foreach my $key (keys %raw_attributes) {
287
288 my $raw = $raw_attributes{$key};
289
290 foreach my $value (ref($raw) eq 'ARRAY' ? @$raw : $raw) {
291
292 my $meth = "_parse_${key}_attr";
ae29b412 293 if ( my $code = $self->can($meth) ) {
294 ( $key, $value ) = $self->$code( $c, $name, $value );
234763d4 295 }
296 push( @{ $final_attributes{$key} }, $value );
297 }
298 }
299
300 return \%final_attributes;
301}
302
303sub _parse_Global_attr {
304 my ( $self, $c, $name, $value ) = @_;
305 return $self->_parse_Path_attr( $c, $name, "/$name" );
306}
307
308sub _parse_Absolute_attr { shift->_parse_Global_attr(@_); }
309
310sub _parse_Local_attr {
311 my ( $self, $c, $name, $value ) = @_;
312 return $self->_parse_Path_attr( $c, $name, $name );
313}
314
315sub _parse_Relative_attr { shift->_parse_Local_attr(@_); }
316
317sub _parse_Path_attr {
318 my ( $self, $c, $name, $value ) = @_;
53119b78 319 $value = '' if !defined $value;
234763d4 320 if ( $value =~ m!^/! ) {
321 return ( 'Path', $value );
322 }
323 elsif ( length $value ) {
324 return ( 'Path', join( '/', $self->path_prefix($c), $value ) );
325 }
326 else {
327 return ( 'Path', $self->path_prefix($c) );
328 }
329}
330
331sub _parse_Regex_attr {
332 my ( $self, $c, $name, $value ) = @_;
333 return ( 'Regex', $value );
334}
335
336sub _parse_Regexp_attr { shift->_parse_Regex_attr(@_); }
337
338sub _parse_LocalRegex_attr {
339 my ( $self, $c, $name, $value ) = @_;
340 unless ( $value =~ s/^\^// ) { $value = "(?:.*?)$value"; }
19c01ee1 341
342 my $prefix = $self->path_prefix( $c );
343 $prefix .= '/' if length( $prefix );
27ae4114 344
19c01ee1 345 return ( 'Regex', "^${prefix}${value}" );
234763d4 346}
347
348sub _parse_LocalRegexp_attr { shift->_parse_LocalRegex_attr(@_); }
349
f3107403 350sub _parse_Chained_attr {
351 my ($self, $c, $name, $value) = @_;
352
353 if (defined($value) && length($value)) {
354 if ($value eq '.') {
355 $value = '/'.$self->action_namespace($c);
fb56008f 356 } elsif (my ($rel, $rest) = $value =~ /^((?:\.{2}\/)+)(.*)$/) {
eb270c30 357 my @parts = split '/', $self->action_namespace($c);
fb56008f 358 my @levels = split '/', $rel;
359
360 $value = '/'.join('/', @parts[0 .. $#parts - @levels], $rest);
f3107403 361 } elsif ($value !~ m/^\//) {
362 my $action_ns = $self->action_namespace($c);
363
364 if ($action_ns) {
365 $value = '/'.join('/', $action_ns, $value);
366 } else {
367 $value = '/'.$value; # special case namespace '' (root)
368 }
369 }
370 } else {
371 $value = '/'
372 }
373
374 return Chained => $value;
375}
376
9356b981 377sub _parse_ChainedParent_attr {
378 my ($self, $c, $name, $value) = @_;
379 return $self->_parse_Chained_attr($c, $name, '../'.$name);
380}
381
e5d2cfdb 382sub _parse_PathPrefix_attr {
02825551 383 my ( $self, $c ) = @_;
384 return PathPart => $self->path_prefix($c);
e5d2cfdb 385}
386
234763d4 387sub _parse_ActionClass_attr {
388 my ( $self, $c, $name, $value ) = @_;
5d8129e9 389 my $appname = $self->_application;
390 $value = Catalyst::Utils::resolve_namespace($appname . '::Action', $self->_action_class, $value);
234763d4 391 return ( 'ActionClass', $value );
392}
393
9287719b 394sub _parse_MyAction_attr {
395 my ( $self, $c, $name, $value ) = @_;
396
397 my $appclass = Catalyst::Utils::class2appclass($self);
398 $value = "${appclass}::Action::${value}";
234763d4 399
9287719b 400 return ( 'ActionClass', $value );
401}
234763d4 402
ae29b412 403__PACKAGE__->meta->make_immutable;
404
234763d4 4051;
406
407__END__
408
409=head1 CONFIGURATION
410
a269e0c2 411Like any other L<Catalyst::Component>, controllers have a config hash,
412accessible through $self->config from the controller actions. Some
413settings are in use by the Catalyst framework:
234763d4 414
415=head2 namespace
416
a269e0c2 417This specifies the internal namespace the controller should be bound
418to. By default the controller is bound to the URI version of the
419controller name. For instance controller 'MyApp::Controller::Foo::Bar'
420will be bound to 'foo/bar'. The default Root controller is an example
421of setting namespace to '' (the null string).
234763d4 422
27ae4114 423=head2 path
234763d4 424
425Sets 'path_prefix', as described below.
426
427=head1 METHODS
428
c4d02967 429=head2 BUILDARGS ($app, @args)
234763d4 430
c4d02967 431From L<Catalyst::Component::ApplicationAttribute>, stashes the application
432instance as $self->_application.
234763d4 433
434=head2 $self->action_for('name')
435
a269e0c2 436Returns the Catalyst::Action object (if any) for a given method name
437in this component.
234763d4 438
234763d4 439=head2 $self->action_namespace($c)
440
a269e0c2 441Returns the private namespace for actions in this component. Defaults
442to a value from the controller name (for
443e.g. MyApp::Controller::Foo::Bar becomes "foo/bar") or can be
444overridden from the "namespace" config key.
234763d4 445
446
447=head2 $self->path_prefix($c)
448
e5d2cfdb 449Returns the default path prefix for :PathPrefix, :Local, :LocalRegex and
450relative :Path actions in this component. Defaults to the action_namespace or
a269e0c2 451can be overridden from the "path" config key.
234763d4 452
c4d02967 453=head2 $self->register_actions($c)
454
455Finds all applicable actions for this component, creates
456Catalyst::Action objects (using $self->create_action) for them and
457registers them with $c->dispatcher.
458
459=head2 $self->get_action_methods()
460
461Returns a list of L<Moose::Meta::Method> objects, doing the
462L<MooseX::MethodAttributes::Role::Meta::Method> role, which are the set of
463action methods for this package.
464
465=head2 $self->register_action_methods($c, @methods)
466
467Creates action objects for a set of action methods using C< create_action >,
468and registers them with the dispatcher.
469
234763d4 470=head2 $self->create_action(%args)
471
a269e0c2 472Called with a hash of data to be use for construction of a new
473Catalyst::Action (or appropriate sub/alternative class) object.
234763d4 474
a269e0c2 475=head2 $self->_application
234763d4 476
477=head2 $self->_app
478
479Returns the application instance stored by C<new()>
5ee249f2 480
0bf7ab71 481=head1 AUTHORS
5ee249f2 482
0bf7ab71 483Catalyst Contributors, see Catalyst.pm
5ee249f2 484
485=head1 COPYRIGHT
486
536bee89 487This library is free software. You can redistribute it and/or modify
a269e0c2 488it under the same terms as Perl itself.
5ee249f2 489
490=cut