Merge master into gsoc_breadboard
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Controller.pm
1 package Catalyst::Controller;
2
3 use Moose;
4 use Class::MOP;
5 use Class::Load ':all';
6 use String::RewritePrefix;
7 use Moose::Util qw/find_meta/;
8 use List::Util qw/first/;
9 use List::MoreUtils qw/uniq/;
10 use namespace::clean -except => 'meta';
11
12 BEGIN { extends qw/Catalyst::Component MooseX::MethodAttributes::Inheritable/; }
13
14 use MooseX::MethodAttributes;
15 use Catalyst::Exception;
16 use Catalyst::Utils;
17
18 with 'Catalyst::Component::ApplicationAttribute';
19
20 has path_prefix => (
21     is        => 'rw',
22     isa       => 'Str',
23     init_arg  => 'path',
24     predicate => 'has_path_prefix',
25 );
26
27 has action_namespace => (
28     is        => 'rw',
29     isa       => 'Str',
30     init_arg  => 'namespace',
31     predicate => 'has_action_namespace',
32 );
33
34 has actions => (
35     accessor => '_controller_actions',
36     isa      => 'HashRef',
37     init_arg => undef,
38 );
39
40 has _action_role_args => (
41     traits     => [qw(Array)],
42     isa        => 'ArrayRef[Str]',
43     init_arg   => 'action_roles',
44     default    => sub { [] },
45     handles    => {
46         _action_role_args => 'elements',
47     },
48 );
49
50 has _action_roles => (
51     traits     => [qw(Array)],
52     isa        => 'ArrayRef[RoleName]',
53     init_arg   => undef,
54     lazy       => 1,
55     builder    => '_build__action_roles',
56     handles    => {
57         _action_roles => 'elements',
58     },
59 );
60
61 has action_args => (is => 'ro');
62
63 # ->config(actions => { '*' => ...
64 has _all_actions_attributes => (
65     is       => 'ro',
66     isa      => 'HashRef',
67     init_arg => undef,
68     lazy     => 1,
69     builder  => '_build__all_actions_attributes',
70 );
71
72 sub BUILD {
73     my ($self, $args) = @_;
74     my $action  = delete $args->{action}  || {};
75     my $actions = delete $args->{actions} || {};
76     my $attr_value = $self->merge_config_hashes($actions, $action);
77     $self->_controller_actions($attr_value);
78
79     # trigger lazy builder
80     $self->_all_actions_attributes;
81     $self->_action_roles;
82 }
83
84 sub _build__action_roles {
85     my $self = shift;
86     my @roles = $self->_expand_role_shortname($self->_action_role_args);
87     load_class($_) for @roles;
88     return \@roles;
89 }
90
91 sub _build__all_actions_attributes {
92     my ($self) = @_;
93     delete $self->_controller_actions->{'*'} || {};
94 }
95
96 =head1 NAME
97
98 Catalyst::Controller - Catalyst Controller base class
99
100 =head1 SYNOPSIS
101
102   package MyApp::Controller::Search
103   use base qw/Catalyst::Controller/;
104
105   sub foo : Local {
106     my ($self,$c,@args) = @_;
107     ...
108   } # Dispatches to /search/foo
109
110 =head1 DESCRIPTION
111
112 Controllers are where the actions in the Catalyst framework
113 reside. Each action is represented by a function with an attribute to
114 identify what kind of action it is. See the L<Catalyst::Dispatcher>
115 for more info about how Catalyst dispatches to actions.
116
117 =cut
118
119 #I think both of these could be attributes. doesn't really seem like they need
120 #to ble class data. i think that attributes +default would work just fine
121 __PACKAGE__->mk_classdata($_) for qw/_dispatch_steps _action_class _action_role_prefix/;
122
123 __PACKAGE__->_dispatch_steps( [qw/_BEGIN _AUTO _ACTION/] );
124 __PACKAGE__->_action_class('Catalyst::Action');
125 __PACKAGE__->_action_role_prefix([ 'Catalyst::ActionRole::' ]);
126
127
128 sub _DISPATCH : Private {
129     my ( $self, $c ) = @_;
130
131     foreach my $disp ( @{ $self->_dispatch_steps } ) {
132         last unless $c->forward($disp);
133     }
134
135     $c->forward('_END');
136 }
137
138 sub _BEGIN : Private {
139     my ( $self, $c ) = @_;
140     my $begin = ( $c->get_actions( 'begin', $c->namespace ) )[-1];
141     return 1 unless $begin;
142     $begin->dispatch( $c );
143     return !@{ $c->error };
144 }
145
146 sub _AUTO : Private {
147     my ( $self, $c ) = @_;
148     my @auto = $c->get_actions( 'auto', $c->namespace );
149     foreach my $auto (@auto) {
150         $auto->dispatch( $c );
151         return 0 unless $c->state;
152     }
153     return 1;
154 }
155
156 sub _ACTION : Private {
157     my ( $self, $c ) = @_;
158     if (   ref $c->action
159         && $c->action->can('execute')
160         && defined $c->req->action )
161     {
162         $c->action->dispatch( $c );
163     }
164     return !@{ $c->error };
165 }
166
167 sub _END : Private {
168     my ( $self, $c ) = @_;
169     my $end = ( $c->get_actions( 'end', $c->namespace ) )[-1];
170     return 1 unless $end;
171     $end->dispatch( $c );
172     return !@{ $c->error };
173 }
174
175 sub action_for {
176     my ( $self, $name ) = @_;
177     my $app = ($self->isa('Catalyst') ? $self : $self->_application);
178     return $app->dispatcher->get_action($name, $self->action_namespace);
179 }
180
181 #my opinion is that this whole sub really should be a builder method, not
182 #something that happens on every call. Anyone else disagree?? -- groditi
183 ## -- apparently this is all just waiting for app/ctx split
184 around action_namespace => sub {
185     my $orig = shift;
186     my ( $self, $c ) = @_;
187
188     my $class = ref($self) || $self;
189     my $appclass = ref($c) || $c;
190
191     # FIXME - catalyst_component_name is no longer a class accessor, because
192     # 'MyApp as a controller' behavior is removed. But is this call to
193     # catalyst_component_name necessary, or is it always the same as $class?
194     my $component_name = ref($self) ? $self->catalyst_component_name : $self;
195
196     if( ref($self) ){
197         return $self->$orig if $self->has_action_namespace;
198     } else {
199         return $class->config->{namespace} if exists $class->config->{namespace};
200     }
201
202     my $case_s;
203     if( $c ){
204         $case_s = $appclass->config->{case_sensitive};
205     } else {
206         if ($self->isa('Catalyst')) {
207             $case_s = $class->config->{case_sensitive};
208         } else {
209             if (ref $self) {
210                 $case_s = ref($self->_application)->config->{case_sensitive};
211             } else {
212                 confess("Can't figure out case_sensitive setting");
213             }
214         }
215     }
216
217     my $namespace = Catalyst::Utils::class2prefix($component_name, $case_s) || '';
218     $self->$orig($namespace) if ref($self);
219     return $namespace;
220 };
221
222 #Once again, this is probably better written as a builder method
223 around path_prefix => sub {
224     my $orig = shift;
225     my $self = shift;
226     if( ref($self) ){
227       return $self->$orig if $self->has_path_prefix;
228     } else {
229       return $self->config->{path} if exists $self->config->{path};
230     }
231     my $namespace = $self->action_namespace(@_);
232     $self->$orig($namespace) if ref($self);
233     return $namespace;
234 };
235
236 sub get_action_methods {
237     my $self = shift;
238     my $meta = find_meta($self) || confess("No metaclass setup for $self");
239     confess(
240         sprintf "Metaclass %s for %s cannot support register_actions.",
241             ref $meta, $meta->name,
242     ) unless $meta->can('get_nearest_methods_with_attributes');
243     my @methods = $meta->get_nearest_methods_with_attributes;
244
245     # actions specified via config are also action_methods
246     push(
247         @methods,
248         map {
249             $meta->find_method_by_name($_)
250                 || confess( sprintf 'Action "%s" is not available from controller %s',
251                             $_, ref $self )
252         } keys %{ $self->_controller_actions }
253     ) if ( ref $self );
254     return uniq @methods;
255 }
256
257
258 sub register_actions {
259     my ( $self, $c ) = @_;
260     $self->register_action_methods( $c, $self->get_action_methods );
261 }
262
263 sub register_action_methods {
264     my ( $self, $c, @methods ) = @_;
265     my $class = $self->catalyst_component_name;
266     #this is still not correct for some reason.
267     my $namespace = $self->action_namespace($c);
268
269     # FIXME - fugly
270     if (!blessed($self) && $self eq $c && scalar(@methods)) {
271         my @really_bad_methods = grep { ! /^_(DISPATCH|BEGIN|AUTO|ACTION|END)$/ } map { $_->name } @methods;
272         if (scalar(@really_bad_methods)) {
273             $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.");
274         }
275     }
276
277     foreach my $method (@methods) {
278         my $name = $method->name;
279         # Horrible hack! All method metaclasses should have an attributes
280         # method, core Moose bug - see r13354.
281         my $attributes = $method->can('attributes') ? $method->attributes : [];
282         my $attrs = $self->_parse_attrs( $c, $name, @{ $attributes } );
283         if ( $attrs->{Private} && ( keys %$attrs > 1 ) ) {
284             $c->log->debug( 'Bad action definition "'
285                   . join( ' ', @{ $attributes } )
286                   . qq/" for "$class->$name"/ )
287               if $c->debug;
288             next;
289         }
290         my $reverse = $namespace ? "${namespace}/${name}" : $name;
291         my $action = $self->create_action(
292             name       => $name,
293             code       => $method->body,
294             reverse    => $reverse,
295             namespace  => $namespace,
296             class      => $class,
297             attributes => $attrs,
298         );
299
300         $c->dispatcher->register( $c, $action );
301     }
302 }
303
304 sub _apply_action_class_roles {
305     my ($self, $class, @roles) = @_;
306
307     load_class($_) for @roles;
308     my $meta = Moose::Meta::Class->initialize($class)->create_anon_class(
309         superclasses => [$class],
310         roles        => \@roles,
311         cache        => 1,
312     );
313     $meta->add_method(meta => sub { $meta });
314
315     return $meta->name;
316 }
317
318 sub action_class {
319     my $self = shift;
320     my %args = @_;
321
322     my $class = (exists $args{attributes}{ActionClass}
323         ? $args{attributes}{ActionClass}[0]
324         : $self->_action_class);
325
326     Class::MOP::load_class($class);
327     return $class;
328 }
329
330 sub create_action {
331     my $self = shift;
332     my %args = @_;
333
334     my $class = $self->action_class(%args);
335
336     load_class($class);
337     Moose->init_meta(for_class => $class)
338         unless Class::MOP::does_metaclass_exist($class);
339
340     unless ($args{name} =~ /^_(DISPATCH|BEGIN|AUTO|ACTION|END)$/) {
341        my @roles = $self->gather_action_roles(%args);
342        $class = $self->_apply_action_class_roles($class, @roles) if @roles;
343     }
344
345     my $action_args = (
346         ref($self)
347             ? $self->action_args
348             : $self->config->{action_args}
349     );
350
351     my %extra_args = (
352         %{ $action_args->{'*'}           || {} },
353         %{ $action_args->{ $args{name} } || {} },
354     );
355
356     return $class->new({ %extra_args, %args });
357 }
358
359 sub gather_action_roles {
360    my ($self, %args) = @_;
361
362    return (
363       (blessed $self ? $self->_action_roles : ()),
364       @{ $args{attributes}->{Does} || [] },
365    );
366 }
367
368 sub _parse_attrs {
369     my ( $self, $c, $name, @attrs ) = @_;
370
371     my %raw_attributes;
372
373     foreach my $attr (@attrs) {
374
375         # Parse out :Foo(bar) into Foo => bar etc (and arrayify)
376
377         if ( my ( $key, $value ) = ( $attr =~ /^(.*?)(?:\(\s*(.+?)\s*\))?$/ ) )
378         {
379
380             if ( defined $value ) {
381                 ( $value =~ s/^'(.*)'$/$1/ ) || ( $value =~ s/^"(.*)"/$1/ );
382             }
383             push( @{ $raw_attributes{$key} }, $value );
384         }
385     }
386
387     my ($actions_config, $all_actions_config);
388     if( ref($self) ) {
389         $actions_config = $self->_controller_actions;
390         # No, you're not getting actions => { '*' => ... } with actions in MyApp.
391         $all_actions_config = $self->_all_actions_attributes;
392     } else {
393         my $cfg = $self->config;
394         $actions_config = $self->merge_config_hashes($cfg->{actions}, $cfg->{action});
395         $all_actions_config = {};
396     }
397
398     %raw_attributes = (
399         %raw_attributes,
400         # Note we deep copy array refs here to stop crapping on config
401         # when attributes are parsed. RT#65463
402         exists $actions_config->{$name} ? map { ref($_) eq 'ARRAY' ? [ @$_ ] : $_ } %{ $actions_config->{$name } } : (),
403     );
404
405     # Private actions with additional attributes will raise a warning and then
406     # be ignored. Adding '*' arguments to the default _DISPATCH / etc. methods,
407     # which are Private, will prevent those from being registered. They should
408     # probably be turned into :Actions instead, or we might want to otherwise
409     # disambiguate between those built-in internal actions and user-level
410     # Private ones.
411     %raw_attributes = (%{ $all_actions_config }, %raw_attributes)
412         unless $raw_attributes{Private};
413
414     my %final_attributes;
415
416     foreach my $key (keys %raw_attributes) {
417
418         my $raw = $raw_attributes{$key};
419
420         foreach my $value (ref($raw) eq 'ARRAY' ? @$raw : $raw) {
421
422             my $meth = "_parse_${key}_attr";
423             if ( my $code = $self->can($meth) ) {
424                 ( $key, $value ) = $self->$code( $c, $name, $value );
425             }
426             push( @{ $final_attributes{$key} }, $value );
427         }
428     }
429
430     return \%final_attributes;
431 }
432
433 sub _parse_Global_attr {
434     my ( $self, $c, $name, $value ) = @_;
435     return $self->_parse_Path_attr( $c, $name, "/$name" );
436 }
437
438 sub _parse_Absolute_attr { shift->_parse_Global_attr(@_); }
439
440 sub _parse_Local_attr {
441     my ( $self, $c, $name, $value ) = @_;
442     return $self->_parse_Path_attr( $c, $name, $name );
443 }
444
445 sub _parse_Relative_attr { shift->_parse_Local_attr(@_); }
446
447 sub _parse_Path_attr {
448     my ( $self, $c, $name, $value ) = @_;
449     $value = '' if !defined $value;
450     if ( $value =~ m!^/! ) {
451         return ( 'Path', $value );
452     }
453     elsif ( length $value ) {
454         return ( 'Path', join( '/', $self->path_prefix($c), $value ) );
455     }
456     else {
457         return ( 'Path', $self->path_prefix($c) );
458     }
459 }
460
461 sub _parse_Regex_attr {
462     my ( $self, $c, $name, $value ) = @_;
463     return ( 'Regex', $value );
464 }
465
466 sub _parse_Regexp_attr { shift->_parse_Regex_attr(@_); }
467
468 sub _parse_LocalRegex_attr {
469     my ( $self, $c, $name, $value ) = @_;
470     unless ( $value =~ s/^\^// ) { $value = "(?:.*?)$value"; }
471
472     my $prefix = $self->path_prefix( $c );
473     $prefix .= '/' if length( $prefix );
474
475     return ( 'Regex', "^${prefix}${value}" );
476 }
477
478 sub _parse_LocalRegexp_attr { shift->_parse_LocalRegex_attr(@_); }
479
480 sub _parse_Chained_attr {
481     my ($self, $c, $name, $value) = @_;
482
483     if (defined($value) && length($value)) {
484         if ($value eq '.') {
485             $value = '/'.$self->action_namespace($c);
486         } elsif (my ($rel, $rest) = $value =~ /^((?:\.{2}\/)+)(.*)$/) {
487             my @parts = split '/', $self->action_namespace($c);
488             my @levels = split '/', $rel;
489
490             $value = '/'.join('/', @parts[0 .. $#parts - @levels], $rest);
491         } elsif ($value !~ m/^\//) {
492             my $action_ns = $self->action_namespace($c);
493
494             if ($action_ns) {
495                 $value = '/'.join('/', $action_ns, $value);
496             } else {
497                 $value = '/'.$value; # special case namespace '' (root)
498             }
499         }
500     } else {
501         $value = '/'
502     }
503
504     return Chained => $value;
505 }
506
507 sub _parse_ChainedParent_attr {
508     my ($self, $c, $name, $value) = @_;
509     return $self->_parse_Chained_attr($c, $name, '../'.$name);
510 }
511
512 sub _parse_PathPrefix_attr {
513     my ( $self, $c ) = @_;
514     return PathPart => $self->path_prefix($c);
515 }
516
517 sub _parse_ActionClass_attr {
518     my ( $self, $c, $name, $value ) = @_;
519     my $appname = $self->_application;
520     $value = Catalyst::Utils::resolve_namespace($appname . '::Action', $self->_action_class, $value);
521     return ( 'ActionClass', $value );
522 }
523
524 sub _parse_MyAction_attr {
525     my ( $self, $c, $name, $value ) = @_;
526
527     my $appclass = Catalyst::Utils::class2appclass($self);
528     $value = "${appclass}::Action::${value}";
529
530     return ( 'ActionClass', $value );
531 }
532
533 sub _parse_Does_attr {
534     my ($self, $app, $name, $value) = @_;
535     return Does => $self->_expand_role_shortname($value);
536 }
537
538 sub _expand_role_shortname {
539     my ($self, @shortnames) = @_;
540     my $app = $self->_application;
541
542     my $prefix = $self->can('_action_role_prefix') ? $self->_action_role_prefix : ['Catalyst::ActionRole::'];
543     my @prefixes = (qq{${app}::ActionRole::}, @$prefix);
544
545     return String::RewritePrefix->rewrite(
546         { ''  => sub {
547             my $loaded = load_first_existing_class(
548                 map { "$_$_[0]" } @prefixes
549             );
550             return first { $loaded =~ /^$_/ }
551               sort { length $b <=> length $a } @prefixes;
552           },
553           '~' => $prefixes[0],
554           '+' => '' },
555         @shortnames,
556     );
557 }
558
559 __PACKAGE__->meta->make_immutable;
560
561 1;
562
563 __END__
564
565 =head1 CONFIGURATION
566
567 Like any other L<Catalyst::Component>, controllers have a config hash,
568 accessible through $self->config from the controller actions.  Some
569 settings are in use by the Catalyst framework:
570
571 =head2 namespace
572
573 This specifies the internal namespace the controller should be bound
574 to. By default the controller is bound to the URI version of the
575 controller name. For instance controller 'MyApp::Controller::Foo::Bar'
576 will be bound to 'foo/bar'. The default Root controller is an example
577 of setting namespace to '' (the null string).
578
579 =head2 path
580
581 Sets 'path_prefix', as described below.
582
583 =head2 action
584
585 Allows you to set the attributes that the dispatcher creates actions out of.
586 This allows you to do 'rails style routes', or override some of the
587 attribute definitions of actions composed from Roles.
588 You can set arguments globally (for all actions of the controller) and
589 specifically (for a single action).
590
591     __PACKAGE__->config(
592         action => {
593             '*' => { Chained => 'base', Args => 0  },
594             base => { Chained => '/', PathPart => '', CaptureArgs => 0 },
595         },
596      );
597
598 In the case above every sub in the package would be made into a Chain
599 endpoint with a URI the same as the sub name for each sub, chained
600 to the sub named C<base>. Ergo dispatch to C</example> would call the
601 C<base> method, then the C<example> method.
602
603 =head2 action_args
604
605 Allows you to set constructor arguments on your actions. You can set arguments
606 globally and specifically (as above).
607 This is particularly useful when using C<ActionRole>s
608 (L<Catalyst::Controller::ActionRole>) and custom C<ActionClass>es.
609
610     __PACKAGE__->config(
611         action_args => {
612             '*' => { globalarg1 => 'hello', globalarg2 => 'goodbye' },
613             'specific_action' => { customarg => 'arg1' },
614         },
615      );
616
617 In the case above the action class associated with C<specific_action> would get
618 passed the following arguments, in addition to the normal action constructor
619 arguments, when it is instantiated:
620
621   (globalarg1 => 'hello', globalarg2 => 'goodbye', customarg => 'arg1')
622
623 =head1 METHODS
624
625 =head2 BUILDARGS ($app, @args)
626
627 From L<Catalyst::Component::ApplicationAttribute>, stashes the application
628 instance as $self->_application.
629
630 =head2 $self->action_for('name')
631
632 Returns the Catalyst::Action object (if any) for a given method name
633 in this component.
634
635 =head2 $self->action_namespace($c)
636
637 Returns the private namespace for actions in this component. Defaults
638 to a value from the controller name (for
639 e.g. MyApp::Controller::Foo::Bar becomes "foo/bar") or can be
640 overridden from the "namespace" config key.
641
642
643 =head2 $self->path_prefix($c)
644
645 Returns the default path prefix for :PathPrefix, :Local, :LocalRegex and
646 relative :Path actions in this component. Defaults to the action_namespace or
647 can be overridden from the "path" config key.
648
649 =head2 $self->register_actions($c)
650
651 Finds all applicable actions for this component, creates
652 Catalyst::Action objects (using $self->create_action) for them and
653 registers them with $c->dispatcher.
654
655 =head2 $self->get_action_methods()
656
657 Returns a list of L<Moose::Meta::Method> objects, doing the
658 L<MooseX::MethodAttributes::Role::Meta::Method> role, which are the set of
659 action methods for this package.
660
661 =head2 $self->register_action_methods($c, @methods)
662
663 Creates action objects for a set of action methods using C< create_action >,
664 and registers them with the dispatcher.
665
666 =head2 $self->action_class(%args)
667
668 Used when a controller is creating an action to determine the correct base
669 action class to use.
670
671 =head2 $self->create_action(%args)
672
673 Called with a hash of data to be use for construction of a new
674 Catalyst::Action (or appropriate sub/alternative class) object.
675
676 =head2 $self->gather_action_roles(\%action_args)
677
678 Gathers the list of roles to apply to an action with the given %action_args.
679
680 =head2 $self->_application
681
682 =head2 $self->_app
683
684 Returns the application instance stored by C<new()>
685
686 =head1 AUTHORS
687
688 Catalyst Contributors, see Catalyst.pm
689
690 =head1 COPYRIGHT
691
692 This library is free software. You can redistribute it and/or modify
693 it under the same terms as Perl itself.
694
695 =cut