deprecate Catalyst::Base (left for compability reasons)
[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
18 use base qw/Catalyst::Controller;
19
20 sub foo : Local {
21 my ($self,$c,@args) = @_;
22 ...
23 } # Dispatches to /search/foo
5ee249f2 24
25=head1 DESCRIPTION
26
234763d4 27Controllers are where the actions in the Catalyst framework reside. each
28action is represented by a function with an attribute to identify what kind
29of action it is. See the L<Catalyst::Dispatcher> for more info about how
30Catalyst dispatches to actions.
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')
76 && $c->req->action )
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 ) = @_;
251 $value ||= '';
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
286
287
2881;
289
290__END__
291
292=head1 CONFIGURATION
293
294As any other L<Catalyst::Component>, controllers have a config
295hash, accessable through $self->config from the controller actions.
296Some settings are in use by the Catalyst framework:
297
298=head2 namespace
299
300This spesifies the internal namespace the controller should be bound to. By default
301the controller is bound to the uri version of the controller name. For instance
302controller 'MyApp::Controller::Foo::Bar' will be bound to 'foo/bar'. The default Root
303controller is an example of setting namespace to ''.
304
305=head2 prefix
306
307Sets 'path_prefix', as described below.
308
309=head1 METHODS
310
311=head2 $class->new($app, @args)
312
313Proxies through to NEXT::new and stashes the application instance as
314$self->_application.
315
316=head2 $self->action_for('name')
317
318Returns the Catalyst::Action object (if any) for a given method name in
319this component.
320
321=head2 $self->register_actions($c)
322
323Finds all applicable actions for this component, creates Catalyst::Action
324objects (using $self->create_action) for them and registers them with
325$c->dispatcher.
326
327=head2 $self->action_namespace($c)
328
329Returns the private namespace for actions in this component. Defaults to a value
330from the controller name (for e.g. MyApp::Controller::Foo::Bar becomes
331"foo/bar") or can be overriden from the "namespace" config key.
332
333
334=head2 $self->path_prefix($c)
335
336Returns the default path prefix for :Local, :LocalRegex and relative :Path
337actions in this component. Defaults to the action_namespace or can be
338overriden from the "path" config key.
339
340=head2 $self->create_action(%args)
341
342Called with a hash of data to be use for construction of a new Catalyst::Action
343(or appropriate sub/alternative class) object.
344
345Primarily designed for the use of register_actions.
346
347=head2 $self->_application
348
349=head2 $self->_app
350
351Returns the application instance stored by C<new()>
5ee249f2 352
353=head1 AUTHOR
354
355Sebastian Riedel, C<sri@oook.de>
234763d4 356Marcus Ramberg C<mramberg@cpan.org>
5ee249f2 357
358=head1 COPYRIGHT
359
360This program is free software, you can redistribute it and/or modify it under
361the same terms as Perl itself.
362
363=cut