remove a confusing and duplicate bit of documentation
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Controller.pm
CommitLineData
5ee249f2 1package Catalyst::Controller;
2
3use strict;
234763d4 4use base qw/Catalyst::Component Catalyst::AttrContainer Class::Accessor::Fast/;
5
6use Catalyst::Exception;
7use Catalyst::Utils;
8use Class::Inspector;
9use NEXT;
5ee249f2 10
11=head1 NAME
12
13Catalyst::Controller - Catalyst Controller base class
14
15=head1 SYNOPSIS
16
234763d4 17 package MyApp::Controller::Search
a269e0c2 18 use base qw/Catalyst::Controller/;
234763d4 19
20 sub foo : Local {
85d9fce6 21 my ($self,$c,@args) = @_;
22 ...
234763d4 23 } # Dispatches to /search/foo
5ee249f2 24
25=head1 DESCRIPTION
26
a269e0c2 27Controllers are where the actions in the Catalyst framework
28reside. Each action is represented by a function with an attribute to
29identify what kind of action it is. See the L<Catalyst::Dispatcher>
30for more info about how Catalyst dispatches to actions.
234763d4 31
32=cut
33
34__PACKAGE__->mk_classdata($_) for qw/_dispatch_steps _action_class/;
35
36__PACKAGE__->_dispatch_steps( [qw/_BEGIN _AUTO _ACTION/] );
37__PACKAGE__->_action_class('Catalyst::Action');
38
39__PACKAGE__->mk_accessors( qw/_application/ );
40
41### _app as alias
42*_app = *_application;
43
44sub _DISPATCH : Private {
45 my ( $self, $c ) = @_;
46
47 foreach my $disp ( @{ $self->_dispatch_steps } ) {
48 last unless $c->forward($disp);
49 }
50
51 $c->forward('_END');
52}
53
54sub _BEGIN : Private {
55 my ( $self, $c ) = @_;
56 my $begin = ( $c->get_actions( 'begin', $c->namespace ) )[-1];
57 return 1 unless $begin;
58 $begin->dispatch( $c );
59 return !@{ $c->error };
60}
61
62sub _AUTO : Private {
63 my ( $self, $c ) = @_;
64 my @auto = $c->get_actions( 'auto', $c->namespace );
65 foreach my $auto (@auto) {
66 $auto->dispatch( $c );
67 return 0 unless $c->state;
68 }
69 return 1;
70}
71
72sub _ACTION : Private {
73 my ( $self, $c ) = @_;
74 if ( ref $c->action
75 && $c->action->can('execute')
53119b78 76 && defined $c->req->action )
234763d4 77 {
78 $c->action->dispatch( $c );
79 }
80 return !@{ $c->error };
81}
82
83sub _END : Private {
84 my ( $self, $c ) = @_;
85 my $end = ( $c->get_actions( 'end', $c->namespace ) )[-1];
86 return 1 unless $end;
87 $end->dispatch( $c );
88 return !@{ $c->error };
89}
90
91sub new {
92 my $self = shift;
93 my $app = $_[0];
94 my $new = $self->NEXT::new(@_);
95 $new->_application( $app );
96 return $new;
97}
98
99
100sub action_for {
101 my ( $self, $name ) = @_;
102 my $app = ($self->isa('Catalyst') ? $self : $self->_application);
103 return $app->dispatcher->get_action($name, $self->action_namespace);
104}
105
106sub action_namespace {
107 my ( $self, $c ) = @_;
108 unless ( $c ) {
109 $c = ($self->isa('Catalyst') ? $self : $self->_application);
110 }
111 my $hash = (ref $self ? $self : $self->config); # hate app-is-class
112 return $hash->{namespace} if exists $hash->{namespace};
113 return Catalyst::Utils::class2prefix( ref($self) || $self,
114 $c->config->{case_sensitive} )
115 || '';
116}
117
118sub path_prefix {
119 my ( $self, $c ) = @_;
120 unless ( $c ) {
121 $c = ($self->isa('Catalyst') ? $self : $self->_application);
122 }
123 my $hash = (ref $self ? $self : $self->config); # hate app-is-class
124 return $hash->{path} if exists $hash->{path};
125 return shift->action_namespace(@_);
126}
127
128
129sub register_actions {
130 my ( $self, $c ) = @_;
131 my $class = ref $self || $self;
132 my $namespace = $self->action_namespace($c);
133 my %methods;
134 $methods{ $self->can($_) } = $_
135 for @{ Class::Inspector->methods($class) || [] };
136
137 # Advanced inheritance support for plugins and the like
138 my @action_cache;
139 {
140 no strict 'refs';
141 for my $isa ( @{"$class\::ISA"}, $class ) {
142 push @action_cache, @{ $isa->_action_cache }
143 if $isa->can('_action_cache');
144 }
145 }
146
147 foreach my $cache (@action_cache) {
148 my $code = $cache->[0];
149 my $method = delete $methods{$code}; # avoid dupe registers
150 next unless $method;
151 my $attrs = $self->_parse_attrs( $c, $method, @{ $cache->[1] } );
152 if ( $attrs->{Private} && ( keys %$attrs > 1 ) ) {
153 $c->log->debug( 'Bad action definition "'
154 . join( ' ', @{ $cache->[1] } )
155 . qq/" for "$class->$method"/ )
156 if $c->debug;
157 next;
158 }
159 my $reverse = $namespace ? "$namespace/$method" : $method;
160 my $action = $self->create_action(
161 name => $method,
162 code => $code,
163 reverse => $reverse,
164 namespace => $namespace,
165 class => $class,
166 attributes => $attrs,
167 );
168
169 $c->dispatcher->register( $c, $action );
170 }
171}
172
173sub create_action {
174 my $self = shift;
175 my %args = @_;
176
177 my $class = (exists $args{attributes}{ActionClass}
178 ? $args{attributes}{ActionClass}[0]
179 : $self->_action_class);
180
181 unless ( Class::Inspector->loaded($class) ) {
182 require Class::Inspector->filename($class);
183 }
184
185 return $class->new( \%args );
186}
187
188sub _parse_attrs {
189 my ( $self, $c, $name, @attrs ) = @_;
190
191 my %raw_attributes;
192
193 foreach my $attr (@attrs) {
194
195 # Parse out :Foo(bar) into Foo => bar etc (and arrayify)
196
197 if ( my ( $key, $value ) = ( $attr =~ /^(.*?)(?:\(\s*(.+?)\s*\))?$/ ) )
198 {
199
200 if ( defined $value ) {
201 ( $value =~ s/^'(.*)'$/$1/ ) || ( $value =~ s/^"(.*)"/$1/ );
202 }
203 push( @{ $raw_attributes{$key} }, $value );
204 }
205 }
206
207 my $hash = (ref $self ? $self : $self->config); # hate app-is-class
208
209 if (exists $hash->{actions} || exists $hash->{action}) {
210 my $a = $hash->{actions} || $hash->{action};
211 %raw_attributes = ((exists $a->{'*'} ? %{$a->{'*'}} : ()),
212 %raw_attributes,
213 (exists $a->{$name} ? %{$a->{$name}} : ()));
214 }
215
216 my %final_attributes;
217
218 foreach my $key (keys %raw_attributes) {
219
220 my $raw = $raw_attributes{$key};
221
222 foreach my $value (ref($raw) eq 'ARRAY' ? @$raw : $raw) {
223
224 my $meth = "_parse_${key}_attr";
225 if ( $self->can($meth) ) {
226 ( $key, $value ) = $self->$meth( $c, $name, $value );
227 }
228 push( @{ $final_attributes{$key} }, $value );
229 }
230 }
231
232 return \%final_attributes;
233}
234
235sub _parse_Global_attr {
236 my ( $self, $c, $name, $value ) = @_;
237 return $self->_parse_Path_attr( $c, $name, "/$name" );
238}
239
240sub _parse_Absolute_attr { shift->_parse_Global_attr(@_); }
241
242sub _parse_Local_attr {
243 my ( $self, $c, $name, $value ) = @_;
244 return $self->_parse_Path_attr( $c, $name, $name );
245}
246
247sub _parse_Relative_attr { shift->_parse_Local_attr(@_); }
248
249sub _parse_Path_attr {
250 my ( $self, $c, $name, $value ) = @_;
53119b78 251 $value = '' if !defined $value;
234763d4 252 if ( $value =~ m!^/! ) {
253 return ( 'Path', $value );
254 }
255 elsif ( length $value ) {
256 return ( 'Path', join( '/', $self->path_prefix($c), $value ) );
257 }
258 else {
259 return ( 'Path', $self->path_prefix($c) );
260 }
261}
262
263sub _parse_Regex_attr {
264 my ( $self, $c, $name, $value ) = @_;
265 return ( 'Regex', $value );
266}
267
268sub _parse_Regexp_attr { shift->_parse_Regex_attr(@_); }
269
270sub _parse_LocalRegex_attr {
271 my ( $self, $c, $name, $value ) = @_;
272 unless ( $value =~ s/^\^// ) { $value = "(?:.*?)$value"; }
273 return ( 'Regex', '^' . $self->path_prefix($c) . "/${value}" );
274}
275
276sub _parse_LocalRegexp_attr { shift->_parse_LocalRegex_attr(@_); }
277
278sub _parse_ActionClass_attr {
279 my ( $self, $c, $name, $value ) = @_;
280 unless ( $value =~ s/^\+// ) {
281 $value = join('::', $self->_action_class, $value );
282 }
283 return ( 'ActionClass', $value );
284}
285
9287719b 286sub _parse_MyAction_attr {
287 my ( $self, $c, $name, $value ) = @_;
288
289 my $appclass = Catalyst::Utils::class2appclass($self);
290 $value = "${appclass}::Action::${value}";
234763d4 291
9287719b 292 return ( 'ActionClass', $value );
293}
234763d4 294
2951;
296
297__END__
298
299=head1 CONFIGURATION
300
a269e0c2 301Like any other L<Catalyst::Component>, controllers have a config hash,
302accessible through $self->config from the controller actions. Some
303settings are in use by the Catalyst framework:
234763d4 304
305=head2 namespace
306
a269e0c2 307This specifies the internal namespace the controller should be bound
308to. By default the controller is bound to the URI version of the
309controller name. For instance controller 'MyApp::Controller::Foo::Bar'
310will be bound to 'foo/bar'. The default Root controller is an example
311of setting namespace to '' (the null string).
234763d4 312
2cbd9d12 313=head2 path
234763d4 314
315Sets 'path_prefix', as described below.
316
317=head1 METHODS
318
319=head2 $class->new($app, @args)
320
321Proxies through to NEXT::new and stashes the application instance as
322$self->_application.
323
324=head2 $self->action_for('name')
325
a269e0c2 326Returns the Catalyst::Action object (if any) for a given method name
327in this component.
234763d4 328
329=head2 $self->register_actions($c)
330
a269e0c2 331Finds all applicable actions for this component, creates
332Catalyst::Action objects (using $self->create_action) for them and
333registers them with $c->dispatcher.
234763d4 334
335=head2 $self->action_namespace($c)
336
a269e0c2 337Returns the private namespace for actions in this component. Defaults
338to a value from the controller name (for
339e.g. MyApp::Controller::Foo::Bar becomes "foo/bar") or can be
340overridden from the "namespace" config key.
234763d4 341
342
343=head2 $self->path_prefix($c)
344
a269e0c2 345Returns the default path prefix for :Local, :LocalRegex and relative
346:Path actions in this component. Defaults to the action_namespace or
347can be overridden from the "path" config key.
234763d4 348
349=head2 $self->create_action(%args)
350
a269e0c2 351Called with a hash of data to be use for construction of a new
352Catalyst::Action (or appropriate sub/alternative class) object.
234763d4 353
354Primarily designed for the use of register_actions.
355
a269e0c2 356=head2 $self->_application
234763d4 357
358=head2 $self->_app
359
360Returns the application instance stored by C<new()>
5ee249f2 361
362=head1 AUTHOR
363
364Sebastian Riedel, C<sri@oook.de>
234763d4 365Marcus Ramberg C<mramberg@cpan.org>
5ee249f2 366
367=head1 COPYRIGHT
368
a269e0c2 369This program is free software, you can redistribute it and/or modify
370it under the same terms as Perl itself.
5ee249f2 371
372=cut