Tidy error, remove debug, tidy warn.
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Controller.pm
CommitLineData
5ee249f2 1package Catalyst::Controller;
2
ae29b412 3use Moose;
a58af53d 4use Moose::Util qw/find_meta/;
ae29b412 5
6use namespace::clean -except => 'meta';
7
ba545c13 8BEGIN { extends qw/Catalyst::Component MooseX::MethodAttributes::Inheritable/; }
234763d4 9
10use Catalyst::Exception;
11use Catalyst::Utils;
ae29b412 12
13with 'Catalyst::Component::ApplicationAttribute';
14
15has path_prefix =>
16 (
17 is => 'rw',
18 isa => 'Str',
19 init_arg => 'path',
20 predicate => 'has_path_prefix',
21 );
22
23has action_namespace =>
24 (
25 is => 'rw',
26 isa => 'Str',
27 init_arg => 'namespace',
28 predicate => 'has_action_namespace',
29 );
30
31has actions =>
32 (
33 is => 'rw',
34 isa => 'HashRef',
35 init_arg => undef,
36 );
37
38sub BUILD {
39 my ($self, $args) = @_;
40 my $action = delete $args->{action} || {};
41 my $actions = delete $args->{actions} || {};
42 my $attr_value = $self->merge_config_hashes($actions, $action);
43 $self->actions($attr_value);
44}
5ee249f2 45
46=head1 NAME
47
48Catalyst::Controller - Catalyst Controller base class
49
50=head1 SYNOPSIS
51
234763d4 52 package MyApp::Controller::Search
a269e0c2 53 use base qw/Catalyst::Controller/;
234763d4 54
55 sub foo : Local {
85d9fce6 56 my ($self,$c,@args) = @_;
57 ...
234763d4 58 } # Dispatches to /search/foo
5ee249f2 59
60=head1 DESCRIPTION
61
a269e0c2 62Controllers are where the actions in the Catalyst framework
63reside. Each action is represented by a function with an attribute to
64identify what kind of action it is. See the L<Catalyst::Dispatcher>
65for more info about how Catalyst dispatches to actions.
234763d4 66
67=cut
68
ae29b412 69#I think both of these could be attributes. doesn't really seem like they need
70#to ble class data. i think that attributes +default would work just fine
234763d4 71__PACKAGE__->mk_classdata($_) for qw/_dispatch_steps _action_class/;
72
73__PACKAGE__->_dispatch_steps( [qw/_BEGIN _AUTO _ACTION/] );
74__PACKAGE__->_action_class('Catalyst::Action');
75
234763d4 76
77sub _DISPATCH : Private {
78 my ( $self, $c ) = @_;
79
80 foreach my $disp ( @{ $self->_dispatch_steps } ) {
81 last unless $c->forward($disp);
82 }
83
84 $c->forward('_END');
85}
86
87sub _BEGIN : Private {
88 my ( $self, $c ) = @_;
89 my $begin = ( $c->get_actions( 'begin', $c->namespace ) )[-1];
90 return 1 unless $begin;
91 $begin->dispatch( $c );
92 return !@{ $c->error };
93}
94
95sub _AUTO : Private {
96 my ( $self, $c ) = @_;
97 my @auto = $c->get_actions( 'auto', $c->namespace );
98 foreach my $auto (@auto) {
99 $auto->dispatch( $c );
100 return 0 unless $c->state;
101 }
102 return 1;
103}
104
105sub _ACTION : Private {
106 my ( $self, $c ) = @_;
107 if ( ref $c->action
108 && $c->action->can('execute')
53119b78 109 && defined $c->req->action )
234763d4 110 {
111 $c->action->dispatch( $c );
112 }
113 return !@{ $c->error };
114}
115
116sub _END : Private {
117 my ( $self, $c ) = @_;
118 my $end = ( $c->get_actions( 'end', $c->namespace ) )[-1];
119 return 1 unless $end;
120 $end->dispatch( $c );
121 return !@{ $c->error };
122}
123
234763d4 124sub action_for {
125 my ( $self, $name ) = @_;
126 my $app = ($self->isa('Catalyst') ? $self : $self->_application);
127 return $app->dispatcher->get_action($name, $self->action_namespace);
128}
129
ae29b412 130#my opinion is that this whole sub really should be a builder method, not
131#something that happens on every call. Anyone else disagree?? -- groditi
132## -- apparently this is all just waiting for app/ctx split
133around action_namespace => sub {
134 my $orig = shift;
234763d4 135 my ( $self, $c ) = @_;
ae29b412 136
137 if( ref($self) ){
138 return $self->$orig if $self->has_action_namespace;
139 } else {
140 return $self->config->{namespace} if exists $self->config->{namespace};
234763d4 141 }
234763d4 142
ae29b412 143 my $case_s;
144 if( $c ){
145 $case_s = $c->config->{case_sensitive};
146 } else {
147 if ($self->isa('Catalyst')) {
148 $case_s = $self->config->{case_sensitive};
149 } else {
150 if (ref $self) {
151 $case_s = $self->_application->config->{case_sensitive};
152 } else {
153 confess("Can't figure out case_sensitive setting");
154 }
155 }
234763d4 156 }
ae29b412 157
158 my $namespace = Catalyst::Utils::class2prefix(ref($self) || $self, $case_s) || '';
159 $self->$orig($namespace) if ref($self);
160 return $namespace;
161};
162
163#Once again, this is probably better written as a builder method
164around path_prefix => sub {
165 my $orig = shift;
166 my $self = shift;
167 if( ref($self) ){
168 return $self->$orig if $self->has_path_prefix;
169 } else {
170 return $self->config->{path} if exists $self->config->{path};
171 }
172 my $namespace = $self->action_namespace(@_);
173 $self->$orig($namespace) if ref($self);
174 return $namespace;
175};
234763d4 176
177
178sub register_actions {
179 my ( $self, $c ) = @_;
180 my $class = ref $self || $self;
ae29b412 181 #this is still not correct for some reason.
234763d4 182 my $namespace = $self->action_namespace($c);
ae29b412 183 my $meta = find_meta($self);
56e19078 184 confess("Metaclass for " . ref($meta) ." for " . $meta->name
185 . " cannot support register_actions.")
67837eb6 186 unless $meta->can('get_all_methods_with_attributes');
a58af53d 187 my @methods = $meta->get_all_methods_with_attributes;
234763d4 188
ba545c13 189 foreach my $method (@methods) {
190 my $name = $method->name;
191 my $attributes = $method->attributes;
192 next unless $attributes;
193 my $attrs = $self->_parse_attrs( $c, $name, @{ $attributes } );
234763d4 194 if ( $attrs->{Private} && ( keys %$attrs > 1 ) ) {
195 $c->log->debug( 'Bad action definition "'
ba545c13 196 . join( ' ', @{ $attributes } )
197 . qq/" for "$class->$name"/ )
234763d4 198 if $c->debug;
199 next;
200 }
bc677969 201 my $reverse = $namespace ? "${namespace}/${name}" : $name;
234763d4 202 my $action = $self->create_action(
ba545c13 203 name => $name,
204 code => $method->body,
234763d4 205 reverse => $reverse,
206 namespace => $namespace,
207 class => $class,
208 attributes => $attrs,
209 );
210
211 $c->dispatcher->register( $c, $action );
212 }
213}
214
215sub create_action {
216 my $self = shift;
217 my %args = @_;
218
219 my $class = (exists $args{attributes}{ActionClass}
220 ? $args{attributes}{ActionClass}[0]
221 : $self->_action_class);
222
ae29b412 223 Class::MOP::load_class($class);
234763d4 224 return $class->new( \%args );
225}
226
227sub _parse_attrs {
228 my ( $self, $c, $name, @attrs ) = @_;
229
230 my %raw_attributes;
231
232 foreach my $attr (@attrs) {
233
234 # Parse out :Foo(bar) into Foo => bar etc (and arrayify)
235
236 if ( my ( $key, $value ) = ( $attr =~ /^(.*?)(?:\(\s*(.+?)\s*\))?$/ ) )
237 {
238
239 if ( defined $value ) {
240 ( $value =~ s/^'(.*)'$/$1/ ) || ( $value =~ s/^"(.*)"/$1/ );
241 }
242 push( @{ $raw_attributes{$key} }, $value );
243 }
244 }
245
ae29b412 246 #I know that the original behavior was to ignore action if actions was set
247 # but i actually think this may be a little more sane? we can always remove
248 # the merge behavior quite easily and go back to having actions have
249 # presedence over action by modifying the keys. i honestly think this is
250 # superior while mantaining really high degree of compat
251 my $actions;
252 if( ref($self) ) {
253 $actions = $self->actions;
254 } else {
255 my $cfg = $self->config;
256 $actions = $self->merge_config_hashes($cfg->{actions}, $cfg->{action});
234763d4 257 }
258
ae29b412 259 %raw_attributes = ((exists $actions->{'*'} ? %{$actions->{'*'}} : ()),
260 %raw_attributes,
261 (exists $actions->{$name} ? %{$actions->{$name}} : ()));
262
263
234763d4 264 my %final_attributes;
265
266 foreach my $key (keys %raw_attributes) {
267
268 my $raw = $raw_attributes{$key};
269
270 foreach my $value (ref($raw) eq 'ARRAY' ? @$raw : $raw) {
271
272 my $meth = "_parse_${key}_attr";
ae29b412 273 if ( my $code = $self->can($meth) ) {
274 ( $key, $value ) = $self->$code( $c, $name, $value );
234763d4 275 }
276 push( @{ $final_attributes{$key} }, $value );
277 }
278 }
279
280 return \%final_attributes;
281}
282
283sub _parse_Global_attr {
284 my ( $self, $c, $name, $value ) = @_;
285 return $self->_parse_Path_attr( $c, $name, "/$name" );
286}
287
288sub _parse_Absolute_attr { shift->_parse_Global_attr(@_); }
289
290sub _parse_Local_attr {
291 my ( $self, $c, $name, $value ) = @_;
292 return $self->_parse_Path_attr( $c, $name, $name );
293}
294
295sub _parse_Relative_attr { shift->_parse_Local_attr(@_); }
296
297sub _parse_Path_attr {
298 my ( $self, $c, $name, $value ) = @_;
53119b78 299 $value = '' if !defined $value;
234763d4 300 if ( $value =~ m!^/! ) {
301 return ( 'Path', $value );
302 }
303 elsif ( length $value ) {
304 return ( 'Path', join( '/', $self->path_prefix($c), $value ) );
305 }
306 else {
307 return ( 'Path', $self->path_prefix($c) );
308 }
309}
310
311sub _parse_Regex_attr {
312 my ( $self, $c, $name, $value ) = @_;
313 return ( 'Regex', $value );
314}
315
316sub _parse_Regexp_attr { shift->_parse_Regex_attr(@_); }
317
318sub _parse_LocalRegex_attr {
319 my ( $self, $c, $name, $value ) = @_;
320 unless ( $value =~ s/^\^// ) { $value = "(?:.*?)$value"; }
19c01ee1 321
322 my $prefix = $self->path_prefix( $c );
323 $prefix .= '/' if length( $prefix );
324
325 return ( 'Regex', "^${prefix}${value}" );
234763d4 326}
327
328sub _parse_LocalRegexp_attr { shift->_parse_LocalRegex_attr(@_); }
329
f3107403 330sub _parse_Chained_attr {
331 my ($self, $c, $name, $value) = @_;
332
333 if (defined($value) && length($value)) {
334 if ($value eq '.') {
335 $value = '/'.$self->action_namespace($c);
fb56008f 336 } elsif (my ($rel, $rest) = $value =~ /^((?:\.{2}\/)+)(.*)$/) {
eb270c30 337 my @parts = split '/', $self->action_namespace($c);
fb56008f 338 my @levels = split '/', $rel;
339
340 $value = '/'.join('/', @parts[0 .. $#parts - @levels], $rest);
f3107403 341 } elsif ($value !~ m/^\//) {
342 my $action_ns = $self->action_namespace($c);
343
344 if ($action_ns) {
345 $value = '/'.join('/', $action_ns, $value);
346 } else {
347 $value = '/'.$value; # special case namespace '' (root)
348 }
349 }
350 } else {
351 $value = '/'
352 }
353
354 return Chained => $value;
355}
356
9356b981 357sub _parse_ChainedParent_attr {
358 my ($self, $c, $name, $value) = @_;
359 return $self->_parse_Chained_attr($c, $name, '../'.$name);
360}
361
e5d2cfdb 362sub _parse_PathPrefix_attr {
363 my $self = shift;
364 return PathPart => $self->path_prefix;
365}
366
234763d4 367sub _parse_ActionClass_attr {
368 my ( $self, $c, $name, $value ) = @_;
369 unless ( $value =~ s/^\+// ) {
370 $value = join('::', $self->_action_class, $value );
371 }
372 return ( 'ActionClass', $value );
373}
374
9287719b 375sub _parse_MyAction_attr {
376 my ( $self, $c, $name, $value ) = @_;
377
378 my $appclass = Catalyst::Utils::class2appclass($self);
379 $value = "${appclass}::Action::${value}";
234763d4 380
9287719b 381 return ( 'ActionClass', $value );
382}
234763d4 383
ae29b412 384__PACKAGE__->meta->make_immutable;
385
234763d4 3861;
387
388__END__
389
390=head1 CONFIGURATION
391
a269e0c2 392Like any other L<Catalyst::Component>, controllers have a config hash,
393accessible through $self->config from the controller actions. Some
394settings are in use by the Catalyst framework:
234763d4 395
396=head2 namespace
397
a269e0c2 398This specifies the internal namespace the controller should be bound
399to. By default the controller is bound to the URI version of the
400controller name. For instance controller 'MyApp::Controller::Foo::Bar'
401will be bound to 'foo/bar'. The default Root controller is an example
402of setting namespace to '' (the null string).
234763d4 403
2cbd9d12 404=head2 path
234763d4 405
406Sets 'path_prefix', as described below.
407
408=head1 METHODS
409
410=head2 $class->new($app, @args)
411
412Proxies through to NEXT::new and stashes the application instance as
413$self->_application.
414
415=head2 $self->action_for('name')
416
a269e0c2 417Returns the Catalyst::Action object (if any) for a given method name
418in this component.
234763d4 419
420=head2 $self->register_actions($c)
421
a269e0c2 422Finds all applicable actions for this component, creates
423Catalyst::Action objects (using $self->create_action) for them and
424registers them with $c->dispatcher.
234763d4 425
426=head2 $self->action_namespace($c)
427
a269e0c2 428Returns the private namespace for actions in this component. Defaults
429to a value from the controller name (for
430e.g. MyApp::Controller::Foo::Bar becomes "foo/bar") or can be
431overridden from the "namespace" config key.
234763d4 432
433
434=head2 $self->path_prefix($c)
435
e5d2cfdb 436Returns the default path prefix for :PathPrefix, :Local, :LocalRegex and
437relative :Path actions in this component. Defaults to the action_namespace or
a269e0c2 438can be overridden from the "path" config key.
234763d4 439
440=head2 $self->create_action(%args)
441
a269e0c2 442Called with a hash of data to be use for construction of a new
443Catalyst::Action (or appropriate sub/alternative class) object.
234763d4 444
445Primarily designed for the use of register_actions.
446
a269e0c2 447=head2 $self->_application
234763d4 448
449=head2 $self->_app
450
451Returns the application instance stored by C<new()>
5ee249f2 452
0bf7ab71 453=head1 AUTHORS
5ee249f2 454
0bf7ab71 455Catalyst Contributors, see Catalyst.pm
5ee249f2 456
457=head1 COPYRIGHT
458
a269e0c2 459This program is free software, you can redistribute it and/or modify
460it under the same terms as Perl itself.
5ee249f2 461
462=cut