fb6df0dfd170c3b1e51729dee95a4f186231c2cf
[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     is        => 'rw',
18     isa       => 'Str',
19     init_arg  => 'path',
20     predicate => 'has_path_prefix',
21 );
22
23 has action_namespace => (
24     is        => 'rw',
25     isa       => 'Str',
26     init_arg  => 'namespace',
27     predicate => 'has_action_namespace',
28 );
29
30 has actions => (
31     accessor => '_controller_actions',
32     isa      => 'HashRef',
33     init_arg => undef,
34 );
35
36 has action_args => (is => 'ro');
37
38 # ->config(actions => { '*' => ...
39 has _all_actions_attributes => (
40     is       => 'ro',
41     isa      => 'HashRef',
42     init_arg => undef,
43     lazy     => 1,
44     builder  => '_build__all_actions_attributes',
45 );
46
47 sub BUILD {
48     my ($self, $args) = @_;
49     my $action  = delete $args->{action}  || {};
50     my $actions = delete $args->{actions} || {};
51     my $attr_value = $self->merge_config_hashes($actions, $action);
52     $self->_controller_actions($attr_value);
53
54     # trigger lazy builder
55     $self->_all_actions_attributes;
56 }
57
58 sub _build__all_actions_attributes {
59     my ($self) = @_;
60     delete $self->_controller_actions->{'*'} || {};
61 }
62
63 =head1 NAME
64
65 Catalyst::Controller - Catalyst Controller base class
66
67 =head1 SYNOPSIS
68
69   package MyApp::Controller::Search
70   use base qw/Catalyst::Controller/;
71
72   sub foo : Local {
73     my ($self,$c,@args) = @_;
74     ...
75   } # Dispatches to /search/foo
76
77 =head1 DESCRIPTION
78
79 Controllers are where the actions in the Catalyst framework
80 reside. Each action is represented by a function with an attribute to
81 identify what kind of action it is. See the L<Catalyst::Dispatcher>
82 for more info about how Catalyst dispatches to actions.
83
84 =cut
85
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
88 __PACKAGE__->mk_classdata($_) for qw/_dispatch_steps _action_class/;
89
90 __PACKAGE__->_dispatch_steps( [qw/_BEGIN _AUTO _ACTION/] );
91 __PACKAGE__->_action_class('Catalyst::Action');
92
93
94 sub _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
104 sub _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
112 sub _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
122 sub _ACTION : Private {
123     my ( $self, $c ) = @_;
124     if (   ref $c->action
125         && $c->action->can('execute')
126         && defined $c->req->action )
127     {
128         $c->action->dispatch( $c );
129     }
130     return !@{ $c->error };
131 }
132
133 sub _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
141 sub 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
147 #my opinion is that this whole sub really should be a builder method, not
148 #something that happens on every call. Anyone else disagree?? -- groditi
149 ## -- apparently this is all just waiting for app/ctx split
150 around action_namespace => sub {
151     my $orig = shift;
152     my ( $self, $c ) = @_;
153
154     my $class = ref($self) || $self;
155     my $appclass = ref($c) || $c;
156     if( ref($self) ){
157         return $self->$orig if $self->has_action_namespace;
158     } else {
159         return $class->config->{namespace} if exists $class->config->{namespace};
160     }
161
162     my $case_s;
163     if( $c ){
164         $case_s = $appclass->config->{case_sensitive};
165     } else {
166         if ($self->isa('Catalyst')) {
167             $case_s = $class->config->{case_sensitive};
168         } else {
169             if (ref $self) {
170                 $case_s = ref($self->_application)->config->{case_sensitive};
171             } else {
172                 confess("Can't figure out case_sensitive setting");
173             }
174         }
175     }
176
177     my $namespace = Catalyst::Utils::class2prefix($self->catalyst_component_name, $case_s) || '';
178     $self->$orig($namespace) if ref($self);
179     return $namespace;
180 };
181
182 #Once again, this is probably better written as a builder method
183 around 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 };
195
196 sub get_action_methods {
197     my $self = shift;
198     my $meta = find_meta($self) || confess("No metaclass setup for $self");
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');
203     my @methods = $meta->get_nearest_methods_with_attributes;
204
205     # actions specified via config are also action_methods
206     push(
207         @methods,
208         map {
209             $meta->find_method_by_name($_)
210                 || confess( sprintf 'Action "%s" is not available from controller %s',
211                             $_, ref $self )
212         } keys %{ $self->_controller_actions }
213     ) if ( ref $self );
214     return uniq @methods;
215 }
216
217
218 sub register_actions {
219     my ( $self, $c ) = @_;
220     $self->register_action_methods( $c, $self->get_action_methods );
221 }
222
223 sub register_action_methods {
224     my ( $self, $c, @methods ) = @_;
225     my $class = $self->catalyst_component_name;
226     #this is still not correct for some reason.
227     my $namespace = $self->action_namespace($c);
228
229     # FIXME - fugly
230     if (!blessed($self) && $self eq $c && scalar(@methods)) {
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         }
235     }
236
237     foreach my $method (@methods) {
238         my $name = $method->name;
239         # Horrible hack! All method metaclasses should have an attributes
240         # method, core Moose bug - see r13354.
241         my $attributes = $method->can('attributes') ? $method->attributes : [];
242         my $attrs = $self->_parse_attrs( $c, $name, @{ $attributes } );
243         if ( $attrs->{Private} && ( keys %$attrs > 1 ) ) {
244             $c->log->debug( 'Bad action definition "'
245                   . join( ' ', @{ $attributes } )
246                   . qq/" for "$class->$name"/ )
247               if $c->debug;
248             next;
249         }
250         my $reverse = $namespace ? "${namespace}/${name}" : $name;
251         my $action = $self->create_action(
252             name       => $name,
253             code       => $method->body,
254             reverse    => $reverse,
255             namespace  => $namespace,
256             class      => $class,
257             attributes => $attrs,
258         );
259
260         $c->dispatcher->register( $c, $action );
261     }
262 }
263
264 sub action_class {
265     my $self = shift;
266     my %args = @_;
267
268     my $class = (exists $args{attributes}{ActionClass}
269         ? $args{attributes}{ActionClass}[0]
270         : $self->_action_class);
271
272     Class::MOP::load_class($class);
273     return $class;
274 }
275
276 sub create_action {
277     my $self = shift;
278     my %args = @_;
279
280     my $class = $self->action_class(%args);
281     my $action_args = (
282         ref($self)
283             ? $self->action_args
284             : $self->config->{action_args}
285     );
286
287     my %extra_args = (
288         %{ $action_args->{'*'}           || {} },
289         %{ $action_args->{ $args{name} } || {} },
290     );
291
292     return $class->new({ %extra_args, %args });
293 }
294
295 sub _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
314     my ($actions_config, $all_actions_config);
315     if( ref($self) ) {
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;
319     } else {
320         my $cfg = $self->config;
321         $actions_config = $self->merge_config_hashes($cfg->{actions}, $cfg->{action});
322         $all_actions_config = {};
323     }
324
325     %raw_attributes = (
326         %raw_attributes,
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 } } : (),
330     );
331
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.
338     %raw_attributes = (%{ $all_actions_config }, %raw_attributes)
339         unless $raw_attributes{Private};
340
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";
350             if ( my $code = $self->can($meth) ) {
351                 ( $key, $value ) = $self->$code( $c, $name, $value );
352             }
353             push( @{ $final_attributes{$key} }, $value );
354         }
355     }
356
357     return \%final_attributes;
358 }
359
360 sub _parse_Global_attr {
361     my ( $self, $c, $name, $value ) = @_;
362     return $self->_parse_Path_attr( $c, $name, "/$name" );
363 }
364
365 sub _parse_Absolute_attr { shift->_parse_Global_attr(@_); }
366
367 sub _parse_Local_attr {
368     my ( $self, $c, $name, $value ) = @_;
369     return $self->_parse_Path_attr( $c, $name, $name );
370 }
371
372 sub _parse_Relative_attr { shift->_parse_Local_attr(@_); }
373
374 sub _parse_Path_attr {
375     my ( $self, $c, $name, $value ) = @_;
376     $value = '' if !defined $value;
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
388 sub _parse_Regex_attr {
389     my ( $self, $c, $name, $value ) = @_;
390     return ( 'Regex', $value );
391 }
392
393 sub _parse_Regexp_attr { shift->_parse_Regex_attr(@_); }
394
395 sub _parse_LocalRegex_attr {
396     my ( $self, $c, $name, $value ) = @_;
397     unless ( $value =~ s/^\^// ) { $value = "(?:.*?)$value"; }
398
399     my $prefix = $self->path_prefix( $c );
400     $prefix .= '/' if length( $prefix );
401
402     return ( 'Regex', "^${prefix}${value}" );
403 }
404
405 sub _parse_LocalRegexp_attr { shift->_parse_LocalRegex_attr(@_); }
406
407 sub _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);
413         } elsif (my ($rel, $rest) = $value =~ /^((?:\.{2}\/)+)(.*)$/) {
414             my @parts = split '/', $self->action_namespace($c);
415             my @levels = split '/', $rel;
416
417             $value = '/'.join('/', @parts[0 .. $#parts - @levels], $rest);
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
434 sub _parse_ChainedParent_attr {
435     my ($self, $c, $name, $value) = @_;
436     return $self->_parse_Chained_attr($c, $name, '../'.$name);
437 }
438
439 sub _parse_PathPrefix_attr {
440     my ( $self, $c ) = @_;
441     return PathPart => $self->path_prefix($c);
442 }
443
444 sub _parse_ActionClass_attr {
445     my ( $self, $c, $name, $value ) = @_;
446     my $appname = $self->_application;
447     $value = Catalyst::Utils::resolve_namespace($appname . '::Action', $self->_action_class, $value);
448     return ( 'ActionClass', $value );
449 }
450
451 sub _parse_MyAction_attr {
452     my ( $self, $c, $name, $value ) = @_;
453
454     my $appclass = Catalyst::Utils::class2appclass($self);
455     $value = "${appclass}::Action::${value}";
456
457     return ( 'ActionClass', $value );
458 }
459
460 __PACKAGE__->meta->make_immutable;
461
462 1;
463
464 __END__
465
466 =head1 CONFIGURATION
467
468 Like any other L<Catalyst::Component>, controllers have a config hash,
469 accessible through $self->config from the controller actions.  Some
470 settings are in use by the Catalyst framework:
471
472 =head2 namespace
473
474 This specifies the internal namespace the controller should be bound
475 to. By default the controller is bound to the URI version of the
476 controller name. For instance controller 'MyApp::Controller::Foo::Bar'
477 will be bound to 'foo/bar'. The default Root controller is an example
478 of setting namespace to '' (the null string).
479
480 =head2 path
481
482 Sets 'path_prefix', as described below.
483
484 =head2 action
485
486 Allows you to set the attributes that the dispatcher creates actions out of.
487 This allows you to do 'rails style routes', or override some of the
488 attribute definitions of actions composed from Roles.
489 You can set arguments globally (for all actions of the controller) and
490 specifically (for a single action).
491
492     __PACKAGE__->config(
493         action => {
494             '*' => { Chained => 'base', Args => 0  },
495             base => { Chained => '/', PathPart => '', CaptureArgs => 0 },
496         },
497      );
498
499 In the case above every sub in the package would be made into a Chain
500 endpoint with a URI the same as the sub name for each sub, chained
501 to the sub named C<base>. Ergo dispatch to C</example> would call the
502 C<base> method, then the C<example> method.
503
504 =head2 action_args
505
506 Allows you to set constructor arguments on your actions. You can set arguments
507 globally and specifically (as above).
508 This is particularly useful when using C<ActionRole>s
509 (L<Catalyst::Controller::ActionRole>) and custom C<ActionClass>es.
510
511     __PACKAGE__->config(
512         action_args => {
513             '*' => { globalarg1 => 'hello', globalarg2 => 'goodbye' },
514             'specific_action' => { customarg => 'arg1' },
515         },
516      );
517
518 In the case above the action class associated with C<specific_action> would get
519 passed the following arguments, in addition to the normal action constructor
520 arguments, when it is instantiated:
521
522   (globalarg1 => 'hello', globalarg2 => 'goodbye', customarg => 'arg1')
523
524 =head1 METHODS
525
526 =head2 BUILDARGS ($app, @args)
527
528 From L<Catalyst::Component::ApplicationAttribute>, stashes the application
529 instance as $self->_application.
530
531 =head2 $self->action_for('name')
532
533 Returns the Catalyst::Action object (if any) for a given method name
534 in this component.
535
536 =head2 $self->action_namespace($c)
537
538 Returns the private namespace for actions in this component. Defaults
539 to a value from the controller name (for
540 e.g. MyApp::Controller::Foo::Bar becomes "foo/bar") or can be
541 overridden from the "namespace" config key.
542
543
544 =head2 $self->path_prefix($c)
545
546 Returns the default path prefix for :PathPrefix, :Local, :LocalRegex and
547 relative :Path actions in this component. Defaults to the action_namespace or
548 can be overridden from the "path" config key.
549
550 =head2 $self->register_actions($c)
551
552 Finds all applicable actions for this component, creates
553 Catalyst::Action objects (using $self->create_action) for them and
554 registers them with $c->dispatcher.
555
556 =head2 $self->get_action_methods()
557
558 Returns a list of L<Moose::Meta::Method> objects, doing the
559 L<MooseX::MethodAttributes::Role::Meta::Method> role, which are the set of
560 action methods for this package.
561
562 =head2 $self->register_action_methods($c, @methods)
563
564 Creates action objects for a set of action methods using C< create_action >,
565 and registers them with the dispatcher.
566
567 =head2 $self->action_class(%args)
568
569 Used when a controller is creating an action to determine the correct base
570 action class to use.
571
572 =head2 $self->create_action(%args)
573
574 Called with a hash of data to be use for construction of a new
575 Catalyst::Action (or appropriate sub/alternative class) object.
576
577 =head2 $self->_application
578
579 =head2 $self->_app
580
581 Returns the application instance stored by C<new()>
582
583 =head1 AUTHORS
584
585 Catalyst Contributors, see Catalyst.pm
586
587 =head1 COPYRIGHT
588
589 This library is free software. You can redistribute it and/or modify
590 it under the same terms as Perl itself.
591
592 =cut