Stop using deprecated CMOP APIs
[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     if( ref($self) ){
191         return $self->$orig if $self->has_action_namespace;
192     } else {
193         return $class->config->{namespace} if exists $class->config->{namespace};
194     }
195
196     my $case_s;
197     if( $c ){
198         $case_s = $appclass->config->{case_sensitive};
199     } else {
200         if ($self->isa('Catalyst')) {
201             $case_s = $class->config->{case_sensitive};
202         } else {
203             if (ref $self) {
204                 $case_s = ref($self->_application)->config->{case_sensitive};
205             } else {
206                 confess("Can't figure out case_sensitive setting");
207             }
208         }
209     }
210
211     my $namespace = Catalyst::Utils::class2prefix($self->catalyst_component_name, $case_s) || '';
212     $self->$orig($namespace) if ref($self);
213     return $namespace;
214 };
215
216 #Once again, this is probably better written as a builder method
217 around path_prefix => sub {
218     my $orig = shift;
219     my $self = shift;
220     if( ref($self) ){
221       return $self->$orig if $self->has_path_prefix;
222     } else {
223       return $self->config->{path} if exists $self->config->{path};
224     }
225     my $namespace = $self->action_namespace(@_);
226     $self->$orig($namespace) if ref($self);
227     return $namespace;
228 };
229
230 sub get_action_methods {
231     my $self = shift;
232     my $meta = find_meta($self) || confess("No metaclass setup for $self");
233     confess(
234         sprintf "Metaclass %s for %s cannot support register_actions.",
235             ref $meta, $meta->name,
236     ) unless $meta->can('get_nearest_methods_with_attributes');
237     my @methods = $meta->get_nearest_methods_with_attributes;
238
239     # actions specified via config are also action_methods
240     push(
241         @methods,
242         map {
243             $meta->find_method_by_name($_)
244                 || confess( sprintf 'Action "%s" is not available from controller %s',
245                             $_, ref $self )
246         } keys %{ $self->_controller_actions }
247     ) if ( ref $self );
248     return uniq @methods;
249 }
250
251
252 sub register_actions {
253     my ( $self, $c ) = @_;
254     $self->register_action_methods( $c, $self->get_action_methods );
255 }
256
257 sub register_action_methods {
258     my ( $self, $c, @methods ) = @_;
259     my $class = $self->catalyst_component_name;
260     #this is still not correct for some reason.
261     my $namespace = $self->action_namespace($c);
262
263     # FIXME - fugly
264     if (!blessed($self) && $self eq $c && scalar(@methods)) {
265         my @really_bad_methods = grep { ! /^_(DISPATCH|BEGIN|AUTO|ACTION|END)$/ } map { $_->name } @methods;
266         if (scalar(@really_bad_methods)) {
267             $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.");
268         }
269     }
270
271     foreach my $method (@methods) {
272         my $name = $method->name;
273         # Horrible hack! All method metaclasses should have an attributes
274         # method, core Moose bug - see r13354.
275         my $attributes = $method->can('attributes') ? $method->attributes : [];
276         my $attrs = $self->_parse_attrs( $c, $name, @{ $attributes } );
277         if ( $attrs->{Private} && ( keys %$attrs > 1 ) ) {
278             $c->log->debug( 'Bad action definition "'
279                   . join( ' ', @{ $attributes } )
280                   . qq/" for "$class->$name"/ )
281               if $c->debug;
282             next;
283         }
284         my $reverse = $namespace ? "${namespace}/${name}" : $name;
285         my $action = $self->create_action(
286             name       => $name,
287             code       => $method->body,
288             reverse    => $reverse,
289             namespace  => $namespace,
290             class      => $class,
291             attributes => $attrs,
292         );
293
294         $c->dispatcher->register( $c, $action );
295     }
296 }
297
298 sub _apply_action_class_roles {
299     my ($self, $class, @roles) = @_;
300
301     load_class($_) for @roles;
302     my $meta = Moose::Meta::Class->initialize($class)->create_anon_class(
303         superclasses => [$class],
304         roles        => \@roles,
305         cache        => 1,
306     );
307     $meta->add_method(meta => sub { $meta });
308
309     return $meta->name;
310 }
311
312 sub action_class {
313     my $self = shift;
314     my %args = @_;
315
316     my $class = (exists $args{attributes}{ActionClass}
317         ? $args{attributes}{ActionClass}[0]
318         : $self->_action_class);
319
320     Class::MOP::load_class($class);
321     return $class;
322 }
323
324 sub create_action {
325     my $self = shift;
326     my %args = @_;
327
328     my $class = $self->action_class(%args);
329
330     load_class($class);
331     Moose->init_meta(for_class => $class)
332         unless Class::MOP::does_metaclass_exist($class);
333
334     unless ($args{name} =~ /^_(DISPATCH|BEGIN|AUTO|ACTION|END)$/) {
335        my @roles = $self->gather_action_roles(%args);
336        $class = $self->_apply_action_class_roles($class, @roles) if @roles;
337     }
338
339     my $action_args = (
340         ref($self)
341             ? $self->action_args
342             : $self->config->{action_args}
343     );
344
345     my %extra_args = (
346         %{ $action_args->{'*'}           || {} },
347         %{ $action_args->{ $args{name} } || {} },
348     );
349
350     return $class->new({ %extra_args, %args });
351 }
352
353 sub gather_action_roles {
354    my ($self, %args) = @_;
355
356    return (
357       (blessed $self ? $self->_action_roles : ()),
358       @{ $args{attributes}->{Does} || [] },
359    );
360 }
361
362 sub _parse_attrs {
363     my ( $self, $c, $name, @attrs ) = @_;
364
365     my %raw_attributes;
366
367     foreach my $attr (@attrs) {
368
369         # Parse out :Foo(bar) into Foo => bar etc (and arrayify)
370
371         if ( my ( $key, $value ) = ( $attr =~ /^(.*?)(?:\(\s*(.+?)\s*\))?$/ ) )
372         {
373
374             if ( defined $value ) {
375                 ( $value =~ s/^'(.*)'$/$1/ ) || ( $value =~ s/^"(.*)"/$1/ );
376             }
377             push( @{ $raw_attributes{$key} }, $value );
378         }
379     }
380
381     my ($actions_config, $all_actions_config);
382     if( ref($self) ) {
383         $actions_config = $self->_controller_actions;
384         # No, you're not getting actions => { '*' => ... } with actions in MyApp.
385         $all_actions_config = $self->_all_actions_attributes;
386     } else {
387         my $cfg = $self->config;
388         $actions_config = $self->merge_config_hashes($cfg->{actions}, $cfg->{action});
389         $all_actions_config = {};
390     }
391
392     %raw_attributes = (
393         %raw_attributes,
394         # Note we deep copy array refs here to stop crapping on config
395         # when attributes are parsed. RT#65463
396         exists $actions_config->{$name} ? map { ref($_) eq 'ARRAY' ? [ @$_ ] : $_ } %{ $actions_config->{$name } } : (),
397     );
398
399     # Private actions with additional attributes will raise a warning and then
400     # be ignored. Adding '*' arguments to the default _DISPATCH / etc. methods,
401     # which are Private, will prevent those from being registered. They should
402     # probably be turned into :Actions instead, or we might want to otherwise
403     # disambiguate between those built-in internal actions and user-level
404     # Private ones.
405     %raw_attributes = (%{ $all_actions_config }, %raw_attributes)
406         unless $raw_attributes{Private};
407
408     my %final_attributes;
409
410     foreach my $key (keys %raw_attributes) {
411
412         my $raw = $raw_attributes{$key};
413
414         foreach my $value (ref($raw) eq 'ARRAY' ? @$raw : $raw) {
415
416             my $meth = "_parse_${key}_attr";
417             if ( my $code = $self->can($meth) ) {
418                 ( $key, $value ) = $self->$code( $c, $name, $value );
419             }
420             push( @{ $final_attributes{$key} }, $value );
421         }
422     }
423
424     return \%final_attributes;
425 }
426
427 sub _parse_Global_attr {
428     my ( $self, $c, $name, $value ) = @_;
429     return $self->_parse_Path_attr( $c, $name, "/$name" );
430 }
431
432 sub _parse_Absolute_attr { shift->_parse_Global_attr(@_); }
433
434 sub _parse_Local_attr {
435     my ( $self, $c, $name, $value ) = @_;
436     return $self->_parse_Path_attr( $c, $name, $name );
437 }
438
439 sub _parse_Relative_attr { shift->_parse_Local_attr(@_); }
440
441 sub _parse_Path_attr {
442     my ( $self, $c, $name, $value ) = @_;
443     $value = '' if !defined $value;
444     if ( $value =~ m!^/! ) {
445         return ( 'Path', $value );
446     }
447     elsif ( length $value ) {
448         return ( 'Path', join( '/', $self->path_prefix($c), $value ) );
449     }
450     else {
451         return ( 'Path', $self->path_prefix($c) );
452     }
453 }
454
455 sub _parse_Regex_attr {
456     my ( $self, $c, $name, $value ) = @_;
457     return ( 'Regex', $value );
458 }
459
460 sub _parse_Regexp_attr { shift->_parse_Regex_attr(@_); }
461
462 sub _parse_LocalRegex_attr {
463     my ( $self, $c, $name, $value ) = @_;
464     unless ( $value =~ s/^\^// ) { $value = "(?:.*?)$value"; }
465
466     my $prefix = $self->path_prefix( $c );
467     $prefix .= '/' if length( $prefix );
468
469     return ( 'Regex', "^${prefix}${value}" );
470 }
471
472 sub _parse_LocalRegexp_attr { shift->_parse_LocalRegex_attr(@_); }
473
474 sub _parse_Chained_attr {
475     my ($self, $c, $name, $value) = @_;
476
477     if (defined($value) && length($value)) {
478         if ($value eq '.') {
479             $value = '/'.$self->action_namespace($c);
480         } elsif (my ($rel, $rest) = $value =~ /^((?:\.{2}\/)+)(.*)$/) {
481             my @parts = split '/', $self->action_namespace($c);
482             my @levels = split '/', $rel;
483
484             $value = '/'.join('/', @parts[0 .. $#parts - @levels], $rest);
485         } elsif ($value !~ m/^\//) {
486             my $action_ns = $self->action_namespace($c);
487
488             if ($action_ns) {
489                 $value = '/'.join('/', $action_ns, $value);
490             } else {
491                 $value = '/'.$value; # special case namespace '' (root)
492             }
493         }
494     } else {
495         $value = '/'
496     }
497
498     return Chained => $value;
499 }
500
501 sub _parse_ChainedParent_attr {
502     my ($self, $c, $name, $value) = @_;
503     return $self->_parse_Chained_attr($c, $name, '../'.$name);
504 }
505
506 sub _parse_PathPrefix_attr {
507     my ( $self, $c ) = @_;
508     return PathPart => $self->path_prefix($c);
509 }
510
511 sub _parse_ActionClass_attr {
512     my ( $self, $c, $name, $value ) = @_;
513     my $appname = $self->_application;
514     $value = Catalyst::Utils::resolve_namespace($appname . '::Action', $self->_action_class, $value);
515     return ( 'ActionClass', $value );
516 }
517
518 sub _parse_MyAction_attr {
519     my ( $self, $c, $name, $value ) = @_;
520
521     my $appclass = Catalyst::Utils::class2appclass($self);
522     $value = "${appclass}::Action::${value}";
523
524     return ( 'ActionClass', $value );
525 }
526
527 sub _parse_Does_attr {
528     my ($self, $app, $name, $value) = @_;
529     return Does => $self->_expand_role_shortname($value);
530 }
531
532 sub _expand_role_shortname {
533     my ($self, @shortnames) = @_;
534     my $app = $self->_application;
535
536     my $prefix = $self->can('_action_role_prefix') ? $self->_action_role_prefix : ['Catalyst::ActionRole::'];
537     my @prefixes = (qq{${app}::ActionRole::}, @$prefix);
538
539     return String::RewritePrefix->rewrite(
540         { ''  => sub {
541             my $loaded = load_first_existing_class(
542                 map { "$_$_[0]" } @prefixes
543             );
544             return first { $loaded =~ /^$_/ }
545               sort { length $b <=> length $a } @prefixes;
546           },
547           '~' => $prefixes[0],
548           '+' => '' },
549         @shortnames,
550     );
551 }
552
553 __PACKAGE__->meta->make_immutable;
554
555 1;
556
557 __END__
558
559 =head1 CONFIGURATION
560
561 Like any other L<Catalyst::Component>, controllers have a config hash,
562 accessible through $self->config from the controller actions.  Some
563 settings are in use by the Catalyst framework:
564
565 =head2 namespace
566
567 This specifies the internal namespace the controller should be bound
568 to. By default the controller is bound to the URI version of the
569 controller name. For instance controller 'MyApp::Controller::Foo::Bar'
570 will be bound to 'foo/bar'. The default Root controller is an example
571 of setting namespace to '' (the null string).
572
573 =head2 path
574
575 Sets 'path_prefix', as described below.
576
577 =head2 action
578
579 Allows you to set the attributes that the dispatcher creates actions out of.
580 This allows you to do 'rails style routes', or override some of the
581 attribute definitions of actions composed from Roles.
582 You can set arguments globally (for all actions of the controller) and
583 specifically (for a single action).
584
585     __PACKAGE__->config(
586         action => {
587             '*' => { Chained => 'base', Args => 0  },
588             base => { Chained => '/', PathPart => '', CaptureArgs => 0 },
589         },
590      );
591
592 In the case above every sub in the package would be made into a Chain
593 endpoint with a URI the same as the sub name for each sub, chained
594 to the sub named C<base>. Ergo dispatch to C</example> would call the
595 C<base> method, then the C<example> method.
596
597 =head2 action_args
598
599 Allows you to set constructor arguments on your actions. You can set arguments
600 globally and specifically (as above).
601 This is particularly useful when using C<ActionRole>s
602 (L<Catalyst::Controller::ActionRole>) and custom C<ActionClass>es.
603
604     __PACKAGE__->config(
605         action_args => {
606             '*' => { globalarg1 => 'hello', globalarg2 => 'goodbye' },
607             'specific_action' => { customarg => 'arg1' },
608         },
609      );
610
611 In the case above the action class associated with C<specific_action> would get
612 passed the following arguments, in addition to the normal action constructor
613 arguments, when it is instantiated:
614
615   (globalarg1 => 'hello', globalarg2 => 'goodbye', customarg => 'arg1')
616
617 =head1 METHODS
618
619 =head2 BUILDARGS ($app, @args)
620
621 From L<Catalyst::Component::ApplicationAttribute>, stashes the application
622 instance as $self->_application.
623
624 =head2 $self->action_for('name')
625
626 Returns the Catalyst::Action object (if any) for a given method name
627 in this component.
628
629 =head2 $self->action_namespace($c)
630
631 Returns the private namespace for actions in this component. Defaults
632 to a value from the controller name (for
633 e.g. MyApp::Controller::Foo::Bar becomes "foo/bar") or can be
634 overridden from the "namespace" config key.
635
636
637 =head2 $self->path_prefix($c)
638
639 Returns the default path prefix for :PathPrefix, :Local, :LocalRegex and
640 relative :Path actions in this component. Defaults to the action_namespace or
641 can be overridden from the "path" config key.
642
643 =head2 $self->register_actions($c)
644
645 Finds all applicable actions for this component, creates
646 Catalyst::Action objects (using $self->create_action) for them and
647 registers them with $c->dispatcher.
648
649 =head2 $self->get_action_methods()
650
651 Returns a list of L<Moose::Meta::Method> objects, doing the
652 L<MooseX::MethodAttributes::Role::Meta::Method> role, which are the set of
653 action methods for this package.
654
655 =head2 $self->register_action_methods($c, @methods)
656
657 Creates action objects for a set of action methods using C< create_action >,
658 and registers them with the dispatcher.
659
660 =head2 $self->action_class(%args)
661
662 Used when a controller is creating an action to determine the correct base
663 action class to use.
664
665 =head2 $self->create_action(%args)
666
667 Called with a hash of data to be use for construction of a new
668 Catalyst::Action (or appropriate sub/alternative class) object.
669
670 =head2 $self->gather_action_roles(\%action_args)
671
672 Gathers the list of roles to apply to an action with the given %action_args.
673
674 =head2 $self->_application
675
676 =head2 $self->_app
677
678 Returns the application instance stored by C<new()>
679
680 =head1 AUTHORS
681
682 Catalyst Contributors, see Catalyst.pm
683
684 =head1 COPYRIGHT
685
686 This library is free software. You can redistribute it and/or modify
687 it under the same terms as Perl itself.
688
689 =cut