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