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