Implement :Chained('../action').
[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"; }
19c01ee1 273
274 my $prefix = $self->path_prefix( $c );
275 $prefix .= '/' if length( $prefix );
276
277 return ( 'Regex', "^${prefix}${value}" );
234763d4 278}
279
280sub _parse_LocalRegexp_attr { shift->_parse_LocalRegex_attr(@_); }
281
f3107403 282sub _parse_Chained_attr {
283 my ($self, $c, $name, $value) = @_;
284
285 if (defined($value) && length($value)) {
286 if ($value eq '.') {
287 $value = '/'.$self->action_namespace($c);
eb270c30 288 } elsif (my ($rest) = $value =~ /^\.{2}\/(.*)$/) {
289 my @parts = split '/', $self->action_namespace($c);
290 $value = '/'.join('/', @parts[0 .. $#parts-1], $rest);
f3107403 291 } elsif ($value !~ m/^\//) {
292 my $action_ns = $self->action_namespace($c);
293
294 if ($action_ns) {
295 $value = '/'.join('/', $action_ns, $value);
296 } else {
297 $value = '/'.$value; # special case namespace '' (root)
298 }
299 }
300 } else {
301 $value = '/'
302 }
303
304 return Chained => $value;
305}
306
e5d2cfdb 307sub _parse_PathPrefix_attr {
308 my $self = shift;
309 return PathPart => $self->path_prefix;
310}
311
234763d4 312sub _parse_ActionClass_attr {
313 my ( $self, $c, $name, $value ) = @_;
314 unless ( $value =~ s/^\+// ) {
315 $value = join('::', $self->_action_class, $value );
316 }
317 return ( 'ActionClass', $value );
318}
319
9287719b 320sub _parse_MyAction_attr {
321 my ( $self, $c, $name, $value ) = @_;
322
323 my $appclass = Catalyst::Utils::class2appclass($self);
324 $value = "${appclass}::Action::${value}";
234763d4 325
9287719b 326 return ( 'ActionClass', $value );
327}
234763d4 328
3291;
330
331__END__
332
333=head1 CONFIGURATION
334
a269e0c2 335Like any other L<Catalyst::Component>, controllers have a config hash,
336accessible through $self->config from the controller actions. Some
337settings are in use by the Catalyst framework:
234763d4 338
339=head2 namespace
340
a269e0c2 341This specifies the internal namespace the controller should be bound
342to. By default the controller is bound to the URI version of the
343controller name. For instance controller 'MyApp::Controller::Foo::Bar'
344will be bound to 'foo/bar'. The default Root controller is an example
345of setting namespace to '' (the null string).
234763d4 346
2cbd9d12 347=head2 path
234763d4 348
349Sets 'path_prefix', as described below.
350
351=head1 METHODS
352
353=head2 $class->new($app, @args)
354
355Proxies through to NEXT::new and stashes the application instance as
356$self->_application.
357
358=head2 $self->action_for('name')
359
a269e0c2 360Returns the Catalyst::Action object (if any) for a given method name
361in this component.
234763d4 362
363=head2 $self->register_actions($c)
364
a269e0c2 365Finds all applicable actions for this component, creates
366Catalyst::Action objects (using $self->create_action) for them and
367registers them with $c->dispatcher.
234763d4 368
369=head2 $self->action_namespace($c)
370
a269e0c2 371Returns the private namespace for actions in this component. Defaults
372to a value from the controller name (for
373e.g. MyApp::Controller::Foo::Bar becomes "foo/bar") or can be
374overridden from the "namespace" config key.
234763d4 375
376
377=head2 $self->path_prefix($c)
378
e5d2cfdb 379Returns the default path prefix for :PathPrefix, :Local, :LocalRegex and
380relative :Path actions in this component. Defaults to the action_namespace or
a269e0c2 381can be overridden from the "path" config key.
234763d4 382
383=head2 $self->create_action(%args)
384
a269e0c2 385Called with a hash of data to be use for construction of a new
386Catalyst::Action (or appropriate sub/alternative class) object.
234763d4 387
388Primarily designed for the use of register_actions.
389
a269e0c2 390=head2 $self->_application
234763d4 391
392=head2 $self->_app
393
394Returns the application instance stored by C<new()>
5ee249f2 395
0bf7ab71 396=head1 AUTHORS
5ee249f2 397
0bf7ab71 398Catalyst Contributors, see Catalyst.pm
5ee249f2 399
400=head1 COPYRIGHT
401
a269e0c2 402This program is free software, you can redistribute it and/or modify
403it under the same terms as Perl itself.
5ee249f2 404
405=cut