mro compat stuff
[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
143
144#we are wrapping the accessor, so just uyse a modifier since a normal sub would
145#just be overridden by the generated moose method
0bc4c3ab 146around action_namespace => sub {
0fc2d522 147 my $orig = shift;
148 my ( $self, $c ) = @_;
0bc4c3ab 149
5fb67d52 150 if( ref($self) ){
0bc4c3ab 151 return $self->$orig if $self->has_action_namespace;
0fc2d522 152 } else {
84ff88cf 153 warn "action_namespace called as class method";
154 # if the following won't change at runtime it should be lazy_building thing
0bc4c3ab 155 return $self->config->{namespace} if exists $self->config->{namespace};
156 }
234763d4 157
0bc4c3ab 158 #the following looks like a possible target for a default setting. i am not
159 #making the below the builder because i don't know if $c will vary from
0fc2d522 160 #call to call, which would affect case sensitivity settings -- groditi
0bc4c3ab 161 my $case_s;
162 if( $c ){
163 $case_s = $c->config->{case_sensitive};
164 } else {
165 if ($self->isa('Catalyst')) {
166 $case_s = $self->config->{case_sensitive};
167 } else {
168 if (ref $self) {
169 $case_s = $self->_application->config->{case_sensitive};
170 } else {
171 confess("Can't figure out case_sensitive setting");
172 }
173 }
234763d4 174 }
0bc4c3ab 175
84ff88cf 176 my $namespace = Catalyst::Utils::class2prefix(ref($self) || $self, $case_s) || '';
177 $self->$orig($namespace) if ref($self);
178 return $namespace;
0bc4c3ab 179};
180
0fc2d522 181#Once again, this is probably better written as a builder method
0bc4c3ab 182around path_prefix => sub {
183 my $orig = shift;
184 my $self = shift;
5fb67d52 185 if( ref($self) ){
0bc4c3ab 186 return $self->$orig if $self->has_path_prefix;
187 } else {
5fb67d52 188 return $self->config->{path} if exists $self->config->{path};
0bc4c3ab 189 }
84ff88cf 190 my $namespace = $self->action_namespace(@_);
191 $self->$orig($namespace) if ref($self);
192 return $namespace;
0bc4c3ab 193};
234763d4 194
195
196sub register_actions {
197 my ( $self, $c ) = @_;
198 my $class = ref $self || $self;
5fb67d52 199 #this is still not correct for some reason.
234763d4 200 my $namespace = $self->action_namespace($c);
84ff88cf 201 my $meta = $self->meta;
202 my %methods = map{ $_->{code}->body => $_->{name} }
2361ed67 203 grep {$_->{class} ne 'Moose::Object'} #ignore Moose::Object methods
84ff88cf 204 $meta->compute_all_applicable_methods;
205
234763d4 206
207 # Advanced inheritance support for plugins and the like
0bc4c3ab 208 #moose todo: migrate to eliminate CDI compat
234763d4 209 my @action_cache;
84ff88cf 210 for my $isa ( $meta->superclasses, $class ) {
211 if(my $coderef = $isa->can('_action_cache')){
212 push(@action_cache, @{ $isa->$coderef });
234763d4 213 }
214 }
215
216 foreach my $cache (@action_cache) {
217 my $code = $cache->[0];
218 my $method = delete $methods{$code}; # avoid dupe registers
219 next unless $method;
220 my $attrs = $self->_parse_attrs( $c, $method, @{ $cache->[1] } );
221 if ( $attrs->{Private} && ( keys %$attrs > 1 ) ) {
222 $c->log->debug( 'Bad action definition "'
223 . join( ' ', @{ $cache->[1] } )
224 . qq/" for "$class->$method"/ )
225 if $c->debug;
226 next;
227 }
5fb67d52 228 my $reverse = $namespace ? "${namespace}/${method}" : $method;
234763d4 229 my $action = $self->create_action(
230 name => $method,
231 code => $code,
232 reverse => $reverse,
233 namespace => $namespace,
234 class => $class,
235 attributes => $attrs,
236 );
237
238 $c->dispatcher->register( $c, $action );
239 }
240}
241
242sub create_action {
243 my $self = shift;
244 my %args = @_;
245
246 my $class = (exists $args{attributes}{ActionClass}
247 ? $args{attributes}{ActionClass}[0]
248 : $self->_action_class);
249
2361ed67 250 Class::MOP::load_class($class);
234763d4 251 return $class->new( \%args );
252}
253
254sub _parse_attrs {
255 my ( $self, $c, $name, @attrs ) = @_;
256
257 my %raw_attributes;
258
259 foreach my $attr (@attrs) {
260
261 # Parse out :Foo(bar) into Foo => bar etc (and arrayify)
262
263 if ( my ( $key, $value ) = ( $attr =~ /^(.*?)(?:\(\s*(.+?)\s*\))?$/ ) )
264 {
265
266 if ( defined $value ) {
267 ( $value =~ s/^'(.*)'$/$1/ ) || ( $value =~ s/^"(.*)"/$1/ );
268 }
269 push( @{ $raw_attributes{$key} }, $value );
270 }
271 }
272
0bc4c3ab 273 #I know that the original behavior was to ignore action if actions was set
274 # but i actually think this may be a little more sane? we can always remove
275 # the merge behavior quite easily and go back to having actions have
276 # presedence over action by modifying the keys. i honestly think this is
277 # superior while mantaining really high degree of compat
278 my $actions;
279 if( ref($self) ) {
280 $actions = $self->actions;
281 } else {
282 my $cfg = $self->config;
283 $actions = $self->merge_config_hashes($cfg->{actions}, $cfg->{action});
234763d4 284 }
285
0bc4c3ab 286 %raw_attributes = ((exists $actions->{'*'} ? %{$actions->{'*'}} : ()),
287 %raw_attributes,
288 (exists $actions->{$name} ? %{$actions->{$name}} : ()));
289
290
234763d4 291 my %final_attributes;
292
293 foreach my $key (keys %raw_attributes) {
294
295 my $raw = $raw_attributes{$key};
296
297 foreach my $value (ref($raw) eq 'ARRAY' ? @$raw : $raw) {
298
299 my $meth = "_parse_${key}_attr";
0bc4c3ab 300 if ( my $code = $self->can($meth) ) {
301 ( $key, $value ) = $self->$code( $c, $name, $value );
234763d4 302 }
303 push( @{ $final_attributes{$key} }, $value );
304 }
305 }
306
307 return \%final_attributes;
308}
309
310sub _parse_Global_attr {
311 my ( $self, $c, $name, $value ) = @_;
312 return $self->_parse_Path_attr( $c, $name, "/$name" );
313}
314
315sub _parse_Absolute_attr { shift->_parse_Global_attr(@_); }
316
317sub _parse_Local_attr {
318 my ( $self, $c, $name, $value ) = @_;
319 return $self->_parse_Path_attr( $c, $name, $name );
320}
321
322sub _parse_Relative_attr { shift->_parse_Local_attr(@_); }
323
324sub _parse_Path_attr {
325 my ( $self, $c, $name, $value ) = @_;
326 $value ||= '';
327 if ( $value =~ m!^/! ) {
328 return ( 'Path', $value );
329 }
330 elsif ( length $value ) {
331 return ( 'Path', join( '/', $self->path_prefix($c), $value ) );
332 }
333 else {
334 return ( 'Path', $self->path_prefix($c) );
335 }
336}
337
338sub _parse_Regex_attr {
339 my ( $self, $c, $name, $value ) = @_;
340 return ( 'Regex', $value );
341}
342
343sub _parse_Regexp_attr { shift->_parse_Regex_attr(@_); }
344
345sub _parse_LocalRegex_attr {
346 my ( $self, $c, $name, $value ) = @_;
347 unless ( $value =~ s/^\^// ) { $value = "(?:.*?)$value"; }
348 return ( 'Regex', '^' . $self->path_prefix($c) . "/${value}" );
349}
350
351sub _parse_LocalRegexp_attr { shift->_parse_LocalRegex_attr(@_); }
352
353sub _parse_ActionClass_attr {
354 my ( $self, $c, $name, $value ) = @_;
355 unless ( $value =~ s/^\+// ) {
356 $value = join('::', $self->_action_class, $value );
357 }
358 return ( 'ActionClass', $value );
359}
360
9287719b 361sub _parse_MyAction_attr {
362 my ( $self, $c, $name, $value ) = @_;
363
364 my $appclass = Catalyst::Utils::class2appclass($self);
365 $value = "${appclass}::Action::${value}";
234763d4 366
9287719b 367 return ( 'ActionClass', $value );
368}
234763d4 369
0fc2d522 370no Moose;
371
234763d4 3721;
373
374__END__
375
376=head1 CONFIGURATION
377
a269e0c2 378Like any other L<Catalyst::Component>, controllers have a config hash,
379accessible through $self->config from the controller actions. Some
380settings are in use by the Catalyst framework:
234763d4 381
382=head2 namespace
383
a269e0c2 384This specifies the internal namespace the controller should be bound
385to. By default the controller is bound to the URI version of the
386controller name. For instance controller 'MyApp::Controller::Foo::Bar'
387will be bound to 'foo/bar'. The default Root controller is an example
388of setting namespace to '' (the null string).
234763d4 389
ac5c933b 390=head2 path
234763d4 391
392Sets 'path_prefix', as described below.
393
394=head1 METHODS
395
396=head2 $class->new($app, @args)
397
398Proxies through to NEXT::new and stashes the application instance as
399$self->_application.
400
401=head2 $self->action_for('name')
402
a269e0c2 403Returns the Catalyst::Action object (if any) for a given method name
404in this component.
234763d4 405
406=head2 $self->register_actions($c)
407
a269e0c2 408Finds all applicable actions for this component, creates
409Catalyst::Action objects (using $self->create_action) for them and
410registers them with $c->dispatcher.
234763d4 411
412=head2 $self->action_namespace($c)
413
a269e0c2 414Returns the private namespace for actions in this component. Defaults
415to a value from the controller name (for
416e.g. MyApp::Controller::Foo::Bar becomes "foo/bar") or can be
417overridden from the "namespace" config key.
234763d4 418
419
420=head2 $self->path_prefix($c)
421
a269e0c2 422Returns the default path prefix for :Local, :LocalRegex and relative
423:Path actions in this component. Defaults to the action_namespace or
424can be overridden from the "path" config key.
234763d4 425
426=head2 $self->create_action(%args)
427
a269e0c2 428Called with a hash of data to be use for construction of a new
429Catalyst::Action (or appropriate sub/alternative class) object.
234763d4 430
431Primarily designed for the use of register_actions.
432
a269e0c2 433=head2 $self->_application
234763d4 434
435=head2 $self->_app
436
437Returns the application instance stored by C<new()>
5ee249f2 438
439=head1 AUTHOR
440
441Sebastian Riedel, C<sri@oook.de>
234763d4 442Marcus Ramberg C<mramberg@cpan.org>
5ee249f2 443
444=head1 COPYRIGHT
445
a269e0c2 446This program is free software, you can redistribute it and/or modify
447it under the same terms as Perl itself.
5ee249f2 448
449=cut