1 package Catalyst::Controller;
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';
12 BEGIN { extends qw/Catalyst::Component MooseX::MethodAttributes::Inheritable/; }
14 use MooseX::MethodAttributes;
15 use Catalyst::Exception;
18 with 'Catalyst::Component::ApplicationAttribute';
24 predicate => 'has_path_prefix',
27 has action_namespace => (
30 init_arg => 'namespace',
31 predicate => 'has_action_namespace',
35 accessor => '_controller_actions',
40 has _action_role_args => (
41 traits => [qw(Array)],
42 isa => 'ArrayRef[Str]',
43 init_arg => 'action_roles',
44 default => sub { [] },
46 _action_role_args => 'elements',
50 has _action_roles => (
51 traits => [qw(Array)],
52 isa => 'ArrayRef[RoleName]',
55 builder => '_build__action_roles',
57 _action_roles => 'elements',
61 has action_args => (is => 'ro');
63 # ->config(actions => { '*' => ...
64 has _all_actions_attributes => (
69 builder => '_build__all_actions_attributes',
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);
79 # trigger lazy builder
80 $self->_all_actions_attributes;
84 sub _build__action_roles {
86 my @roles = $self->_expand_role_shortname($self->_action_role_args);
87 load_class($_) for @roles;
91 sub _build__all_actions_attributes {
93 delete $self->_controller_actions->{'*'} || {};
98 Catalyst::Controller - Catalyst Controller base class
102 package MyApp::Controller::Search
103 use base qw/Catalyst::Controller/;
106 my ($self,$c,@args) = @_;
108 } # Dispatches to /search/foo
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.
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/;
123 __PACKAGE__->_dispatch_steps( [qw/_BEGIN _AUTO _ACTION/] );
124 __PACKAGE__->_action_class('Catalyst::Action');
125 __PACKAGE__->_action_role_prefix([ 'Catalyst::ActionRole::' ]);
128 sub _DISPATCH : Private {
129 my ( $self, $c ) = @_;
131 foreach my $disp ( @{ $self->_dispatch_steps } ) {
132 last unless $c->forward($disp);
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 };
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;
156 sub _ACTION : Private {
157 my ( $self, $c ) = @_;
159 && $c->action->can('execute')
160 && defined $c->req->action )
162 $c->action->dispatch( $c );
164 return !@{ $c->error };
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 };
176 my ( $self, $name ) = @_;
177 my $app = ($self->isa('Catalyst') ? $self : $self->_application);
178 return $app->dispatcher->get_action($name, $self->action_namespace);
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 {
186 my ( $self, $c ) = @_;
188 my $class = ref($self) || $self;
189 my $appclass = ref($c) || $c;
191 return $self->$orig if $self->has_action_namespace;
193 return $class->config->{namespace} if exists $class->config->{namespace};
198 $case_s = $appclass->config->{case_sensitive};
200 if ($self->isa('Catalyst')) {
201 $case_s = $class->config->{case_sensitive};
204 $case_s = ref($self->_application)->config->{case_sensitive};
206 confess("Can't figure out case_sensitive setting");
211 my $namespace = Catalyst::Utils::class2prefix($self->catalyst_component_name, $case_s) || '';
212 $self->$orig($namespace) if ref($self);
216 #Once again, this is probably better written as a builder method
217 around path_prefix => sub {
221 return $self->$orig if $self->has_path_prefix;
223 return $self->config->{path} if exists $self->config->{path};
225 my $namespace = $self->action_namespace(@_);
226 $self->$orig($namespace) if ref($self);
230 sub get_action_methods {
232 my $meta = find_meta($self) || confess("No metaclass setup for $self");
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;
239 # actions specified via config are also action_methods
243 $meta->find_method_by_name($_)
244 || confess( sprintf 'Action "%s" is not available from controller %s',
246 } keys %{ $self->_controller_actions }
248 return uniq @methods;
252 sub register_actions {
253 my ( $self, $c ) = @_;
254 $self->register_action_methods( $c, $self->get_action_methods );
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);
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.");
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->warn( 'Bad action definition "'
279 . join( ' ', @{ $attributes } )
280 . qq/" for "$class->$name"/ )
284 my $reverse = $namespace ? "${namespace}/${name}" : $name;
285 my $action = $self->create_action(
287 code => $method->body,
289 namespace => $namespace,
291 attributes => $attrs,
294 $c->dispatcher->register( $c, $action );
298 sub _apply_action_class_roles {
299 my ($self, $class, @roles) = @_;
301 load_class($_) for @roles;
302 my $meta = Moose::Meta::Class->initialize($class)->create_anon_class(
303 superclasses => [$class],
307 $meta->add_method(meta => sub { $meta });
316 my $class = (exists $args{attributes}{ActionClass}
317 ? $args{attributes}{ActionClass}[0]
318 : $self->_action_class);
320 Class::MOP::load_class($class);
328 my $class = $self->action_class(%args);
331 Moose->init_meta(for_class => $class)
332 unless Class::MOP::does_metaclass_exist($class);
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;
342 : $self->config->{action_args}
346 %{ $action_args->{'*'} || {} },
347 %{ $action_args->{ $args{name} } || {} },
350 return $class->new({ %extra_args, %args });
353 sub gather_action_roles {
354 my ($self, %args) = @_;
357 (blessed $self ? $self->_action_roles : ()),
358 @{ $args{attributes}->{Does} || [] },
363 my ( $self, $c, $name, @attrs ) = @_;
367 foreach my $attr (@attrs) {
369 # Parse out :Foo(bar) into Foo => bar etc (and arrayify)
371 if ( my ( $key, $value ) = ( $attr =~ /^(.*?)(?:\(\s*(.+?)\s*\))?$/ ) )
374 if ( defined $value ) {
375 ( $value =~ s/^'(.*)'$/$1/ ) || ( $value =~ s/^"(.*)"/$1/ );
377 push( @{ $raw_attributes{$key} }, $value );
381 my ($actions_config, $all_actions_config);
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;
387 my $cfg = $self->config;
388 $actions_config = $self->merge_config_hashes($cfg->{actions}, $cfg->{action});
389 $all_actions_config = {};
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 } } : (),
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
405 %raw_attributes = (%{ $all_actions_config }, %raw_attributes)
406 unless $raw_attributes{Private};
408 my %final_attributes;
410 while (my ($key, $value) = each %raw_attributes){
411 my $new_attrs = $self->_parse_attr($c, $name, $key => $value );
412 push @{ $final_attributes{$_} }, @{ $new_attrs->{$_} } for keys %$new_attrs;
415 return \%final_attributes;
419 my ($self, $c, $name, $key, $values) = @_;
421 my %final_attributes;
422 foreach my $value (ref($values) eq 'ARRAY' ? @$values : $values) {
423 my $meth = "_parse_${key}_attr";
424 if ( my $code = $self->can($meth) ) {
425 my %new_attrs = $self->$code( $c, $name, $value );
426 while (my ($new_key, $value) = each %new_attrs){
427 my $new_attrs = $key eq $new_key ?
428 { $new_key => [$value] } :
429 $self->_parse_attr($c, $name, $new_key => $value );
430 push @{ $final_attributes{$_} }, @{ $new_attrs->{$_} } for keys %$new_attrs;
434 push( @{ $final_attributes{$key} }, $value );
438 return \%final_attributes;
441 sub _parse_Global_attr {
442 my ( $self, $c, $name, $value ) = @_;
443 # _parse_attr will call _parse_Path_attr for us
444 return Path => "/$name";
447 sub _parse_Absolute_attr { shift->_parse_Global_attr(@_); }
449 sub _parse_Local_attr {
450 my ( $self, $c, $name, $value ) = @_;
451 # _parse_attr will call _parse_Path_attr for us
452 return Path => $name;
455 sub _parse_Relative_attr { shift->_parse_Local_attr(@_); }
457 sub _parse_Path_attr {
458 my ( $self, $c, $name, $value ) = @_;
459 $value = '' if !defined $value;
460 if ( $value =~ m!^/! ) {
461 return ( 'Path', $value );
463 elsif ( length $value ) {
464 return ( 'Path', join( '/', $self->path_prefix($c), $value ) );
467 return ( 'Path', $self->path_prefix($c) );
471 sub _parse_Regex_attr {
472 my ( $self, $c, $name, $value ) = @_;
473 return ( 'Regex', $value );
476 sub _parse_Regexp_attr { shift->_parse_Regex_attr(@_); }
478 sub _parse_LocalRegex_attr {
479 my ( $self, $c, $name, $value ) = @_;
480 unless ( $value =~ s/^\^// ) { $value = "(?:.*?)$value"; }
482 my $prefix = $self->path_prefix( $c );
483 $prefix .= '/' if length( $prefix );
485 return ( 'Regex', "^${prefix}${value}" );
488 sub _parse_LocalRegexp_attr { shift->_parse_LocalRegex_attr(@_); }
490 sub _parse_Chained_attr {
491 my ($self, $c, $name, $value) = @_;
493 if (defined($value) && length($value)) {
495 $value = '/'.$self->action_namespace($c);
496 } elsif (my ($rel, $rest) = $value =~ /^((?:\.{2}\/)+)(.*)$/) {
497 my @parts = split '/', $self->action_namespace($c);
498 my @levels = split '/', $rel;
500 $value = '/'.join('/', @parts[0 .. $#parts - @levels], $rest);
501 } elsif ($value !~ m/^\//) {
502 my $action_ns = $self->action_namespace($c);
505 $value = '/'.join('/', $action_ns, $value);
507 $value = '/'.$value; # special case namespace '' (root)
514 return Chained => $value;
517 sub _parse_ChainedParent_attr {
518 my ($self, $c, $name, $value) = @_;
519 return $self->_parse_Chained_attr($c, $name, '../'.$name);
522 sub _parse_PathPrefix_attr {
523 my ( $self, $c ) = @_;
524 return PathPart => $self->path_prefix($c);
527 sub _parse_ActionClass_attr {
528 my ( $self, $c, $name, $value ) = @_;
529 my $appname = $self->_application;
530 $value = Catalyst::Utils::resolve_namespace($appname . '::Action', $self->_action_class, $value);
531 return ( 'ActionClass', $value );
534 sub _parse_MyAction_attr {
535 my ( $self, $c, $name, $value ) = @_;
537 my $appclass = Catalyst::Utils::class2appclass($self);
538 $value = "+${appclass}::Action::${value}";
540 return ( 'ActionClass', $value );
543 sub _parse_Does_attr {
544 my ($self, $app, $name, $value) = @_;
545 return Does => $self->_expand_role_shortname($value);
548 sub _expand_role_shortname {
549 my ($self, @shortnames) = @_;
550 my $app = $self->_application;
552 my $prefix = $self->can('_action_role_prefix') ? $self->_action_role_prefix : ['Catalyst::ActionRole::'];
553 my @prefixes = (qq{${app}::ActionRole::}, @$prefix);
555 return String::RewritePrefix->rewrite(
557 my $loaded = load_first_existing_class(
558 map { "$_$_[0]" } @prefixes
560 return first { $loaded =~ /^$_/ }
561 sort { length $b <=> length $a } @prefixes;
569 __PACKAGE__->meta->make_immutable;
577 Like any other L<Catalyst::Component>, controllers have a config hash,
578 accessible through $self->config from the controller actions. Some
579 settings are in use by the Catalyst framework:
583 This specifies the internal namespace the controller should be bound
584 to. By default the controller is bound to the URI version of the
585 controller name. For instance controller 'MyApp::Controller::Foo::Bar'
586 will be bound to 'foo/bar'. The default Root controller is an example
587 of setting namespace to '' (the null string).
591 Sets 'path_prefix', as described below.
595 Allows you to set the attributes that the dispatcher creates actions out of.
596 This allows you to do 'rails style routes', or override some of the
597 attribute definitions of actions composed from Roles.
598 You can set arguments globally (for all actions of the controller) and
599 specifically (for a single action).
603 '*' => { Chained => 'base', Args => 0 },
604 base => { Chained => '/', PathPart => '', CaptureArgs => 0 },
608 In the case above every sub in the package would be made into a Chain
609 endpoint with a URI the same as the sub name for each sub, chained
610 to the sub named C<base>. Ergo dispatch to C</example> would call the
611 C<base> method, then the C<example> method.
615 Allows you to set constructor arguments on your actions. You can set arguments
616 globally and specifically (as above).
617 This is particularly useful when using C<ActionRole>s
618 (L<Catalyst::Controller::ActionRole>) and custom C<ActionClass>es.
622 '*' => { globalarg1 => 'hello', globalarg2 => 'goodbye' },
623 'specific_action' => { customarg => 'arg1' },
627 In the case above the action class associated with C<specific_action> would get
628 passed the following arguments, in addition to the normal action constructor
629 arguments, when it is instantiated:
631 (globalarg1 => 'hello', globalarg2 => 'goodbye', customarg => 'arg1')
635 =head2 BUILDARGS ($app, @args)
637 From L<Catalyst::Component::ApplicationAttribute>, stashes the application
638 instance as $self->_application.
640 =head2 $self->action_for('name')
642 Returns the Catalyst::Action object (if any) for a given method name
645 =head2 $self->action_namespace($c)
647 Returns the private namespace for actions in this component. Defaults
648 to a value from the controller name (for
649 e.g. MyApp::Controller::Foo::Bar becomes "foo/bar") or can be
650 overridden from the "namespace" config key.
653 =head2 $self->path_prefix($c)
655 Returns the default path prefix for :PathPrefix, :Local, :LocalRegex and
656 relative :Path actions in this component. Defaults to the action_namespace or
657 can be overridden from the "path" config key.
659 =head2 $self->register_actions($c)
661 Finds all applicable actions for this component, creates
662 Catalyst::Action objects (using $self->create_action) for them and
663 registers them with $c->dispatcher.
665 =head2 $self->get_action_methods()
667 Returns a list of L<Moose::Meta::Method> objects, doing the
668 L<MooseX::MethodAttributes::Role::Meta::Method> role, which are the set of
669 action methods for this package.
671 =head2 $self->register_action_methods($c, @methods)
673 Creates action objects for a set of action methods using C< create_action >,
674 and registers them with the dispatcher.
676 =head2 $self->action_class(%args)
678 Used when a controller is creating an action to determine the correct base
681 =head2 $self->create_action(%args)
683 Called with a hash of data to be use for construction of a new
684 Catalyst::Action (or appropriate sub/alternative class) object.
686 =head2 $self->gather_action_roles(\%action_args)
688 Gathers the list of roles to apply to an action with the given %action_args.
690 =head2 $self->_application
694 Returns the application instance stored by C<new()>
698 Catalyst Contributors, see Catalyst.pm
702 This library is free software. You can redistribute it and/or modify
703 it under the same terms as Perl itself.