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