1 package Catalyst::Controller;
5 use String::RewritePrefix;
6 use Moose::Util qw/find_meta/;
7 use List::Util qw/first/;
8 use List::MoreUtils qw/uniq/;
9 use namespace::clean -except => 'meta';
11 BEGIN { extends qw/Catalyst::Component MooseX::MethodAttributes::Inheritable/; }
13 use MooseX::MethodAttributes;
14 use Catalyst::Exception;
17 with 'Catalyst::Component::ApplicationAttribute';
23 predicate => 'has_path_prefix',
26 has action_namespace => (
29 init_arg => 'namespace',
30 predicate => 'has_action_namespace',
34 accessor => '_controller_actions',
39 has _action_role_args => (
40 traits => [qw(Array)],
41 isa => 'ArrayRef[Str]',
42 init_arg => 'action_roles',
43 default => sub { [] },
45 _action_role_args => 'elements',
49 has _action_roles => (
50 traits => [qw(Array)],
51 isa => 'ArrayRef[RoleName]',
54 builder => '_build__action_roles',
56 _action_roles => 'elements',
60 has action_args => (is => 'ro');
62 # ->config(actions => { '*' => ...
63 has _all_actions_attributes => (
68 builder => '_build__all_actions_attributes',
72 my ($self, $args) = @_;
73 my $action = delete $args->{action} || {};
74 my $actions = delete $args->{actions} || {};
75 my $attr_value = $self->merge_config_hashes($actions, $action);
76 $self->_controller_actions($attr_value);
78 # trigger lazy builder
79 $self->_all_actions_attributes;
83 sub _build__action_roles {
85 my @roles = $self->_expand_role_shortname($self->_action_role_args);
86 Class::MOP::load_class($_) for @roles;
90 sub _build__all_actions_attributes {
92 delete $self->_controller_actions->{'*'} || {};
97 Catalyst::Controller - Catalyst Controller base class
101 package MyApp::Controller::Search
102 use base qw/Catalyst::Controller/;
105 my ($self,$c,@args) = @_;
107 } # Dispatches to /search/foo
111 Controllers are where the actions in the Catalyst framework
112 reside. Each action is represented by a function with an attribute to
113 identify what kind of action it is. See the L<Catalyst::Dispatcher>
114 for more info about how Catalyst dispatches to actions.
118 #I think both of these could be attributes. doesn't really seem like they need
119 #to ble class data. i think that attributes +default would work just fine
120 __PACKAGE__->mk_classdata($_) for qw/_dispatch_steps _action_class _action_role_prefix/;
122 __PACKAGE__->_dispatch_steps( [qw/_BEGIN _AUTO _ACTION/] );
123 __PACKAGE__->_action_class('Catalyst::Action');
124 __PACKAGE__->_action_role_prefix([ 'Catalyst::ActionRole::' ]);
127 sub _DISPATCH : Private {
128 my ( $self, $c ) = @_;
130 foreach my $disp ( @{ $self->_dispatch_steps } ) {
131 last unless $c->forward($disp);
137 sub _BEGIN : Private {
138 my ( $self, $c ) = @_;
139 my $begin = ( $c->get_actions( 'begin', $c->namespace ) )[-1];
140 return 1 unless $begin;
141 $begin->dispatch( $c );
142 return !@{ $c->error };
145 sub _AUTO : Private {
146 my ( $self, $c ) = @_;
147 my @auto = $c->get_actions( 'auto', $c->namespace );
148 foreach my $auto (@auto) {
149 $auto->dispatch( $c );
150 return 0 unless $c->state;
155 sub _ACTION : Private {
156 my ( $self, $c ) = @_;
158 && $c->action->can('execute')
159 && defined $c->req->action )
161 $c->action->dispatch( $c );
163 return !@{ $c->error };
167 my ( $self, $c ) = @_;
168 my $end = ( $c->get_actions( 'end', $c->namespace ) )[-1];
169 return 1 unless $end;
170 $end->dispatch( $c );
171 return !@{ $c->error };
175 my ( $self, $name ) = @_;
176 my $app = ($self->isa('Catalyst') ? $self : $self->_application);
177 return $app->dispatcher->get_action($name, $self->action_namespace);
180 #my opinion is that this whole sub really should be a builder method, not
181 #something that happens on every call. Anyone else disagree?? -- groditi
182 ## -- apparently this is all just waiting for app/ctx split
183 around action_namespace => sub {
185 my ( $self, $c ) = @_;
187 my $class = ref($self) || $self;
188 my $appclass = ref($c) || $c;
190 return $self->$orig if $self->has_action_namespace;
192 return $class->config->{namespace} if exists $class->config->{namespace};
197 $case_s = $appclass->config->{case_sensitive};
199 if ($self->isa('Catalyst')) {
200 $case_s = $class->config->{case_sensitive};
203 $case_s = ref($self->_application)->config->{case_sensitive};
205 confess("Can't figure out case_sensitive setting");
210 my $namespace = Catalyst::Utils::class2prefix($self->catalyst_component_name, $case_s) || '';
211 $self->$orig($namespace) if ref($self);
215 #Once again, this is probably better written as a builder method
216 around path_prefix => sub {
220 return $self->$orig if $self->has_path_prefix;
222 return $self->config->{path} if exists $self->config->{path};
224 my $namespace = $self->action_namespace(@_);
225 $self->$orig($namespace) if ref($self);
229 sub get_action_methods {
231 my $meta = find_meta($self) || confess("No metaclass setup for $self");
233 sprintf "Metaclass %s for %s cannot support register_actions.",
234 ref $meta, $meta->name,
235 ) unless $meta->can('get_nearest_methods_with_attributes');
236 my @methods = $meta->get_nearest_methods_with_attributes;
238 # actions specified via config are also action_methods
242 $meta->find_method_by_name($_)
243 || confess( sprintf 'Action "%s" is not available from controller %s',
245 } keys %{ $self->_controller_actions }
247 return uniq @methods;
251 sub register_actions {
252 my ( $self, $c ) = @_;
253 $self->register_action_methods( $c, $self->get_action_methods );
256 sub register_action_methods {
257 my ( $self, $c, @methods ) = @_;
258 my $class = $self->catalyst_component_name;
259 #this is still not correct for some reason.
260 my $namespace = $self->action_namespace($c);
263 if (!blessed($self) && $self eq $c && scalar(@methods)) {
264 my @really_bad_methods = grep { ! /^_(DISPATCH|BEGIN|AUTO|ACTION|END)$/ } map { $_->name } @methods;
265 if (scalar(@really_bad_methods)) {
266 $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.");
270 foreach my $method (@methods) {
271 my $name = $method->name;
272 # Horrible hack! All method metaclasses should have an attributes
273 # method, core Moose bug - see r13354.
274 my $attributes = $method->can('attributes') ? $method->attributes : [];
275 my $attrs = $self->_parse_attrs( $c, $name, @{ $attributes } );
276 if ( $attrs->{Private} && ( keys %$attrs > 1 ) ) {
277 $c->log->debug( 'Bad action definition "'
278 . join( ' ', @{ $attributes } )
279 . qq/" for "$class->$name"/ )
283 my $reverse = $namespace ? "${namespace}/${name}" : $name;
284 my $action = $self->create_action(
286 code => $method->body,
288 namespace => $namespace,
290 attributes => $attrs,
293 $c->dispatcher->register( $c, $action );
297 sub _apply_action_class_roles {
298 my ($self, $class, @roles) = @_;
300 Class::MOP::load_class($_) for @roles;
301 my $meta = Moose::Meta::Class->initialize($class)->create_anon_class(
302 superclasses => [$class],
306 $meta->add_method(meta => sub { $meta });
315 my $class = (exists $args{attributes}{ActionClass}
316 ? $args{attributes}{ActionClass}[0]
317 : $self->_action_class);
319 Class::MOP::load_class($class);
327 my $class = $self->action_class(%args);
330 Moose->init_meta(for_class => $class)
331 unless Class::MOP::does_metaclass_exist($class);
333 unless ($args{name} =~ /^_(DISPATCH|BEGIN|AUTO|ACTION|END)$/) {
334 my @roles = $self->gather_action_roles(%args);
335 $class = $self->_apply_action_class_roles($class, @roles) if @roles;
341 : $self->config->{action_args}
345 %{ $action_args->{'*'} || {} },
346 %{ $action_args->{ $args{name} } || {} },
349 return $class->new({ %extra_args, %args });
352 sub gather_action_roles {
353 my ($self, %args) = @_;
356 (blessed $self ? $self->_action_roles : ()),
357 @{ $args{attributes}->{Does} || [] },
362 my ( $self, $c, $name, @attrs ) = @_;
366 foreach my $attr (@attrs) {
368 # Parse out :Foo(bar) into Foo => bar etc (and arrayify)
370 if ( my ( $key, $value ) = ( $attr =~ /^(.*?)(?:\(\s*(.+?)\s*\))?$/ ) )
373 if ( defined $value ) {
374 ( $value =~ s/^'(.*)'$/$1/ ) || ( $value =~ s/^"(.*)"/$1/ );
376 push( @{ $raw_attributes{$key} }, $value );
380 my ($actions_config, $all_actions_config);
382 $actions_config = $self->_controller_actions;
383 # No, you're not getting actions => { '*' => ... } with actions in MyApp.
384 $all_actions_config = $self->_all_actions_attributes;
386 my $cfg = $self->config;
387 $actions_config = $self->merge_config_hashes($cfg->{actions}, $cfg->{action});
388 $all_actions_config = {};
393 # Note we deep copy array refs here to stop crapping on config
394 # when attributes are parsed. RT#65463
395 exists $actions_config->{$name} ? map { ref($_) eq 'ARRAY' ? [ @$_ ] : $_ } %{ $actions_config->{$name } } : (),
398 # Private actions with additional attributes will raise a warning and then
399 # be ignored. Adding '*' arguments to the default _DISPATCH / etc. methods,
400 # which are Private, will prevent those from being registered. They should
401 # probably be turned into :Actions instead, or we might want to otherwise
402 # disambiguate between those built-in internal actions and user-level
404 %raw_attributes = (%{ $all_actions_config }, %raw_attributes)
405 unless $raw_attributes{Private};
407 my %final_attributes;
409 foreach my $key (keys %raw_attributes) {
411 my $raw = $raw_attributes{$key};
413 foreach my $value (ref($raw) eq 'ARRAY' ? @$raw : $raw) {
415 my $meth = "_parse_${key}_attr";
416 if ( my $code = $self->can($meth) ) {
417 ( $key, $value ) = $self->$code( $c, $name, $value );
419 push( @{ $final_attributes{$key} }, $value );
423 return \%final_attributes;
426 sub _parse_Global_attr {
427 my ( $self, $c, $name, $value ) = @_;
428 return $self->_parse_Path_attr( $c, $name, "/$name" );
431 sub _parse_Absolute_attr { shift->_parse_Global_attr(@_); }
433 sub _parse_Local_attr {
434 my ( $self, $c, $name, $value ) = @_;
435 return $self->_parse_Path_attr( $c, $name, $name );
438 sub _parse_Relative_attr { shift->_parse_Local_attr(@_); }
440 sub _parse_Path_attr {
441 my ( $self, $c, $name, $value ) = @_;
442 $value = '' if !defined $value;
443 if ( $value =~ m!^/! ) {
444 return ( 'Path', $value );
446 elsif ( length $value ) {
447 return ( 'Path', join( '/', $self->path_prefix($c), $value ) );
450 return ( 'Path', $self->path_prefix($c) );
454 sub _parse_Regex_attr {
455 my ( $self, $c, $name, $value ) = @_;
456 return ( 'Regex', $value );
459 sub _parse_Regexp_attr { shift->_parse_Regex_attr(@_); }
461 sub _parse_LocalRegex_attr {
462 my ( $self, $c, $name, $value ) = @_;
463 unless ( $value =~ s/^\^// ) { $value = "(?:.*?)$value"; }
465 my $prefix = $self->path_prefix( $c );
466 $prefix .= '/' if length( $prefix );
468 return ( 'Regex', "^${prefix}${value}" );
471 sub _parse_LocalRegexp_attr { shift->_parse_LocalRegex_attr(@_); }
473 sub _parse_Chained_attr {
474 my ($self, $c, $name, $value) = @_;
476 if (defined($value) && length($value)) {
478 $value = '/'.$self->action_namespace($c);
479 } elsif (my ($rel, $rest) = $value =~ /^((?:\.{2}\/)+)(.*)$/) {
480 my @parts = split '/', $self->action_namespace($c);
481 my @levels = split '/', $rel;
483 $value = '/'.join('/', @parts[0 .. $#parts - @levels], $rest);
484 } elsif ($value !~ m/^\//) {
485 my $action_ns = $self->action_namespace($c);
488 $value = '/'.join('/', $action_ns, $value);
490 $value = '/'.$value; # special case namespace '' (root)
497 return Chained => $value;
500 sub _parse_ChainedParent_attr {
501 my ($self, $c, $name, $value) = @_;
502 return $self->_parse_Chained_attr($c, $name, '../'.$name);
505 sub _parse_PathPrefix_attr {
506 my ( $self, $c ) = @_;
507 return PathPart => $self->path_prefix($c);
510 sub _parse_ActionClass_attr {
511 my ( $self, $c, $name, $value ) = @_;
512 my $appname = $self->_application;
513 $value = Catalyst::Utils::resolve_namespace($appname . '::Action', $self->_action_class, $value);
514 return ( 'ActionClass', $value );
517 sub _parse_MyAction_attr {
518 my ( $self, $c, $name, $value ) = @_;
520 my $appclass = Catalyst::Utils::class2appclass($self);
521 $value = "${appclass}::Action::${value}";
523 return ( 'ActionClass', $value );
526 sub _parse_Does_attr {
527 my ($self, $app, $name, $value) = @_;
528 return Does => $self->_expand_role_shortname($value);
531 sub _expand_role_shortname {
532 my ($self, @shortnames) = @_;
533 my $app = $self->_application;
535 my $prefix = $self->can('_action_role_prefix') ? $self->_action_role_prefix : ['Catalyst::ActionRole::'];
536 my @prefixes = (qq{${app}::ActionRole::}, @$prefix);
538 return String::RewritePrefix->rewrite(
540 my $loaded = Class::MOP::load_first_existing_class(
541 map { "$_$_[0]" } @prefixes
543 return first { $loaded =~ /^$_/ }
544 sort { length $b <=> length $a } @prefixes;
552 __PACKAGE__->meta->make_immutable;
560 Like any other L<Catalyst::Component>, controllers have a config hash,
561 accessible through $self->config from the controller actions. Some
562 settings are in use by the Catalyst framework:
566 This specifies the internal namespace the controller should be bound
567 to. By default the controller is bound to the URI version of the
568 controller name. For instance controller 'MyApp::Controller::Foo::Bar'
569 will be bound to 'foo/bar'. The default Root controller is an example
570 of setting namespace to '' (the null string).
574 Sets 'path_prefix', as described below.
578 Allows you to set the attributes that the dispatcher creates actions out of.
579 This allows you to do 'rails style routes', or override some of the
580 attribute definitions of actions composed from Roles.
581 You can set arguments globally (for all actions of the controller) and
582 specifically (for a single action).
586 '*' => { Chained => 'base', Args => 0 },
587 base => { Chained => '/', PathPart => '', CaptureArgs => 0 },
591 In the case above every sub in the package would be made into a Chain
592 endpoint with a URI the same as the sub name for each sub, chained
593 to the sub named C<base>. Ergo dispatch to C</example> would call the
594 C<base> method, then the C<example> method.
598 Allows you to set constructor arguments on your actions. You can set arguments
599 globally and specifically (as above).
600 This is particularly useful when using C<ActionRole>s
601 (L<Catalyst::Controller::ActionRole>) and custom C<ActionClass>es.
605 '*' => { globalarg1 => 'hello', globalarg2 => 'goodbye' },
606 'specific_action' => { customarg => 'arg1' },
610 In the case above the action class associated with C<specific_action> would get
611 passed the following arguments, in addition to the normal action constructor
612 arguments, when it is instantiated:
614 (globalarg1 => 'hello', globalarg2 => 'goodbye', customarg => 'arg1')
618 =head2 BUILDARGS ($app, @args)
620 From L<Catalyst::Component::ApplicationAttribute>, stashes the application
621 instance as $self->_application.
623 =head2 $self->action_for('name')
625 Returns the Catalyst::Action object (if any) for a given method name
628 =head2 $self->action_namespace($c)
630 Returns the private namespace for actions in this component. Defaults
631 to a value from the controller name (for
632 e.g. MyApp::Controller::Foo::Bar becomes "foo/bar") or can be
633 overridden from the "namespace" config key.
636 =head2 $self->path_prefix($c)
638 Returns the default path prefix for :PathPrefix, :Local, :LocalRegex and
639 relative :Path actions in this component. Defaults to the action_namespace or
640 can be overridden from the "path" config key.
642 =head2 $self->register_actions($c)
644 Finds all applicable actions for this component, creates
645 Catalyst::Action objects (using $self->create_action) for them and
646 registers them with $c->dispatcher.
648 =head2 $self->get_action_methods()
650 Returns a list of L<Moose::Meta::Method> objects, doing the
651 L<MooseX::MethodAttributes::Role::Meta::Method> role, which are the set of
652 action methods for this package.
654 =head2 $self->register_action_methods($c, @methods)
656 Creates action objects for a set of action methods using C< create_action >,
657 and registers them with the dispatcher.
659 =head2 $self->action_class(%args)
661 Used when a controller is creating an action to determine the correct base
664 =head2 $self->create_action(%args)
666 Called with a hash of data to be use for construction of a new
667 Catalyst::Action (or appropriate sub/alternative class) object.
669 =head2 $self->gather_action_roles(\%action_args)
671 Gathers the list of roles to apply to an action with the given %action_args.
673 =head2 $self->_application
677 Returns the application instance stored by C<new()>
681 Catalyst Contributors, see Catalyst.pm
685 This library is free software. You can redistribute it and/or modify
686 it under the same terms as Perl itself.