1 package Catalyst::Controller;
4 use Moose::Util qw/find_meta/;
5 use List::MoreUtils qw/uniq/;
6 use namespace::clean -except => 'meta';
8 BEGIN { extends qw/Catalyst::Component MooseX::MethodAttributes::Inheritable/; }
10 use MooseX::MethodAttributes;
11 use Catalyst::Exception;
14 with 'Catalyst::Component::ApplicationAttribute';
20 predicate => 'has_path_prefix',
23 has action_namespace => (
26 init_arg => 'namespace',
27 predicate => 'has_action_namespace',
31 accessor => '_controller_actions',
36 has action_args => (is => 'ro');
38 # ->config(actions => { '*' => ...
39 has _all_actions_attributes => (
44 builder => '_build__all_actions_attributes',
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);
54 # trigger lazy builder
55 $self->_all_actions_attributes;
58 sub _build__all_actions_attributes {
60 delete $self->_controller_actions->{'*'} || {};
65 Catalyst::Controller - Catalyst Controller base class
69 package MyApp::Controller::Search
70 use base qw/Catalyst::Controller/;
73 my ($self,$c,@args) = @_;
75 } # Dispatches to /search/foo
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.
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/;
90 __PACKAGE__->_dispatch_steps( [qw/_BEGIN _AUTO _ACTION/] );
91 __PACKAGE__->_action_class('Catalyst::Action');
94 sub _DISPATCH : Private {
95 my ( $self, $c ) = @_;
97 foreach my $disp ( @{ $self->_dispatch_steps } ) {
98 last unless $c->forward($disp);
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 };
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;
122 sub _ACTION : Private {
123 my ( $self, $c ) = @_;
125 && $c->action->can('execute')
126 && defined $c->req->action )
128 $c->action->dispatch( $c );
130 return !@{ $c->error };
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 };
142 my ( $self, $name ) = @_;
143 my $app = ($self->isa('Catalyst') ? $self : $self->_application);
144 return $app->dispatcher->get_action($name, $self->action_namespace);
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 {
152 my ( $self, $c ) = @_;
154 my $class = ref($self) || $self;
155 my $appclass = ref($c) || $c;
157 return $self->$orig if $self->has_action_namespace;
159 return $class->config->{namespace} if exists $class->config->{namespace};
164 $case_s = $appclass->config->{case_sensitive};
166 if ($self->isa('Catalyst')) {
167 $case_s = $class->config->{case_sensitive};
170 $case_s = ref($self->_application)->config->{case_sensitive};
172 confess("Can't figure out case_sensitive setting");
177 my $namespace = Catalyst::Utils::class2prefix($self->catalyst_component_name, $case_s) || '';
178 $self->$orig($namespace) if ref($self);
182 #Once again, this is probably better written as a builder method
183 around path_prefix => sub {
187 return $self->$orig if $self->has_path_prefix;
189 return $self->config->{path} if exists $self->config->{path};
191 my $namespace = $self->action_namespace(@_);
192 $self->$orig($namespace) if ref($self);
196 sub get_action_methods {
198 my $meta = find_meta($self) || confess("No metaclass setup for $self");
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;
205 # actions specified via config are also action_methods
209 $meta->find_method_by_name($_)
210 || confess( sprintf 'Action "%s" is not available from controller %s',
212 } keys %{ $self->_controller_actions }
214 return uniq @methods;
218 sub register_actions {
219 my ( $self, $c ) = @_;
220 $self->register_action_methods( $c, $self->get_action_methods );
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);
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.");
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"/ )
250 my $reverse = $namespace ? "${namespace}/${name}" : $name;
251 my $action = $self->create_action(
253 code => $method->body,
255 namespace => $namespace,
257 attributes => $attrs,
260 $c->dispatcher->register( $c, $action );
268 my $class = (exists $args{attributes}{ActionClass}
269 ? $args{attributes}{ActionClass}[0]
270 : $self->_action_class);
272 Class::MOP::load_class($class);
280 my $class = $self->action_class(%args);
284 : $self->config->{action_args}
288 %{ $action_args->{'*'} || {} },
289 %{ $action_args->{ $args{name} } || {} },
292 return $class->new({ %extra_args, %args });
296 my ( $self, $c, $name, @attrs ) = @_;
300 foreach my $attr (@attrs) {
302 # Parse out :Foo(bar) into Foo => bar etc (and arrayify)
304 if ( my ( $key, $value ) = ( $attr =~ /^(.*?)(?:\(\s*(.+?)\s*\))?$/ ) )
307 if ( defined $value ) {
308 ( $value =~ s/^'(.*)'$/$1/ ) || ( $value =~ s/^"(.*)"/$1/ );
310 push( @{ $raw_attributes{$key} }, $value );
314 my ($actions_config, $all_actions_config);
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;
320 my $cfg = $self->config;
321 $actions_config = $self->merge_config_hashes($cfg->{actions}, $cfg->{action});
322 $all_actions_config = {};
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 } } : (),
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
338 %raw_attributes = (%{ $all_actions_config }, %raw_attributes)
339 unless $raw_attributes{Private};
341 my %final_attributes;
343 foreach my $key (keys %raw_attributes) {
345 my $raw = $raw_attributes{$key};
347 foreach my $value (ref($raw) eq 'ARRAY' ? @$raw : $raw) {
349 my $meth = "_parse_${key}_attr";
350 if ( my $code = $self->can($meth) ) {
351 ( $key, $value ) = $self->$code( $c, $name, $value );
353 push( @{ $final_attributes{$key} }, $value );
357 return \%final_attributes;
360 sub _parse_Global_attr {
361 my ( $self, $c, $name, $value ) = @_;
362 return $self->_parse_Path_attr( $c, $name, "/$name" );
365 sub _parse_Absolute_attr { shift->_parse_Global_attr(@_); }
367 sub _parse_Local_attr {
368 my ( $self, $c, $name, $value ) = @_;
369 return $self->_parse_Path_attr( $c, $name, $name );
372 sub _parse_Relative_attr { shift->_parse_Local_attr(@_); }
374 sub _parse_Path_attr {
375 my ( $self, $c, $name, $value ) = @_;
376 $value = '' if !defined $value;
377 if ( $value =~ m!^/! ) {
378 return ( 'Path', $value );
380 elsif ( length $value ) {
381 return ( 'Path', join( '/', $self->path_prefix($c), $value ) );
384 return ( 'Path', $self->path_prefix($c) );
388 sub _parse_Regex_attr {
389 my ( $self, $c, $name, $value ) = @_;
390 return ( 'Regex', $value );
393 sub _parse_Regexp_attr { shift->_parse_Regex_attr(@_); }
395 sub _parse_LocalRegex_attr {
396 my ( $self, $c, $name, $value ) = @_;
397 unless ( $value =~ s/^\^// ) { $value = "(?:.*?)$value"; }
399 my $prefix = $self->path_prefix( $c );
400 $prefix .= '/' if length( $prefix );
402 return ( 'Regex', "^${prefix}${value}" );
405 sub _parse_LocalRegexp_attr { shift->_parse_LocalRegex_attr(@_); }
407 sub _parse_Chained_attr {
408 my ($self, $c, $name, $value) = @_;
410 if (defined($value) && length($value)) {
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;
417 $value = '/'.join('/', @parts[0 .. $#parts - @levels], $rest);
418 } elsif ($value !~ m/^\//) {
419 my $action_ns = $self->action_namespace($c);
422 $value = '/'.join('/', $action_ns, $value);
424 $value = '/'.$value; # special case namespace '' (root)
431 return Chained => $value;
434 sub _parse_ChainedParent_attr {
435 my ($self, $c, $name, $value) = @_;
436 return $self->_parse_Chained_attr($c, $name, '../'.$name);
439 sub _parse_PathPrefix_attr {
440 my ( $self, $c ) = @_;
441 return PathPart => $self->path_prefix($c);
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 );
451 sub _parse_MyAction_attr {
452 my ( $self, $c, $name, $value ) = @_;
454 my $appclass = Catalyst::Utils::class2appclass($self);
455 $value = "${appclass}::Action::${value}";
457 return ( 'ActionClass', $value );
460 __PACKAGE__->meta->make_immutable;
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:
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).
482 Sets 'path_prefix', as described below.
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).
494 '*' => { Chained => 'base', Args => 0 },
495 base => { Chained => '/', PathPart => '', CaptureArgs => 0 },
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.
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.
513 '*' => { globalarg1 => 'hello', globalarg2 => 'goodbye' },
514 'specific_action' => { customarg => 'arg1' },
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:
522 (globalarg1 => 'hello', globalarg2 => 'goodbye', customarg => 'arg1')
526 =head2 BUILDARGS ($app, @args)
528 From L<Catalyst::Component::ApplicationAttribute>, stashes the application
529 instance as $self->_application.
531 =head2 $self->action_for('name')
533 Returns the Catalyst::Action object (if any) for a given method name
536 =head2 $self->action_namespace($c)
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.
544 =head2 $self->path_prefix($c)
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.
550 =head2 $self->register_actions($c)
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.
556 =head2 $self->get_action_methods()
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.
562 =head2 $self->register_action_methods($c, @methods)
564 Creates action objects for a set of action methods using C< create_action >,
565 and registers them with the dispatcher.
567 =head2 $self->action_class(%args)
569 Used when a controller is creating an action to determine the correct base
572 =head2 $self->create_action(%args)
574 Called with a hash of data to be use for construction of a new
575 Catalyst::Action (or appropriate sub/alternative class) object.
577 =head2 $self->_application
581 Returns the application instance stored by C<new()>
585 Catalyst Contributors, see Catalyst.pm
589 This library is free software. You can redistribute it and/or modify
590 it under the same terms as Perl itself.