Context for TestAppPluginWithConstructor
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Controller.pm
1 package Catalyst::Controller;
2
3 use Moose;
4 use Moose::Util qw/find_meta/;
5 use List::MoreUtils qw/uniq/;
6 use namespace::clean -except => 'meta';
7
8 BEGIN { extends qw/Catalyst::Component MooseX::MethodAttributes::Inheritable/; }
9
10 use MooseX::MethodAttributes;
11 use Catalyst::Exception;
12 use Catalyst::Utils;
13
14 with 'Catalyst::Component::ApplicationAttribute';
15
16 has path_prefix =>
17     (
18      is => 'rw',
19      isa => 'Str',
20      init_arg => 'path',
21      predicate => 'has_path_prefix',
22     );
23
24 has action_namespace =>
25     (
26      is => 'rw',
27      isa => 'Str',
28      init_arg => 'namespace',
29      predicate => 'has_action_namespace',
30     );
31
32 has actions =>
33     (
34      accessor => '_controller_actions',
35      isa => 'HashRef',
36      init_arg => undef,
37     );
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->_controller_actions($attr_value);
45 }
46
47
48
49 =head1 NAME
50
51 Catalyst::Controller - Catalyst Controller base class
52
53 =head1 SYNOPSIS
54
55   package MyApp::Controller::Search
56   use base qw/Catalyst::Controller/;
57
58   sub foo : Local {
59     my ($self,$c,@args) = @_;
60     ...
61   } # Dispatches to /search/foo
62
63 =head1 DESCRIPTION
64
65 Controllers are where the actions in the Catalyst framework
66 reside. Each action is represented by a function with an attribute to
67 identify what kind of action it is. See the L<Catalyst::Dispatcher>
68 for more info about how Catalyst dispatches to actions.
69
70 =cut
71
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
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
79
80 sub _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
90 sub _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
98 sub _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
108 sub _ACTION : Private {
109     my ( $self, $c ) = @_;
110     if (   ref $c->action
111         && $c->action->can('execute')
112         && defined $c->req->action )
113     {
114         $c->action->dispatch( $c );
115     }
116     return !@{ $c->error };
117 }
118
119 sub _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
127 sub 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
133 #my opinion is that this whole sub really should be a builder method, not
134 #something that happens on every call. Anyone else disagree?? -- groditi
135 ## -- apparently this is all just waiting for app/ctx split
136 around action_namespace => sub {
137     my $orig = shift;
138     my ( $self, $c ) = @_;
139
140     my $class = ref($self) || $self;
141     if( ref($self) ){
142         return $self->$orig if $self->has_action_namespace;
143     } else {
144         return $class->config->{namespace} if exists $class->config->{namespace};
145     }
146
147     my $namespace = Catalyst::Utils::class2prefix($self->catalyst_component_name) || '';
148     $self->$orig($namespace) if ref($self);
149     return $namespace;
150 };
151
152 #Once again, this is probably better written as a builder method
153 around 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 };
165
166 sub get_action_methods {
167     my $self = shift;
168     my $meta = find_meta($self) || confess("No metaclass setup for $self");
169     confess("Metaclass "
170           . ref($meta) . " for "
171           . $meta->name
172           . " cannot support register_actions." )
173       unless $meta->can('get_nearest_methods_with_attributes');
174     my @methods = $meta->get_nearest_methods_with_attributes;
175
176     # actions specified via config are also action_methods
177     push(
178         @methods,
179         map {
180             $meta->find_method_by_name($_)
181               || confess( 'Action "'
182                   . $_
183                   . '" is not available from controller '
184                   . ( ref $self ) )
185           } keys %{ $self->_controller_actions }
186     ) if ( ref $self );
187     return uniq @methods;
188 }
189
190
191 sub register_actions {
192     my ( $self, $c ) = @_;
193     $self->register_action_methods( $c, $self->get_action_methods );
194 }
195
196 sub register_action_methods {
197     my ( $self, $c, @methods ) = @_;
198     my $class = $self->catalyst_component_name;
199     #this is still not correct for some reason.
200     my $namespace = $self->action_namespace($c);
201
202     # FIXME - fugly
203     if (!blessed($self) && $self eq $c && scalar(@methods)) {
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         }
208     }
209
210     foreach my $method (@methods) {
211         my $name = $method->name;
212         my $attributes = $method->attributes;
213         my $attrs = $self->_parse_attrs( $c, $name, @{ $attributes } );
214         if ( $attrs->{Private} && ( keys %$attrs > 1 ) ) {
215             $c->log->debug( 'Bad action definition "'
216                   . join( ' ', @{ $attributes } )
217                   . qq/" for "$class->$name"/ )
218               if $c->debug;
219             next;
220         }
221         my $reverse = $namespace ? "${namespace}/${name}" : $name;
222         my $action = $self->create_action(
223             name       => $name,
224             code       => $method->body,
225             reverse    => $reverse,
226             namespace  => $namespace,
227             class      => $class,
228             attributes => $attrs,
229         );
230
231         $c->dispatcher->register( $c, $action );
232     }
233 }
234
235 sub 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
243     Class::MOP::load_class($class);
244     return $class->new( \%args );
245 }
246
247 sub _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
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) ) {
273         $actions = $self->_controller_actions;
274     } else {
275         my $cfg = $self->config;
276         $actions = $self->merge_config_hashes($cfg->{actions}, $cfg->{action});
277     }
278
279     %raw_attributes = ((exists $actions->{'*'} ? %{$actions->{'*'}} : ()),
280                        %raw_attributes,
281                        (exists $actions->{$name} ? %{$actions->{$name}} : ()));
282
283
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";
293             if ( my $code = $self->can($meth) ) {
294                 ( $key, $value ) = $self->$code( $c, $name, $value );
295             }
296             push( @{ $final_attributes{$key} }, $value );
297         }
298     }
299
300     return \%final_attributes;
301 }
302
303 sub _parse_Global_attr {
304     my ( $self, $c, $name, $value ) = @_;
305     return $self->_parse_Path_attr( $c, $name, "/$name" );
306 }
307
308 sub _parse_Absolute_attr { shift->_parse_Global_attr(@_); }
309
310 sub _parse_Local_attr {
311     my ( $self, $c, $name, $value ) = @_;
312     return $self->_parse_Path_attr( $c, $name, $name );
313 }
314
315 sub _parse_Relative_attr { shift->_parse_Local_attr(@_); }
316
317 sub _parse_Path_attr {
318     my ( $self, $c, $name, $value ) = @_;
319     $value = '' if !defined $value;
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
331 sub _parse_Regex_attr {
332     my ( $self, $c, $name, $value ) = @_;
333     return ( 'Regex', $value );
334 }
335
336 sub _parse_Regexp_attr { shift->_parse_Regex_attr(@_); }
337
338 sub _parse_LocalRegex_attr {
339     my ( $self, $c, $name, $value ) = @_;
340     unless ( $value =~ s/^\^// ) { $value = "(?:.*?)$value"; }
341
342     my $prefix = $self->path_prefix( $c );
343     $prefix .= '/' if length( $prefix );
344
345     return ( 'Regex', "^${prefix}${value}" );
346 }
347
348 sub _parse_LocalRegexp_attr { shift->_parse_LocalRegex_attr(@_); }
349
350 sub _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);
356         } elsif (my ($rel, $rest) = $value =~ /^((?:\.{2}\/)+)(.*)$/) {
357             my @parts = split '/', $self->action_namespace($c);
358             my @levels = split '/', $rel;
359
360             $value = '/'.join('/', @parts[0 .. $#parts - @levels], $rest);
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
377 sub _parse_ChainedParent_attr {
378     my ($self, $c, $name, $value) = @_;
379     return $self->_parse_Chained_attr($c, $name, '../'.$name);
380 }
381
382 sub _parse_PathPrefix_attr {
383     my ( $self, $c ) = @_;
384     return PathPart => $self->path_prefix($c);
385 }
386
387 sub _parse_ActionClass_attr {
388     my ( $self, $c, $name, $value ) = @_;
389     my $appname = $self->_application;
390     $value = Catalyst::Utils::resolve_namespace($appname . '::Action', $self->_action_class, $value);
391     return ( 'ActionClass', $value );
392 }
393
394 sub _parse_MyAction_attr {
395     my ( $self, $c, $name, $value ) = @_;
396
397     my $appclass = Catalyst::Utils::class2appclass($self);
398     $value = "${appclass}::Action::${value}";
399
400     return ( 'ActionClass', $value );
401 }
402
403 __PACKAGE__->meta->make_immutable;
404
405 1;
406
407 __END__
408
409 =head1 CONFIGURATION
410
411 Like any other L<Catalyst::Component>, controllers have a config hash,
412 accessible through $self->config from the controller actions.  Some
413 settings are in use by the Catalyst framework:
414
415 =head2 namespace
416
417 This specifies the internal namespace the controller should be bound
418 to. By default the controller is bound to the URI version of the
419 controller name. For instance controller 'MyApp::Controller::Foo::Bar'
420 will be bound to 'foo/bar'. The default Root controller is an example
421 of setting namespace to '' (the null string).
422
423 =head2 path
424
425 Sets 'path_prefix', as described below.
426
427 =head1 METHODS
428
429 =head2 BUILDARGS ($app, @args)
430
431 From L<Catalyst::Component::ApplicationAttribute>, stashes the application
432 instance as $self->_application.
433
434 =head2 $self->action_for('name')
435
436 Returns the Catalyst::Action object (if any) for a given method name
437 in this component.
438
439 =head2 $self->action_namespace($c)
440
441 Returns the private namespace for actions in this component. Defaults
442 to a value from the controller name (for
443 e.g. MyApp::Controller::Foo::Bar becomes "foo/bar") or can be
444 overridden from the "namespace" config key.
445
446
447 =head2 $self->path_prefix($c)
448
449 Returns the default path prefix for :PathPrefix, :Local, :LocalRegex and
450 relative :Path actions in this component. Defaults to the action_namespace or
451 can be overridden from the "path" config key.
452
453 =head2 $self->register_actions($c)
454
455 Finds all applicable actions for this component, creates
456 Catalyst::Action objects (using $self->create_action) for them and
457 registers them with $c->dispatcher.
458
459 =head2 $self->get_action_methods()
460
461 Returns a list of L<Moose::Meta::Method> objects, doing the
462 L<MooseX::MethodAttributes::Role::Meta::Method> role, which are the set of
463 action methods for this package.
464
465 =head2 $self->register_action_methods($c, @methods)
466
467 Creates action objects for a set of action methods using C< create_action >,
468 and registers them with the dispatcher.
469
470 =head2 $self->create_action(%args)
471
472 Called with a hash of data to be use for construction of a new
473 Catalyst::Action (or appropriate sub/alternative class) object.
474
475 =head2 $self->_application
476
477 =head2 $self->_app
478
479 Returns the application instance stored by C<new()>
480
481 =head1 AUTHORS
482
483 Catalyst Contributors, see Catalyst.pm
484
485 =head1 COPYRIGHT
486
487 This library is free software. You can redistribute it and/or modify
488 it under the same terms as Perl itself.
489
490 =cut