Test :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
9356b981 307sub _parse_ChainedParent_attr {
308 my ($self, $c, $name, $value) = @_;
309 return $self->_parse_Chained_attr($c, $name, '../'.$name);
310}
311
e5d2cfdb 312sub _parse_PathPrefix_attr {
313 my $self = shift;
314 return PathPart => $self->path_prefix;
315}
316
234763d4 317sub _parse_ActionClass_attr {
318 my ( $self, $c, $name, $value ) = @_;
319 unless ( $value =~ s/^\+// ) {
320 $value = join('::', $self->_action_class, $value );
321 }
322 return ( 'ActionClass', $value );
323}
324
9287719b 325sub _parse_MyAction_attr {
326 my ( $self, $c, $name, $value ) = @_;
327
328 my $appclass = Catalyst::Utils::class2appclass($self);
329 $value = "${appclass}::Action::${value}";
234763d4 330
9287719b 331 return ( 'ActionClass', $value );
332}
234763d4 333
3341;
335
336__END__
337
338=head1 CONFIGURATION
339
a269e0c2 340Like any other L<Catalyst::Component>, controllers have a config hash,
341accessible through $self->config from the controller actions. Some
342settings are in use by the Catalyst framework:
234763d4 343
344=head2 namespace
345
a269e0c2 346This specifies the internal namespace the controller should be bound
347to. By default the controller is bound to the URI version of the
348controller name. For instance controller 'MyApp::Controller::Foo::Bar'
349will be bound to 'foo/bar'. The default Root controller is an example
350of setting namespace to '' (the null string).
234763d4 351
2cbd9d12 352=head2 path
234763d4 353
354Sets 'path_prefix', as described below.
355
356=head1 METHODS
357
358=head2 $class->new($app, @args)
359
360Proxies through to NEXT::new and stashes the application instance as
361$self->_application.
362
363=head2 $self->action_for('name')
364
a269e0c2 365Returns the Catalyst::Action object (if any) for a given method name
366in this component.
234763d4 367
368=head2 $self->register_actions($c)
369
a269e0c2 370Finds all applicable actions for this component, creates
371Catalyst::Action objects (using $self->create_action) for them and
372registers them with $c->dispatcher.
234763d4 373
374=head2 $self->action_namespace($c)
375
a269e0c2 376Returns the private namespace for actions in this component. Defaults
377to a value from the controller name (for
378e.g. MyApp::Controller::Foo::Bar becomes "foo/bar") or can be
379overridden from the "namespace" config key.
234763d4 380
381
382=head2 $self->path_prefix($c)
383
e5d2cfdb 384Returns the default path prefix for :PathPrefix, :Local, :LocalRegex and
385relative :Path actions in this component. Defaults to the action_namespace or
a269e0c2 386can be overridden from the "path" config key.
234763d4 387
388=head2 $self->create_action(%args)
389
a269e0c2 390Called with a hash of data to be use for construction of a new
391Catalyst::Action (or appropriate sub/alternative class) object.
234763d4 392
393Primarily designed for the use of register_actions.
394
a269e0c2 395=head2 $self->_application
234763d4 396
397=head2 $self->_app
398
399Returns the application instance stored by C<new()>
5ee249f2 400
0bf7ab71 401=head1 AUTHORS
5ee249f2 402
0bf7ab71 403Catalyst Contributors, see Catalyst.pm
5ee249f2 404
405=head1 COPYRIGHT
406
a269e0c2 407This program is free software, you can redistribute it and/or modify
408it under the same terms as Perl itself.
5ee249f2 409
410=cut