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