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