add support for args to uri_for.
[catagits/Catalyst-Runtime.git] / lib / Catalyst.pm
1 package Catalyst;
2
3 use strict;
4 use base 'Catalyst::Base';
5 use bytes;
6 use UNIVERSAL::require;
7 use Catalyst::Exception;
8 use Catalyst::Log;
9 use Catalyst::Request;
10 use Catalyst::Request::Upload;
11 use Catalyst::Response;
12 use Catalyst::Utils;
13 use NEXT;
14 use Text::ASCIITable;
15 use Path::Class;
16 use Time::HiRes qw/gettimeofday tv_interval/;
17 use URI;
18 use Scalar::Util qw/weaken/;
19
20 __PACKAGE__->mk_accessors(
21     qw/counter depth request response state action namespace/
22 );
23
24 # Laziness++
25 *comp = \&component;
26 *req  = \&request;
27 *res  = \&response;
28
29 # For backwards compatibility
30 *finalize_output = \&finalize_body;
31
32 # For statistics
33 our $COUNT     = 1;
34 our $START     = time;
35 our $RECURSION = 1000;
36 our $DETACH    = "catalyst_detach\n";
37
38 require Module::Pluggable::Fast;
39
40 # Helper script generation
41 our $CATALYST_SCRIPT_GEN = 8;
42
43 __PACKAGE__->mk_classdata($_)
44   for qw/components arguments dispatcher engine log/;
45
46 our $VERSION = '5.49_01';
47
48 sub version { return $Catalyst::VERSION }
49
50 sub import {
51     my ( $class, @arguments ) = @_;
52
53     # We have to limit $class to Catalyst to avoid pushing Catalyst upon every
54     # callers @ISA.
55     return unless $class eq 'Catalyst';
56
57     my $caller = caller(0);
58
59     unless ( $caller->isa('Catalyst') ) {
60         no strict 'refs';
61         push @{"$caller\::ISA"}, $class;
62     }
63
64     $caller->arguments( [@arguments] );
65     $caller->setup_home;
66 }
67
68 =head1 NAME
69
70 Catalyst - The Elegant MVC Web Application Framework
71
72 =head1 SYNOPSIS
73
74     # use the helper to start a new application
75     catalyst.pl MyApp
76     cd MyApp
77
78     # add models, views, controllers
79     script/myapp_create.pl model Something
80     script/myapp_create.pl view Stuff
81     script/myapp_create.pl controller Yada
82
83     # built in testserver
84     script/myapp_server.pl
85
86     # command line interface
87     script/myapp_test.pl /yada
88
89
90     use Catalyst;
91
92     use Catalyst qw/My::Module My::OtherModule/;
93
94     use Catalyst '-Debug';
95
96     use Catalyst qw/-Debug -Engine=CGI/;
97
98     sub default : Private { $_[1]->res->output('Hello') } );
99
100     sub index : Path('/index.html') {
101         my ( $self, $c ) = @_;
102         $c->res->output('Hello');
103         $c->forward('foo');
104     }
105
106     sub product : Regex('^product[_]*(\d*).html$') {
107         my ( $self, $c ) = @_;
108         $c->stash->{template} = 'product.tt';
109         $c->stash->{product} = $c->req->snippets->[0];
110     }
111
112 See also L<Catalyst::Manual::Intro>
113
114 =head1 DESCRIPTION
115
116 The key concept of Catalyst is DRY (Don't Repeat Yourself).
117
118 See L<Catalyst::Manual> for more documentation.
119
120 Catalyst plugins can be loaded by naming them as arguments to the "use Catalyst" statement.
121 Omit the C<Catalyst::Plugin::> prefix from the plugin name,
122 so C<Catalyst::Plugin::My::Module> becomes C<My::Module>.
123
124     use Catalyst 'My::Module';
125
126 Special flags like -Debug and -Engine can also be specified as arguments when
127 Catalyst is loaded:
128
129     use Catalyst qw/-Debug My::Module/;
130
131 The position of plugins and flags in the chain is important, because they are
132 loaded in exactly the order that they appear.
133
134 The following flags are supported:
135
136 =over 4
137
138 =item -Debug
139
140 enables debug output, i.e.:
141
142     use Catalyst '-Debug';
143
144 this is equivalent to:
145
146     use Catalyst;
147     sub debug { 1 }
148
149 =item -Dispatcher
150
151 Force Catalyst to use a specific dispatcher.
152
153 =item -Engine
154
155 Force Catalyst to use a specific engine.
156 Omit the C<Catalyst::Engine::> prefix of the engine name, i.e.:
157
158     use Catalyst '-Engine=CGI';
159
160 =item -Home
161
162 Force Catalyst to use a specific home directory.
163
164 =item -Log
165
166 Specify log level.
167
168 =back
169
170 =head1 METHODS
171
172 =over 4
173
174 =item $c->action
175
176 Accessor for the current action
177
178 =item $c->comp($name)
179
180 =item $c->component($name)
181
182 Get a component object by name.
183
184     $c->comp('MyApp::Model::MyModel')->do_stuff;
185
186 =cut
187
188 sub component {
189     my $c = shift;
190
191     if (@_) {
192
193         my $name = shift;
194
195         my $appclass = ref $c || $c;
196
197         my @names = (
198             $name, "${appclass}::${name}",
199             map { "${appclass}::${_}::${name}" } qw/M V C/
200         );
201
202         foreach my $try (@names) {
203
204             if ( exists $c->components->{$try} ) {
205
206                 return $c->components->{$try};
207             }
208         }
209
210         foreach my $component ( keys %{ $c->components } ) {
211
212             return $c->components->{$component} if $component =~ /$name/i;
213         }
214
215     }
216
217     return sort keys %{ $c->components };
218 }
219
220 =item config
221
222 Returns a hashref containing your applications settings.
223
224 =item debug
225
226 Overload to enable debug messages.
227
228 =cut
229
230 sub debug { 0 }
231
232 =item $c->detach( $command [, \@arguments ] )
233
234 Like C<forward> but doesn't return.
235
236 =cut
237
238 sub detach { my $c = shift; $c->dispatcher->detach( $c, @_ ) }
239
240 =item $c->dispatcher
241
242 Contains the dispatcher instance.
243 Stringifies to class.
244
245 =item $c->forward( $command [, \@arguments ] )
246
247 Forward processing to a private action or a method from a class.
248 If you define a class without method it will default to process().
249 also takes an optional arrayref containing arguments to be passed
250 to the new function. $c->req->args will be reset upon returning 
251 from the function.
252
253     $c->forward('/foo');
254     $c->forward('index');
255     $c->forward(qw/MyApp::Model::CDBI::Foo do_stuff/);
256     $c->forward('MyApp::View::TT');
257
258 =cut
259
260 sub forward { my $c = shift; $c->dispatcher->forward( $c, @_ ) }
261
262 =item $c->namespace
263
264 Accessor to the namespace of the current action
265
266 =item $c->setup
267
268 Setup.
269
270     $c->setup;
271
272 =cut
273
274 sub setup {
275     my ( $class, @arguments ) = @_;
276
277     unless ( $class->isa('Catalyst') ) {
278
279         Catalyst::Exception->throw(
280             message => qq/'$class' does not inherit from Catalyst/ );
281     }
282
283     if ( $class->arguments ) {
284         @arguments = ( @arguments, @{ $class->arguments } );
285     }
286
287     # Process options
288     my $flags = {};
289
290     foreach (@arguments) {
291
292         if (/^-Debug$/) {
293             $flags->{log} =
294               ( $flags->{log} ) ? 'debug,' . $flags->{log} : 'debug';
295         }
296         elsif (/^-(\w+)=?(.*)$/) {
297             $flags->{ lc $1 } = $2;
298         }
299         else {
300             push @{ $flags->{plugins} }, $_;
301         }
302     }
303
304     $class->setup_log( delete $flags->{log} );
305     $class->setup_plugins( delete $flags->{plugins} );
306     $class->setup_dispatcher( delete $flags->{dispatcher} );
307     $class->setup_engine( delete $flags->{engine} );
308     $class->setup_home( delete $flags->{home} );
309
310     for my $flag ( sort keys %{$flags} ) {
311
312         if ( my $code = $class->can( 'setup_' . $flag ) ) {
313             &$code( $class, delete $flags->{$flag} );
314         }
315         else {
316             $class->log->warn(qq/Unknown flag "$flag"/);
317         }
318     }
319
320     $class->log->warn( "You are running an old helper script! "
321           . "Please update your scripts by regenerating the "
322           . "application and copying over the new scripts." )
323       if ( $ENV{CATALYST_SCRIPT_GEN}
324         && ( $ENV{CATALYST_SCRIPT_GEN} < $Catalyst::CATALYST_SCRIPT_GEN ) );
325
326     if ( $class->debug ) {
327
328         my @plugins = ();
329
330         {
331             no strict 'refs';
332             @plugins = grep { /^Catalyst::Plugin/ } @{"$class\::ISA"};
333         }
334
335         if (@plugins) {
336             my $t = Text::ASCIITable->new;
337             $t->setOptions( 'hide_HeadRow',  1 );
338             $t->setOptions( 'hide_HeadLine', 1 );
339             $t->setCols('Class');
340             $t->setColWidth( 'Class', 75, 1 );
341             $t->addRow($_) for @plugins;
342             $class->log->debug( "Loaded plugins:\n" . $t->draw );
343         }
344
345         my $dispatcher = $class->dispatcher;
346         my $engine     = $class->engine;
347         my $home       = $class->config->{home};
348
349         $class->log->debug(qq/Loaded dispatcher "$dispatcher"/);
350         $class->log->debug(qq/Loaded engine "$engine"/);
351
352         $home
353           ? ( -d $home )
354           ? $class->log->debug(qq/Found home "$home"/)
355           : $class->log->debug(qq/Home "$home" doesn't exist/)
356           : $class->log->debug(q/Couldn't find home/);
357     }
358
359     # Call plugins setup
360     {
361         no warnings qw/redefine/;
362         local *setup = sub { };
363         $class->setup;
364     }
365
366     # Initialize our data structure
367     $class->components( {} );
368
369     $class->setup_components;
370
371     if ( $class->debug ) {
372         my $t = Text::ASCIITable->new;
373         $t->setOptions( 'hide_HeadRow',  1 );
374         $t->setOptions( 'hide_HeadLine', 1 );
375         $t->setCols('Class');
376         $t->setColWidth( 'Class', 75, 1 );
377         $t->addRow($_) for sort keys %{ $class->components };
378         $class->log->debug( "Loaded components:\n" . $t->draw )
379           if ( @{ $t->{tbl_rows} } );
380     }
381
382     # Add our self to components, since we are also a component
383     $class->components->{$class} = $class;
384
385     $class->setup_actions;
386
387     if ( $class->debug ) {
388         my $name = $class->config->{name} || 'Application';
389         $class->log->info("$name powered by Catalyst $Catalyst::VERSION");
390     }
391     $class->log->_flush() if $class->log->can('_flush');
392 }
393
394 =item $c->uri_for($path,[@args])
395
396 Merges path with $c->request->base for absolute uri's and with
397 $c->request->match for relative uri's, then returns a normalized
398 L<URI> object. If any args are passed, they are added at the end
399 of the path.
400
401 =cut
402
403 sub uri_for {
404     my ( $c, $path , @args) = @_;
405     my $base     = $c->request->base->clone;
406     my $basepath = $base->path;
407     $basepath =~ s/\/$//;
408     $basepath .= '/';
409     my $match = $c->request->match;
410     # massage match, empty if absolute path
411     $match =~ s/^\///;
412     $match .= '/' if $match;
413     $match = '' if $path =~ /^\//;
414     $path =~ s/^\///;
415     # join args with '/', or a blank string
416     my $args=(scalar @args ? '/'.join('/',@args) : '');
417     return URI->new_abs( URI->new_abs( "$path$args", "$basepath$match" ), $base )
418       ->canonical;
419 }
420
421 =item $c->error
422
423 =item $c->error($error, ...)
424
425 =item $c->error($arrayref)
426
427 Returns an arrayref containing error messages.
428
429     my @error = @{ $c->error };
430
431 Add a new error.
432
433     $c->error('Something bad happened');
434
435 =cut
436
437 sub error {
438     my $c = shift;
439     my $error = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];
440     push @{ $c->{error} }, @$error;
441     return $c->{error};
442 }
443
444 =item $c->engine
445
446 Contains the engine instance.
447 Stringifies to the class.
448
449 =item $c->log
450
451 Contains the logging object.  Unless it is already set Catalyst sets this up with a
452 C<Catalyst::Log> object.  To use your own log class:
453
454     $c->log( MyLogger->new );
455     $c->log->info("now logging with my own logger!");
456
457 Your log class should implement the methods described in the C<Catalyst::Log>
458 man page.
459
460 =item $c->plugin( $name, $class, @args )
461
462 Instant plugins for Catalyst.
463 Classdata accessor/mutator will be created, class loaded and instantiated.
464
465     MyApp->plugin( 'prototype', 'HTML::Prototype' );
466
467     $c->prototype->define_javascript_functions;
468
469 =cut
470
471 sub plugin {
472     my ( $class, $name, $plugin, @args ) = @_;
473     $plugin->require;
474
475     if ( my $error = $UNIVERSAL::require::ERROR ) {
476         Catalyst::Exception->throw(
477             message => qq/Couldn't load instant plugin "$plugin", "$error"/ );
478     }
479
480     eval { $plugin->import };
481     $class->mk_classdata($name);
482     my $obj;
483     eval { $obj = $plugin->new(@args) };
484
485     if ($@) {
486         Catalyst::Exception->throw( message =>
487               qq/Couldn't instantiate instant plugin "$plugin", "$@"/ );
488     }
489
490     $class->$name($obj);
491     $class->log->debug(qq/Initialized instant plugin "$plugin" as "$name"/)
492       if $class->debug;
493 }
494
495 =item $c->request
496
497 =item $c->req
498
499 Returns a C<Catalyst::Request> object.
500
501     my $req = $c->req;
502
503 =item $c->response
504
505 =item $c->res
506
507 Returns a C<Catalyst::Response> object.
508
509     my $res = $c->res;
510
511 =item $c->state
512
513 Contains the return value of the last executed action.
514
515 =item $c->stash
516
517 Returns a hashref containing all your data.
518
519     print $c->stash->{foo};
520
521 Keys may be set in the stash by assigning to the hash reference, or by passing
522 either a single hash reference or a list of key/value pairs as arguments.
523
524 For example:
525
526     $c->stash->{foo} ||= 'yada';
527     $c->stash( { moose => 'majestic', qux => 0 } );
528     $c->stash( bar => 1, gorch => 2 );
529
530 =cut
531
532 sub stash {
533     my $c = shift;
534     if (@_) {
535         my $stash = @_ > 1 ? {@_} : $_[0];
536         while ( my ( $key, $val ) = each %$stash ) {
537             $c->{stash}->{$key} = $val;
538         }
539     }
540     return $c->{stash};
541 }
542
543 =item $c->welcome_message
544
545 Returns the Catalyst welcome HTML page.
546
547 =cut
548
549 sub welcome_message {
550     my $c      = shift;
551     my $name   = $c->config->{name};
552     my $logo   = $c->uri_for('/static/images/catalyst_logo.png');
553     my $prefix = Catalyst::Utils::appprefix( ref $c );
554     return <<"EOF";
555 <html>
556     <head>
557         <title>$name on Catalyst $VERSION</title>
558         <style type="text/css">
559             body {
560                 text-align: center;
561                 padding-left: 50%;
562                 color: #000;
563                 background-color: #eee;
564             }
565             div#content {
566                 width: 640px;
567                 margin-left: -320px;
568                 margin-top: 10px;
569                 margin-bottom: 10px;
570                 text-align: left;
571                 background-color: #ccc;
572                 border: 1px solid #aaa;
573                 -moz-border-radius: 10px;
574             }
575             p, h1, h2 {
576                 margin-left: 20px;
577                 margin-right: 20px;
578                 font-family: verdana, tahoma, sans-serif;
579             }
580             a {
581                 font-family: verdana, tahoma, sans-serif;
582             }
583             :link, :visited {
584                     text-decoration: none;
585                     color: #b00;
586                     border-bottom: 1px dotted #bbb;
587             }
588             :link:hover, :visited:hover {
589                     color: #555;
590             }
591             div#topbar {
592                 margin: 0px;
593             }
594             pre {
595                 margin: 10px;
596                 padding: 8px;
597             }
598             div#answers {
599                 padding: 8px;
600                 margin: 10px;
601                 background-color: #fff;
602                 border: 1px solid #aaa;
603                 -moz-border-radius: 10px;
604             }
605             h1 {
606                 font-size: 0.9em;
607                 font-weight: normal;
608                 text-align: center;
609             }
610             h2 {
611                 font-size: 1.0em;
612             }
613             p {
614                 font-size: 0.9em;
615             }
616             p img {
617                 float: right;
618                 margin-left: 10px;
619             }
620             b#appname {
621                 font-size: 1.6em;
622             }
623         </style>
624     </head>
625     <body>
626         <div id="content">
627             <div id="topbar">
628                 <h1><b id="appname">$name</b> on <a href="http://catalyst.perl.org">Catalyst</a>
629                     $VERSION</h1>
630              </div>
631              <div id="answers">
632                  <p>
633                  <img src="$logo"/>
634                  </p>
635                  <p>Welcome to the wonderful world of Catalyst.
636                     This <a href="http://en.wikipedia.org/wiki/MVC">MVC</a>
637                     framework will make web development something you had
638                     never expected it to be: Fun, rewarding and quick.</p>
639                  <h2>What to do now?</h2>
640                  <p>That really depends  on what <b>you</b> want to do.
641                     We do, however, provide you with a few starting points.</p>
642                  <p>If you want to jump right into web development with Catalyst
643                     you might want to check out the documentation.</p>
644                  <pre><code>perldoc <a href="http://cpansearch.perl.org/dist/Catalyst/lib/Catalyst/Manual/Intro.pod">Catalyst::Manual::Intro</a>
645 perldoc <a href="http://cpansearch.perl.org/dist/Catalyst/lib/Catalyst/Manual.pod">Catalyst::Manual</a></code></pre>
646                  <h2>What to do next?</h2>
647                  <p>Next it's time to write an actual application. Use the
648                     helper scripts to generate <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AController%3A%3A&mode=all">controllers</a>,
649                     <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AModel%3A%3A&mode=all">models</a> and
650                     <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AView%3A%3A&mode=all">views</a>,
651                     they can save you a lot of work.</p>
652                     <pre><code>script/${prefix}_create.pl -help</code></pre>
653                     <p>Also, be sure to check out the vast and growing
654                     collection of <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3APlugin%3A%3A&mode=all">plugins for Catalyst on CPAN</a>,
655                     you are likely to find what you need there.
656                     </p>
657
658                  <h2>Need help?</h2>
659                  <p>Catalyst has a very active community. Here are the main places to
660                     get in touch with us.</p>
661                  <ul>
662                      <li>
663                          <a href="http://dev.catalyst.perl.org">Wiki</a>
664                      </li>
665                      <li>
666                          <a href="http://lists.rawmode.org/mailman/listinfo/catalyst">Mailing-List</a>
667                      </li>
668                      <li>
669                          <a href="irc://irc.perl.org/catalyst">IRC channel #catalyst on irc.perl.org</a>
670                      </li>
671                  </ul>
672                  <h2>In conclusion</h2>
673                  <p>The Catalyst team hope you will enjoy using Catalyst as much 
674                     as we enjoyed making it. Please contact us if you have ideas
675                     for improvement or other feedback.</p>
676              </div>
677          </div>
678     </body>
679 </html>
680 EOF
681 }
682
683 =back
684
685 =head1 INTERNAL METHODS
686
687 =over 4
688
689 =item $c->benchmark($coderef)
690
691 Takes a coderef with arguments and returns elapsed time as float.
692
693     my ( $elapsed, $status ) = $c->benchmark( sub { return 1 } );
694     $c->log->info( sprintf "Processing took %f seconds", $elapsed );
695
696 =cut
697
698 sub benchmark {
699     my $c       = shift;
700     my $code    = shift;
701     my $time    = [gettimeofday];
702     my @return  = &$code(@_);
703     my $elapsed = tv_interval $time;
704     return wantarray ? ( $elapsed, @return ) : $elapsed;
705 }
706
707 =item $c->components
708
709 Contains the components.
710
711 =item $c->counter
712
713 Returns a hashref containing coderefs and execution counts.
714 (Needed for deep recursion detection) 
715
716 =item $c->depth
717
718 Returns the actual forward depth.
719
720 =item $c->dispatch
721
722 Dispatch request to actions.
723
724 =cut
725
726 sub dispatch { my $c = shift; $c->dispatcher->dispatch( $c, @_ ) }
727
728 =item $c->execute($class, $coderef)
729
730 Execute a coderef in given class and catch exceptions.
731 Errors are available via $c->error.
732
733 =cut
734
735 sub execute {
736     my ( $c, $class, $code ) = @_;
737     $class = $c->components->{$class} || $class;
738     $c->state(0);
739     my $callsub = ( caller(1) )[3];
740
741     my $action = '';
742     if ( $c->debug ) {
743         $action = "$code";
744         $action = "/$action" unless $action =~ /\-\>/;
745         $c->counter->{"$code"}++;
746
747         if ( $c->counter->{"$code"} > $RECURSION ) {
748             my $error = qq/Deep recursion detected in "$action"/;
749             $c->log->error($error);
750             $c->error($error);
751             $c->state(0);
752             return $c->state;
753         }
754
755         $action = "-> $action" if $callsub =~ /forward$/;
756     }
757     $c->{depth}++;
758     eval {
759         if ( $c->debug ) {
760             my ( $elapsed, @state ) =
761               $c->benchmark( $code, $class, $c, @{ $c->req->args } );
762             push @{ $c->{stats} }, [ $action, sprintf( '%fs', $elapsed ) ];
763             $c->state(@state);
764         }
765         else {
766             $c->state( &$code( $class, $c, @{ $c->req->args } ) || 0 )
767         }
768     };
769     $c->{depth}--;
770
771     if ( my $error = $@ ) {
772
773         if ( $error eq $DETACH ) { die $DETACH if $c->{depth} > 1 }
774         else {
775             unless ( ref $error ) {
776                 chomp $error;
777                 $error = qq/Caught exception "$error"/;
778             }
779
780             $c->log->error($error);
781             $c->error($error);
782             $c->state(0);
783         }
784     }
785     return $c->state;
786 }
787
788 =item $c->finalize
789
790 Finalize request.
791
792 =cut
793
794 sub finalize {
795     my $c = shift;
796
797     $c->finalize_uploads;
798
799     # Error
800     if ( $#{ $c->error } >= 0 ) {
801         $c->finalize_error;
802     }
803
804     $c->finalize_headers;
805
806     # HEAD request
807     if ( $c->request->method eq 'HEAD' ) {
808         $c->response->body('');
809     }
810
811     $c->finalize_body;
812
813     return $c->response->status;
814 }
815
816 =item $c->finalize_body
817
818 Finalize body.
819
820 =cut
821
822 sub finalize_body { my $c = shift; $c->engine->finalize_body( $c, @_ ) }
823
824 =item $c->finalize_cookies
825
826 Finalize cookies.
827
828 =cut
829
830 sub finalize_cookies { my $c = shift; $c->engine->finalize_cookies( $c, @_ ) }
831
832 =item $c->finalize_error
833
834 Finalize error.
835
836 =cut
837
838 sub finalize_error { my $c = shift; $c->engine->finalize_error( $c, @_ ) }
839
840 =item $c->finalize_headers
841
842 Finalize headers.
843
844 =cut
845
846 sub finalize_headers {
847     my $c = shift;
848
849     # Check if we already finalized headers
850     return if $c->response->{_finalized_headers};
851
852     # Handle redirects
853     if ( my $location = $c->response->redirect ) {
854         $c->log->debug(qq/Redirecting to "$location"/) if $c->debug;
855         $c->response->header( Location => $location );
856     }
857
858     # Content-Length
859     if ( $c->response->body && !$c->response->content_length ) {
860         $c->response->content_length( bytes::length( $c->response->body ) );
861     }
862
863     # Errors
864     if ( $c->response->status =~ /^(1\d\d|[23]04)$/ ) {
865         $c->response->headers->remove_header("Content-Length");
866         $c->response->body('');
867     }
868
869     $c->finalize_cookies;
870
871     $c->engine->finalize_headers( $c, @_ );
872
873     # Done
874     $c->response->{_finalized_headers} = 1;
875 }
876
877 =item $c->finalize_output
878
879 An alias for finalize_body.
880
881 =item $c->finalize_read
882
883 Finalize the input after reading is complete.
884
885 =cut
886
887 sub finalize_read { my $c = shift; $c->engine->finalize_read( $c, @_ ) }
888
889 =item $c->finalize_uploads
890
891 Finalize uploads.  Cleans up any temporary files.
892
893 =cut
894
895 sub finalize_uploads { my $c = shift; $c->engine->finalize_uploads( $c, @_ ) }
896
897 =item $c->get_action( $action, $namespace, $inherit )
898
899 Get an action in a given namespace.
900
901 =cut
902
903 sub get_action { my $c = shift; $c->dispatcher->get_action( $c, @_ ) }
904
905 =item handle_request( $class, @arguments )
906
907 Handles the request.
908
909 =cut
910
911 sub handle_request {
912     my ( $class, @arguments ) = @_;
913
914     # Always expect worst case!
915     my $status = -1;
916     eval {
917         my @stats = ();
918
919         my $handler = sub {
920             my $c = $class->prepare(@arguments);
921             $c->{stats} = \@stats;
922             $c->dispatch;
923             return $c->finalize;
924         };
925
926         if ( $class->debug ) {
927             my $elapsed;
928             ( $elapsed, $status ) = $class->benchmark($handler);
929             $elapsed = sprintf '%f', $elapsed;
930             my $av = sprintf '%.3f',
931               ( $elapsed == 0 ? '??' : ( 1 / $elapsed ) );
932             my $t = Text::ASCIITable->new;
933             $t->setCols( 'Action', 'Time' );
934             $t->setColWidth( 'Action', 64, 1 );
935             $t->setColWidth( 'Time',   9,  1 );
936
937             for my $stat (@stats) { $t->addRow( $stat->[0], $stat->[1] ) }
938             $class->log->info(
939                 "Request took ${elapsed}s ($av/s)\n" . $t->draw );
940         }
941         else { $status = &$handler }
942
943     };
944
945     if ( my $error = $@ ) {
946         chomp $error;
947         $class->log->error(qq/Caught exception in engine "$error"/);
948     }
949
950     $COUNT++;
951     $class->log->_flush() if $class->log->can('_flush');
952     return $status;
953 }
954
955 =item $c->prepare(@arguments)
956
957 Turns the engine-specific request( Apache, CGI ... )
958 into a Catalyst context .
959
960 =cut
961
962 sub prepare {
963     my ( $class, @arguments ) = @_;
964
965     my $c = bless {
966         counter => {},
967         depth   => 0,
968         request => Catalyst::Request->new(
969             {
970                 arguments        => [],
971                 body_parameters  => {},
972                 cookies          => {},
973                 headers          => HTTP::Headers->new,
974                 parameters       => {},
975                 query_parameters => {},
976                 secure           => 0,
977                 snippets         => [],
978                 uploads          => {}
979             }
980         ),
981         response => Catalyst::Response->new(
982             {
983                 body    => '',
984                 cookies => {},
985                 headers => HTTP::Headers->new(),
986                 status  => 200
987             }
988         ),
989         stash => {},
990         state => 0
991     }, $class;
992
993     # For on-demand data
994     $c->request->{_context}  = $c;
995     $c->response->{_context} = $c;
996     weaken( $c->request->{_context} );
997     weaken( $c->response->{_context} );
998
999     if ( $c->debug ) {
1000         my $secs = time - $START || 1;
1001         my $av = sprintf '%.3f', $COUNT / $secs;
1002         $c->log->debug('**********************************');
1003         $c->log->debug("* Request $COUNT ($av/s) [$$]");
1004         $c->log->debug('**********************************');
1005         $c->res->headers->header( 'X-Catalyst' => $Catalyst::VERSION );
1006     }
1007
1008     $c->prepare_request(@arguments);
1009     $c->prepare_connection;
1010     $c->prepare_query_parameters;
1011     $c->prepare_headers;
1012     $c->prepare_cookies;
1013     $c->prepare_path;
1014
1015     # On-demand parsing
1016     $c->prepare_body unless $c->config->{parse_on_demand};
1017
1018     $c->prepare_action;
1019     my $method  = $c->req->method  || '';
1020     my $path    = $c->req->path    || '';
1021     my $address = $c->req->address || '';
1022
1023     $c->log->debug(qq/"$method" request for "$path" from $address/)
1024       if $c->debug;
1025
1026     return $c;
1027 }
1028
1029 =item $c->prepare_action
1030
1031 Prepare action.
1032
1033 =cut
1034
1035 sub prepare_action { my $c = shift; $c->dispatcher->prepare_action( $c, @_ ) }
1036
1037 =item $c->prepare_body
1038
1039 Prepare message body.
1040
1041 =cut
1042
1043 sub prepare_body {
1044     my $c = shift;
1045
1046     # Do we run for the first time?
1047     return if defined $c->request->{_body};
1048
1049     # Initialize on-demand data
1050     $c->engine->prepare_body( $c, @_ );
1051     $c->prepare_parameters;
1052     $c->prepare_uploads;
1053
1054     if ( $c->debug && keys %{ $c->req->body_parameters } ) {
1055         my $t = Text::ASCIITable->new;
1056         $t->setCols( 'Key', 'Value' );
1057         $t->setColWidth( 'Key',   37, 1 );
1058         $t->setColWidth( 'Value', 36, 1 );
1059         $t->alignCol( 'Value', 'right' );
1060         for my $key ( sort keys %{ $c->req->body_parameters } ) {
1061             my $param = $c->req->body_parameters->{$key};
1062             my $value = defined($param) ? $param : '';
1063             $t->addRow( $key,
1064                 ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
1065         }
1066         $c->log->debug( "Body Parameters are:\n" . $t->draw );
1067     }
1068 }
1069
1070 =item $c->prepare_body_chunk( $chunk )
1071
1072 Prepare a chunk of data before sending it to HTTP::Body.
1073
1074 =cut
1075
1076 sub prepare_body_chunk {
1077     my $c = shift;
1078     $c->engine->prepare_body_chunk( $c, @_ );
1079 }
1080
1081 =item $c->prepare_body_parameters
1082
1083 Prepare body parameters.
1084
1085 =cut
1086
1087 sub prepare_body_parameters {
1088     my $c = shift;
1089     $c->engine->prepare_body_parameters( $c, @_ );
1090 }
1091
1092 =item $c->prepare_connection
1093
1094 Prepare connection.
1095
1096 =cut
1097
1098 sub prepare_connection {
1099     my $c = shift;
1100     $c->engine->prepare_connection( $c, @_ );
1101 }
1102
1103 =item $c->prepare_cookies
1104
1105 Prepare cookies.
1106
1107 =cut
1108
1109 sub prepare_cookies { my $c = shift; $c->engine->prepare_cookies( $c, @_ ) }
1110
1111 =item $c->prepare_headers
1112
1113 Prepare headers.
1114
1115 =cut
1116
1117 sub prepare_headers { my $c = shift; $c->engine->prepare_headers( $c, @_ ) }
1118
1119 =item $c->prepare_parameters
1120
1121 Prepare parameters.
1122
1123 =cut
1124
1125 sub prepare_parameters {
1126     my $c = shift;
1127     $c->prepare_body_parameters;
1128     $c->engine->prepare_parameters( $c, @_ );
1129 }
1130
1131 =item $c->prepare_path
1132
1133 Prepare path and base.
1134
1135 =cut
1136
1137 sub prepare_path { my $c = shift; $c->engine->prepare_path( $c, @_ ) }
1138
1139 =item $c->prepare_query_parameters
1140
1141 Prepare query parameters.
1142
1143 =cut
1144
1145 sub prepare_query_parameters {
1146     my $c = shift;
1147
1148     $c->engine->prepare_query_parameters( $c, @_ );
1149
1150     if ( $c->debug && keys %{ $c->request->query_parameters } ) {
1151         my $t = Text::ASCIITable->new;
1152         $t->setCols( 'Key', 'Value' );
1153         $t->setColWidth( 'Key',   37, 1 );
1154         $t->setColWidth( 'Value', 36, 1 );
1155         $t->alignCol( 'Value', 'right' );
1156         for my $key ( sort keys %{ $c->req->query_parameters } ) {
1157             my $param = $c->req->query_parameters->{$key};
1158             my $value = defined($param) ? $param : '';
1159             $t->addRow( $key,
1160                 ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
1161         }
1162         $c->log->debug( "Query Parameters are:\n" . $t->draw );
1163     }
1164 }
1165
1166 =item $c->prepare_read
1167
1168 Prepare the input for reading.
1169
1170 =cut
1171
1172 sub prepare_read { my $c = shift; $c->engine->prepare_read( $c, @_ ) }
1173
1174 =item $c->prepare_request
1175
1176 Prepare the engine request.
1177
1178 =cut
1179
1180 sub prepare_request { my $c = shift; $c->engine->prepare_request( $c, @_ ) }
1181
1182 =item $c->prepare_uploads
1183
1184 Prepare uploads.
1185
1186 =cut
1187
1188 sub prepare_uploads {
1189     my $c = shift;
1190
1191     $c->engine->prepare_uploads( $c, @_ );
1192
1193     if ( $c->debug && keys %{ $c->request->uploads } ) {
1194         my $t = Text::ASCIITable->new;
1195         $t->setCols( 'Key', 'Filename', 'Type', 'Size' );
1196         $t->setColWidth( 'Key',      12, 1 );
1197         $t->setColWidth( 'Filename', 28, 1 );
1198         $t->setColWidth( 'Type',     18, 1 );
1199         $t->setColWidth( 'Size',     9,  1 );
1200         $t->alignCol( 'Size', 'left' );
1201         for my $key ( sort keys %{ $c->request->uploads } ) {
1202             my $upload = $c->request->uploads->{$key};
1203             for my $u ( ref $upload eq 'ARRAY' ? @{$upload} : ($upload) ) {
1204                 $t->addRow( $key, $u->filename, $u->type, $u->size );
1205             }
1206         }
1207         $c->log->debug( "File Uploads are:\n" . $t->draw );
1208     }
1209 }
1210
1211 =item $c->prepare_write
1212
1213 Prepare the output for writing.
1214
1215 =cut
1216
1217 sub prepare_write { my $c = shift; $c->engine->prepare_write( $c, @_ ) }
1218
1219 =item $c->read( [$maxlength] )
1220
1221 Read a chunk of data from the request body.  This method is designed to be
1222 used in a while loop, reading $maxlength bytes on every call.  $maxlength
1223 defaults to the size of the request if not specified.
1224
1225 You have to set MyApp->config->{parse_on_demand} to use this directly.
1226
1227 =cut
1228
1229 sub read { my $c = shift; return $c->engine->read( $c, @_ ) }
1230
1231 =item $c->run
1232
1233 Starts the engine.
1234
1235 =cut
1236
1237 sub run { my $c = shift; return $c->engine->run( $c, @_ ) }
1238
1239 =item $c->set_action( $action, $code, $namespace, $attrs )
1240
1241 Set an action in a given namespace.
1242
1243 =cut
1244
1245 sub set_action { my $c = shift; $c->dispatcher->set_action( $c, @_ ) }
1246
1247 =item $c->setup_actions($component)
1248
1249 Setup actions for a component.
1250
1251 =cut
1252
1253 sub setup_actions { my $c = shift; $c->dispatcher->setup_actions( $c, @_ ) }
1254
1255 =item $c->setup_components
1256
1257 Setup components.
1258
1259 =cut
1260
1261 sub setup_components {
1262     my $class = shift;
1263
1264     my $callback = sub {
1265         my ( $component, $context ) = @_;
1266
1267         unless ( $component->isa('Catalyst::Base') ) {
1268             return $component;
1269         }
1270
1271         my $suffix = Catalyst::Utils::class2classsuffix($component);
1272         my $config = $class->config->{$suffix} || {};
1273
1274         my $instance;
1275
1276         eval { $instance = $component->new( $context, $config ); };
1277
1278         if ( my $error = $@ ) {
1279
1280             chomp $error;
1281
1282             Catalyst::Exception->throw( message =>
1283                   qq/Couldn't instantiate component "$component", "$error"/ );
1284         }
1285
1286         Catalyst::Exception->throw( message =>
1287 qq/Couldn't instantiate component "$component", "new() didn't return a object"/
1288           )
1289           unless ref $instance;
1290         return $instance;
1291     };
1292
1293     eval {
1294         Module::Pluggable::Fast->import(
1295             name   => '_catalyst_components',
1296             search => [
1297                 "$class\::Controller", "$class\::C",
1298                 "$class\::Model",      "$class\::M",
1299                 "$class\::View",       "$class\::V"
1300             ],
1301             callback => $callback
1302         );
1303     };
1304
1305     if ( my $error = $@ ) {
1306
1307         chomp $error;
1308
1309         Catalyst::Exception->throw(
1310             message => qq/Couldn't load components "$error"/ );
1311     }
1312
1313     for my $component ( $class->_catalyst_components($class) ) {
1314         $class->components->{ ref $component || $component } = $component;
1315     }
1316 }
1317
1318 =item $c->setup_dispatcher
1319
1320 =cut
1321
1322 sub setup_dispatcher {
1323     my ( $class, $dispatcher ) = @_;
1324
1325     if ($dispatcher) {
1326         $dispatcher = 'Catalyst::Dispatcher::' . $dispatcher;
1327     }
1328
1329     if ( $ENV{CATALYST_DISPATCHER} ) {
1330         $dispatcher = 'Catalyst::Dispatcher::' . $ENV{CATALYST_DISPATCHER};
1331     }
1332
1333     if ( $ENV{ uc($class) . '_DISPATCHER' } ) {
1334         $dispatcher =
1335           'Catalyst::Dispatcher::' . $ENV{ uc($class) . '_DISPATCHER' };
1336     }
1337
1338     unless ($dispatcher) {
1339         $dispatcher = 'Catalyst::Dispatcher';
1340     }
1341
1342     $dispatcher->require;
1343
1344     if ($@) {
1345         Catalyst::Exception->throw(
1346             message => qq/Couldn't load dispatcher "$dispatcher", "$@"/ );
1347     }
1348
1349     # dispatcher instance
1350     $class->dispatcher( $dispatcher->new );
1351 }
1352
1353 =item $c->setup_engine
1354
1355 =cut
1356
1357 sub setup_engine {
1358     my ( $class, $engine ) = @_;
1359
1360     if ($engine) {
1361         $engine = 'Catalyst::Engine::' . $engine;
1362     }
1363
1364     if ( $ENV{CATALYST_ENGINE} ) {
1365         $engine = 'Catalyst::Engine::' . $ENV{CATALYST_ENGINE};
1366     }
1367
1368     if ( $ENV{ uc($class) . '_ENGINE' } ) {
1369         $engine = 'Catalyst::Engine::' . $ENV{ uc($class) . '_ENGINE' };
1370     }
1371
1372     if ( !$engine && $ENV{MOD_PERL} ) {
1373
1374         # create the apache method
1375         {
1376             no strict 'refs';
1377             *{"$class\::apache"} = sub { shift->engine->apache };
1378         }
1379
1380         my ( $software, $version ) =
1381           $ENV{MOD_PERL} =~ /^(\S+)\/(\d+(?:[\.\_]\d+)+)/;
1382
1383         $version =~ s/_//g;
1384         $version =~ s/(\.[^.]+)\./$1/g;
1385
1386         if ( $software eq 'mod_perl' ) {
1387
1388             if ( $version >= 1.99922 ) {
1389                 $engine = 'Catalyst::Engine::Apache2::MP20';
1390             }
1391
1392             elsif ( $version >= 1.9901 ) {
1393                 $engine = 'Catalyst::Engine::Apache2::MP19';
1394             }
1395
1396             elsif ( $version >= 1.24 ) {
1397                 $engine = 'Catalyst::Engine::Apache::MP13';
1398             }
1399
1400             else {
1401                 Catalyst::Exception->throw( message =>
1402                       qq/Unsupported mod_perl version: $ENV{MOD_PERL}/ );
1403             }
1404
1405             # install the correct mod_perl handler
1406             if ( $version >= 1.9901 ) {
1407                 *handler = sub  : method {
1408                     shift->handle_request(@_);
1409                 };
1410             }
1411             else {
1412                 *handler = sub ($$) { shift->handle_request(@_) };
1413             }
1414
1415         }
1416
1417         elsif ( $software eq 'Zeus-Perl' ) {
1418             $engine = 'Catalyst::Engine::Zeus';
1419         }
1420
1421         else {
1422             Catalyst::Exception->throw(
1423                 message => qq/Unsupported mod_perl: $ENV{MOD_PERL}/ );
1424         }
1425     }
1426
1427     unless ($engine) {
1428         $engine = 'Catalyst::Engine::CGI';
1429     }
1430
1431     $engine->require;
1432
1433     if ($@) {
1434         Catalyst::Exception->throw( message =>
1435 qq/Couldn't load engine "$engine" (maybe you forgot to install it?), "$@"/
1436         );
1437     }
1438
1439     # engine instance
1440     $class->engine( $engine->new );
1441 }
1442
1443 =item $c->setup_home
1444
1445 =cut
1446
1447 sub setup_home {
1448     my ( $class, $home ) = @_;
1449
1450     if ( $ENV{CATALYST_HOME} ) {
1451         $home = $ENV{CATALYST_HOME};
1452     }
1453
1454     if ( $ENV{ uc($class) . '_HOME' } ) {
1455         $home = $ENV{ uc($class) . '_HOME' };
1456     }
1457
1458     unless ($home) {
1459         $home = Catalyst::Utils::home($class);
1460     }
1461
1462     if ($home) {
1463         $class->config->{home} ||= $home;
1464         $class->config->{root} ||= dir($home)->subdir('root');
1465     }
1466 }
1467
1468 =item $c->setup_log
1469
1470 =cut
1471
1472 sub setup_log {
1473     my ( $class, $debug ) = @_;
1474
1475     unless ( $class->log ) {
1476         $class->log( Catalyst::Log->new );
1477     }
1478
1479     if ( $ENV{CATALYST_DEBUG} || $ENV{ uc($class) . '_DEBUG' } || $debug ) {
1480         no strict 'refs';
1481         *{"$class\::debug"} = sub { 1 };
1482         $class->log->debug('Debug messages enabled');
1483     }
1484 }
1485
1486 =item $c->setup_plugins
1487
1488 =cut
1489
1490 sub setup_plugins {
1491     my ( $class, $plugins ) = @_;
1492
1493     $plugins ||= [];
1494     for my $plugin ( reverse @$plugins ) {
1495
1496         $plugin = "Catalyst::Plugin::$plugin";
1497
1498         $plugin->require;
1499
1500         if ($@) {
1501             Catalyst::Exception->throw(
1502                 message => qq/Couldn't load plugin "$plugin", "$@"/ );
1503         }
1504
1505         {
1506             no strict 'refs';
1507             unshift @{"$class\::ISA"}, $plugin;
1508         }
1509     }
1510 }
1511
1512 =item $c->write( $data )
1513
1514 Writes $data to the output stream.  When using this method directly, you will
1515 need to manually set the Content-Length header to the length of your output
1516 data, if known.
1517
1518 =cut
1519
1520 sub write {
1521     my $c = shift;
1522
1523     # Finalize headers if someone manually writes output
1524     $c->finalize_headers;
1525
1526     return $c->engine->write( $c, @_ );
1527 }
1528
1529 =back
1530
1531 =head1 CASE SENSITIVITY
1532
1533 By default Catalyst is not case sensitive, so C<MyApp::C::FOO::Bar> becomes
1534 C</foo/bar>.
1535
1536 But you can activate case sensitivity with a config parameter.
1537
1538     MyApp->config->{case_sensitive} = 1;
1539
1540 So C<MyApp::C::Foo::Bar> becomes C</Foo/Bar>.
1541
1542 =head1 ON-DEMAND PARSER
1543
1544 The request body is usually parsed at the beginning of a request,
1545 but if you want to handle input yourself or speed things up a bit
1546 you can enable on-demand parsing with a config parameter.
1547
1548     MyApp->config->{parse_on_demand} = 1;
1549     
1550 =head1 PROXY SUPPORT
1551
1552 Many production servers operate using the common double-server approach, with
1553 a lightweight frontend web server passing requests to a larger backend
1554 server.  An application running on the backend server must deal with two
1555 problems: the remote user always appears to be '127.0.0.1' and the server's
1556 hostname will appear to be 'localhost' regardless of the virtual host the
1557 user connected through.
1558
1559 Catalyst will automatically detect this situation when you are running both
1560 the frontend and backend servers on the same machine.  The following changes
1561 are made to the request.
1562
1563     $c->req->address is set to the user's real IP address, as read from the
1564     HTTP_X_FORWARDED_FOR header.
1565     
1566     The host value for $c->req->base and $c->req->uri is set to the real host,
1567     as read from the HTTP_X_FORWARDED_HOST header.
1568
1569 Obviously, your web server must support these 2 headers for this to work.
1570
1571 In a more complex server farm environment where you may have your frontend
1572 proxy server(s) on different machines, you will need to set a configuration
1573 option to tell Catalyst to read the proxied data from the headers.
1574
1575     MyApp->config->{using_frontend_proxy} = 1;
1576     
1577 If you do not wish to use the proxy support at all, you may set:
1578
1579     MyApp->config->{ignore_frontend_proxy} = 1;
1580
1581 =head1 THREAD SAFETY
1582
1583 Catalyst has been tested under Apache 2's threading mpm_worker, mpm_winnt,
1584 and the standalone forking HTTP server on Windows.  We believe the Catalyst
1585 core to be thread-safe.
1586
1587 If you plan to operate in a threaded environment, remember that all other
1588 modules you are using must also be thread-safe.  Some modules, most notably
1589 DBD::SQLite, are not thread-safe.
1590
1591 =head1 SUPPORT
1592
1593 IRC:
1594
1595     Join #catalyst on irc.perl.org.
1596
1597 Mailing-Lists:
1598
1599     http://lists.rawmode.org/mailman/listinfo/catalyst
1600     http://lists.rawmode.org/mailman/listinfo/catalyst-dev
1601
1602 Web:
1603
1604     http://catalyst.perl.org
1605
1606 =head1 SEE ALSO
1607
1608 =over 4
1609
1610 =item L<Catalyst::Manual> - The Catalyst Manual
1611
1612 =item L<Catalyst::Engine> - Core Engine
1613
1614 =item L<Catalyst::Log> - The Log Class.
1615
1616 =item L<Catalyst::Request> - The Request Object
1617
1618 =item L<Catalyst::Response> - The Response Object
1619
1620 =item L<Catalyst::Test> - The test suite.
1621
1622 =back
1623
1624 =head1 CREDITS
1625
1626 Andy Grundman
1627
1628 Andy Wardley
1629
1630 Andreas Marienborg
1631
1632 Andrew Bramble
1633
1634 Andrew Ford
1635
1636 Andrew Ruthven
1637
1638 Arthur Bergman
1639
1640 Autrijus Tang
1641
1642 Christian Hansen
1643
1644 Christopher Hicks
1645
1646 Dan Sully
1647
1648 Danijel Milicevic
1649
1650 David Naughton
1651
1652 Gary Ashton Jones
1653
1654 Geoff Richards
1655
1656 Jesse Sheidlower
1657
1658 Jesse Vincent
1659
1660 Jody Belka
1661
1662 Johan Lindstrom
1663
1664 Juan Camacho
1665
1666 Leon Brocard
1667
1668 Marcus Ramberg
1669
1670 Matt S Trout
1671
1672 Robert Sedlacek
1673
1674 Tatsuhiko Miyagawa
1675
1676 Ulf Edvinsson
1677
1678 Yuval Kogman
1679
1680 =head1 AUTHOR
1681
1682 Sebastian Riedel, C<sri@oook.de>
1683
1684 =head1 LICENSE
1685
1686 This library is free software, you can redistribute it and/or modify it under
1687 the same terms as Perl itself.
1688
1689 =cut
1690
1691 1;