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';
21 predicate => 'has_path_prefix',
24 has action_namespace =>
28 init_arg => 'namespace',
29 predicate => 'has_action_namespace',
34 accessor => '_controller_actions',
40 my ($self, $args) = @_;
41 my $action = delete $args->{action} || {};
42 my $actions = delete $args->{actions} || {};
43 my $attr_value = $self->merge_config_hashes($actions, $action);
44 $self->_controller_actions($attr_value);
51 Catalyst::Controller - Catalyst Controller base class
55 package MyApp::Controller::Search
56 use base qw/Catalyst::Controller/;
59 my ($self,$c,@args) = @_;
61 } # Dispatches to /search/foo
65 Controllers are where the actions in the Catalyst framework
66 reside. Each action is represented by a function with an attribute to
67 identify what kind of action it is. See the L<Catalyst::Dispatcher>
68 for more info about how Catalyst dispatches to actions.
72 #I think both of these could be attributes. doesn't really seem like they need
73 #to ble class data. i think that attributes +default would work just fine
74 __PACKAGE__->mk_classdata($_) for qw/_dispatch_steps _action_class/;
76 __PACKAGE__->_dispatch_steps( [qw/_BEGIN _AUTO _ACTION/] );
77 __PACKAGE__->_action_class('Catalyst::Action');
80 sub _DISPATCH : Private {
81 my ( $self, $c ) = @_;
83 foreach my $disp ( @{ $self->_dispatch_steps } ) {
84 last unless $c->forward($disp);
90 sub _BEGIN : Private {
91 my ( $self, $c ) = @_;
92 my $begin = ( $c->get_actions( 'begin', $c->namespace ) )[-1];
93 return 1 unless $begin;
94 $begin->dispatch( $c );
95 return !@{ $c->error };
99 my ( $self, $c ) = @_;
100 my @auto = $c->get_actions( 'auto', $c->namespace );
101 foreach my $auto (@auto) {
102 $auto->dispatch( $c );
103 return 0 unless $c->state;
108 sub _ACTION : Private {
109 my ( $self, $c ) = @_;
111 && $c->action->can('execute')
112 && defined $c->req->action )
114 $c->action->dispatch( $c );
116 return !@{ $c->error };
120 my ( $self, $c ) = @_;
121 my $end = ( $c->get_actions( 'end', $c->namespace ) )[-1];
122 return 1 unless $end;
123 $end->dispatch( $c );
124 return !@{ $c->error };
128 my ( $self, $name ) = @_;
129 my $app = ($self->isa('Catalyst') ? $self : $self->_application);
130 return $app->dispatcher->get_action($name, $self->action_namespace);
133 #my opinion is that this whole sub really should be a builder method, not
134 #something that happens on every call. Anyone else disagree?? -- groditi
135 ## -- apparently this is all just waiting for app/ctx split
136 around action_namespace => sub {
138 my ( $self, $c ) = @_;
140 my $class = ref($self) || $self;
141 my $appclass = ref($c) || $c;
143 return $self->$orig if $self->has_action_namespace;
145 return $class->config->{namespace} if exists $class->config->{namespace};
150 $case_s = $appclass->config->{case_sensitive};
152 if ($self->isa('Catalyst')) {
153 $case_s = $class->config->{case_sensitive};
156 $case_s = ref($self->_application)->config->{case_sensitive};
158 confess("Can't figure out case_sensitive setting");
163 my $namespace = Catalyst::Utils::class2prefix($self->catalyst_component_name, $case_s) || '';
164 $self->$orig($namespace) if ref($self);
168 #Once again, this is probably better written as a builder method
169 around path_prefix => sub {
173 return $self->$orig if $self->has_path_prefix;
175 return $self->config->{path} if exists $self->config->{path};
177 my $namespace = $self->action_namespace(@_);
178 $self->$orig($namespace) if ref($self);
182 sub get_action_methods {
184 my $meta = find_meta($self) || confess("No metaclass setup for $self");
186 sprintf "Metaclass %s for %s cannot support register_actions.",
187 ref $meta, $meta->name,
188 ) unless $meta->can('get_nearest_methods_with_attributes');
189 my @methods = $meta->get_nearest_methods_with_attributes;
191 # actions specified via config are also action_methods
195 $meta->find_method_by_name($_)
196 || confess( sprintf 'Action "%s" is not available from controller %s',
199 # '*' is a special action configuration key to apply config to all
200 # actions. It's not to be looked up as a method
201 # name. _controller_actions should probably be cleaned up before
204 } keys %{ $self->_controller_actions }
206 return uniq @methods;
210 sub register_actions {
211 my ( $self, $c ) = @_;
212 $self->register_action_methods( $c, $self->get_action_methods );
215 sub register_action_methods {
216 my ( $self, $c, @methods ) = @_;
217 my $class = $self->catalyst_component_name;
218 #this is still not correct for some reason.
219 my $namespace = $self->action_namespace($c);
222 if (!blessed($self) && $self eq $c && scalar(@methods)) {
223 my @really_bad_methods = grep { ! /^_(DISPATCH|BEGIN|AUTO|ACTION|END)$/ } map { $_->name } @methods;
224 if (scalar(@really_bad_methods)) {
225 $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.");
229 foreach my $method (@methods) {
230 my $name = $method->name;
231 # Horrible hack! All method metaclasses should have an attributes
232 # method, core Moose bug - see r13354.
233 my $attributes = $method->can('attributes') ? $method->attributes : [];
234 my $attrs = $self->_parse_attrs( $c, $name, @{ $attributes } );
235 if ( $attrs->{Private} && ( keys %$attrs > 1 ) ) {
236 $c->log->debug( 'Bad action definition "'
237 . join( ' ', @{ $attributes } )
238 . qq/" for "$class->$name"/ )
242 my $reverse = $namespace ? "${namespace}/${name}" : $name;
243 my $action = $self->create_action(
245 code => $method->body,
247 namespace => $namespace,
249 attributes => $attrs,
252 $c->dispatcher->register( $c, $action );
260 my $class = (exists $args{attributes}{ActionClass}
261 ? $args{attributes}{ActionClass}[0]
262 : $self->_action_class);
264 Class::MOP::load_class($class);
272 my $class = $self->action_class(%args);
273 my $action_args = $self->config->{action_args};
276 %{ $action_args->{'*'} || {} },
277 %{ $action_args->{ $args{name} } || {} },
280 return $class->new({ %extra_args, %args });
284 my ( $self, $c, $name, @attrs ) = @_;
288 foreach my $attr (@attrs) {
290 # Parse out :Foo(bar) into Foo => bar etc (and arrayify)
292 if ( my ( $key, $value ) = ( $attr =~ /^(.*?)(?:\(\s*(.+?)\s*\))?$/ ) )
295 if ( defined $value ) {
296 ( $value =~ s/^'(.*)'$/$1/ ) || ( $value =~ s/^"(.*)"/$1/ );
298 push( @{ $raw_attributes{$key} }, $value );
304 $actions = $self->_controller_actions;
306 my $cfg = $self->config;
307 $actions = $self->merge_config_hashes($cfg->{actions}, $cfg->{action});
312 exists $actions->{$name} ? %{ $actions->{$name } } : (),
315 # Private actions with additional attributes will raise a warning and then
316 # be ignored. Adding '*' arguments to the default _DISPATCH / etc. methods,
317 # which are Private, will prevent those from being registered. They should
318 # probably be turned into :Actions instead, or we might want to otherwise
319 # disambiguate between those built-in internal actions and user-level
322 (exists $actions->{'*'} ? %{ $actions->{'*'} } : ()),
324 ) unless $raw_attributes{Private};
326 my %final_attributes;
328 foreach my $key (keys %raw_attributes) {
330 my $raw = $raw_attributes{$key};
332 foreach my $value (ref($raw) eq 'ARRAY' ? @$raw : $raw) {
334 my $meth = "_parse_${key}_attr";
335 if ( my $code = $self->can($meth) ) {
336 ( $key, $value ) = $self->$code( $c, $name, $value );
338 push( @{ $final_attributes{$key} }, $value );
342 return \%final_attributes;
345 sub _parse_Global_attr {
346 my ( $self, $c, $name, $value ) = @_;
347 return $self->_parse_Path_attr( $c, $name, "/$name" );
350 sub _parse_Absolute_attr { shift->_parse_Global_attr(@_); }
352 sub _parse_Local_attr {
353 my ( $self, $c, $name, $value ) = @_;
354 return $self->_parse_Path_attr( $c, $name, $name );
357 sub _parse_Relative_attr { shift->_parse_Local_attr(@_); }
359 sub _parse_Path_attr {
360 my ( $self, $c, $name, $value ) = @_;
361 $value = '' if !defined $value;
362 if ( $value =~ m!^/! ) {
363 return ( 'Path', $value );
365 elsif ( length $value ) {
366 return ( 'Path', join( '/', $self->path_prefix($c), $value ) );
369 return ( 'Path', $self->path_prefix($c) );
373 sub _parse_Regex_attr {
374 my ( $self, $c, $name, $value ) = @_;
375 return ( 'Regex', $value );
378 sub _parse_Regexp_attr { shift->_parse_Regex_attr(@_); }
380 sub _parse_LocalRegex_attr {
381 my ( $self, $c, $name, $value ) = @_;
382 unless ( $value =~ s/^\^// ) { $value = "(?:.*?)$value"; }
384 my $prefix = $self->path_prefix( $c );
385 $prefix .= '/' if length( $prefix );
387 return ( 'Regex', "^${prefix}${value}" );
390 sub _parse_LocalRegexp_attr { shift->_parse_LocalRegex_attr(@_); }
392 sub _parse_Chained_attr {
393 my ($self, $c, $name, $value) = @_;
395 if (defined($value) && length($value)) {
397 $value = '/'.$self->action_namespace($c);
398 } elsif (my ($rel, $rest) = $value =~ /^((?:\.{2}\/)+)(.*)$/) {
399 my @parts = split '/', $self->action_namespace($c);
400 my @levels = split '/', $rel;
402 $value = '/'.join('/', @parts[0 .. $#parts - @levels], $rest);
403 } elsif ($value !~ m/^\//) {
404 my $action_ns = $self->action_namespace($c);
407 $value = '/'.join('/', $action_ns, $value);
409 $value = '/'.$value; # special case namespace '' (root)
416 return Chained => $value;
419 sub _parse_ChainedParent_attr {
420 my ($self, $c, $name, $value) = @_;
421 return $self->_parse_Chained_attr($c, $name, '../'.$name);
424 sub _parse_PathPrefix_attr {
425 my ( $self, $c ) = @_;
426 return PathPart => $self->path_prefix($c);
429 sub _parse_ActionClass_attr {
430 my ( $self, $c, $name, $value ) = @_;
431 my $appname = $self->_application;
432 $value = Catalyst::Utils::resolve_namespace($appname . '::Action', $self->_action_class, $value);
433 return ( 'ActionClass', $value );
436 sub _parse_MyAction_attr {
437 my ( $self, $c, $name, $value ) = @_;
439 my $appclass = Catalyst::Utils::class2appclass($self);
440 $value = "${appclass}::Action::${value}";
442 return ( 'ActionClass', $value );
445 __PACKAGE__->meta->make_immutable;
453 Like any other L<Catalyst::Component>, controllers have a config hash,
454 accessible through $self->config from the controller actions. Some
455 settings are in use by the Catalyst framework:
459 This specifies the internal namespace the controller should be bound
460 to. By default the controller is bound to the URI version of the
461 controller name. For instance controller 'MyApp::Controller::Foo::Bar'
462 will be bound to 'foo/bar'. The default Root controller is an example
463 of setting namespace to '' (the null string).
467 Sets 'path_prefix', as described below.
471 Allows you to set the attributes that the dispatcher creates actions out of.
472 This allows you to do 'rails style routes', or override some of the
473 attribute defintions of actions composed from Roles.
474 You can set arguments globally (for all actions of the controller) and
475 specifically (for a single action).
479 '*' => { Chained => 'base', Args => 0 },
480 base => { Chained => '/', PathPart => '', CaptureArgs => 0 },
484 In the case above every sub in the package would be made into a Chain
485 endpoint with a URI the same as the sub name for each sub, chained
486 to the sub named C<base>. Ergo dispatch to C</example> would call the
487 C<base> method, then the C<example> method.
491 Allows you to set constructor arguments on your actions. You can set arguments
492 globally and specifically (as above).
493 This is particularly useful when using C<ActionRole>s
494 (L<Catalyst::Controller::ActionRole>) and custom C<ActionClass>es.
498 '*' => { globalarg1 => 'hello', globalarg2 => 'goodbye' },
499 'specific_action' => { customarg => 'arg1' },
503 In the case above the action class associated with C<specific_action> would get
504 passed the following arguments, in addition to the normal action constructor
505 arguments, when it is instantiated:
507 (globalarg1 => 'hello', globalarg2 => 'goodbye', customarg => 'arg1')
511 =head2 BUILDARGS ($app, @args)
513 From L<Catalyst::Component::ApplicationAttribute>, stashes the application
514 instance as $self->_application.
516 =head2 $self->action_for('name')
518 Returns the Catalyst::Action object (if any) for a given method name
521 =head2 $self->action_namespace($c)
523 Returns the private namespace for actions in this component. Defaults
524 to a value from the controller name (for
525 e.g. MyApp::Controller::Foo::Bar becomes "foo/bar") or can be
526 overridden from the "namespace" config key.
529 =head2 $self->path_prefix($c)
531 Returns the default path prefix for :PathPrefix, :Local, :LocalRegex and
532 relative :Path actions in this component. Defaults to the action_namespace or
533 can be overridden from the "path" config key.
535 =head2 $self->register_actions($c)
537 Finds all applicable actions for this component, creates
538 Catalyst::Action objects (using $self->create_action) for them and
539 registers them with $c->dispatcher.
541 =head2 $self->get_action_methods()
543 Returns a list of L<Moose::Meta::Method> objects, doing the
544 L<MooseX::MethodAttributes::Role::Meta::Method> role, which are the set of
545 action methods for this package.
547 =head2 $self->register_action_methods($c, @methods)
549 Creates action objects for a set of action methods using C< create_action >,
550 and registers them with the dispatcher.
552 =head2 $self->action_class(%args)
554 Used when a controller is creating an action to determine the correct base
557 =head2 $self->create_action(%args)
559 Called with a hash of data to be use for construction of a new
560 Catalyst::Action (or appropriate sub/alternative class) object.
562 =head2 $self->_application
566 Returns the application instance stored by C<new()>
570 Catalyst Contributors, see Catalyst.pm
574 This library is free software. You can redistribute it and/or modify
575 it under the same terms as Perl itself.