Minor cleanup to welcome message
[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
fbcc39ad 503 print $c->stash->{foo};
504
23eb3f51 505Keys may be set in the stash by assigning to the hash reference, or by passing
506either a single hash reference or a list of key/value pairs as arguments.
507
508For example:
509
510 $c->stash->{foo} ||= 'yada';
511 $c->stash( { moose => 'majestic', qux => 0 } );
512 $c->stash( bar => 1, gorch => 2 );
513
fbcc39ad 514=cut
515
516sub stash {
517 my $c = shift;
518 if (@_) {
519 my $stash = @_ > 1 ? {@_} : $_[0];
520 while ( my ( $key, $val ) = each %$stash ) {
521 $c->{stash}->{$key} = $val;
522 }
523 }
524 return $c->{stash};
525}
526
ab2374d3 527=head1 $c->welcome_message
528
529Returns the Catalyst welcome HTML page.
530
531=cut
532
533sub welcome_message {
534 my $c = shift;
535 my $name = $c->config->{name};
536 return <<"EOF";
537<html>
538 <head>
539 <title>$name on Catalyst $VERSION</title>
540 <style type="text/css">
541 body {
542 text-align: center;
543 padding-left: 50%;
544 color: #000;
545 background-color: #eee;
546 }
547 div#content {
548 width: 640px;
549 margin-left: -320px;
550 margin-top: 10px;
551 margin-bottom: 10px;
552 text-align: left;
553 background-color: #ccc;
554 border: 1px solid #aaa;
555 -moz-border-radius: 10px;
556 }
557 p, h1, h2, a {
558 margin-left: 20px;
559 margin-right: 20px;
16215972 560 font-family: verdana, tahoma, sans-serif;
ab2374d3 561 }
d114e033 562 :link, :visited {
563 text-decoration: none;
564 color: #b00;
565 border-bottom: 1px dotted #bbb;
566 }
567 :link:hover, :visited:hover {
568 background-color: #fff;
569 color: #555;
570 }
ab2374d3 571 div#topbar {
572 margin: 0px;
573 }
3e82a295 574 pre {
575 border: 1px dotted #555;
576 margin: 10px;
577 padding: 8px;
578 }
ab2374d3 579 div#answers {
580 padding: 8px;
581 margin: 10px;
d114e033 582 background-color: #fff;
ab2374d3 583 border: 1px solid #aaa;
584 -moz-border-radius: 10px;
585 }
586 h1 {
587 font-size: 1.2em;
588 text-align: center;
589 }
590 h2 {
591 font-size: 1.0em;
592 }
593 p {
594 font-size: 0.9em;
595 }
596 p.signature {
597 text-align: right;
598 font-style: italic;
599 }
600 </style>
601 </head>
602 <body>
603 <div id="content">
604 <div id="topbar">
605 <h1>$name on Catalyst $VERSION</h1>
606 </div>
607 <div id="answers">
4b8cb778 608 <p>Welcome to the wonderful world of Catalyst.
609 This MVC framework will make web development
ab2374d3 610 something you had never expected it to be:
611 Fun, rewarding and quick.</p>
612 <h2>What to do now?</h2>
4b8cb778 613 <p>That really depends on what <b>you</b> want to do.
ab2374d3 614 We do, however, provide you with a few starting points.</p>
615 <p>If you want to jump right into web development with Catalyst
5db7f9a1 616 you might want to check out the documentation.</p>
617 <pre><code>perldoc<a href="http://cpansearch.perl.org/dist/Catalyst/lib/Catalyst/Manual.pod">Catalyst::Manual</a>
618perldoc<a href="http://cpansearch.perl.org/dist/Catalyst/lib/Catalyst/Manual/Intro.pod">Catalyst::Manual::Intro</a></code></pre>
ab2374d3 619 <p>If you would like some background information on the
4b8cb778 620 MVC-pattern, these links might be of help to you.</p>
ab2374d3 621 <ul>
622 <li>
3e82a295 623 <a href="http://dev.catalyst.perl.org/wiki/Models">
ab2374d3 624 Introduction to Models
625 </a>
626 </li>
627 <li>
3e82a295 628 <a href="http://dev.catalyst.perl.org/wiki/Views">
ab2374d3 629 Introduction to Views
630 </a>
631 </li>
632 <li>
3e82a295 633 <a href="http://dev.catalyst.perl.org/wiki/Controllers">
ab2374d3 634 Introduction to Controllers
635 </a>
636 </li>
637 </ul>
638 <h2>What to do next?</h2>
16215972 639 <p>Next you need to create an actual application. Use the
ab2374d3 640 helper scripts for what they are worth, they can save you
4b8cb778 641 a lot of work getting everything set up. Also, be sure to
642 check out the vast array of plugins for Catalyst on CPAN.
16215972 643 They can handle everything from A to Z
644 , and a whole lot in between.</p>
82245cc4 645 <h2>Need help?</h2>
16215972 646 <p>Catalyst has a very active community. The main places to get
647 in touch are these.</p>
648 <ul>
649 <li>
2b9a7d76 650 <a href="http://dev.catalyst.perl.org">Wiki</a>
16215972 651 </li>
652 <li>
653 <a href="http://lists.rawmode.org/mailman/listinfo/catalyst">Mailing-List</a>
654 </li>
655 <li>
ea7cd80d 656 <a href="irc://irc.perl.org/catalyst">IRC channel #catalyst on irc.perl.org</a>
16215972 657 </li>
658 </ul>
ab2374d3 659 <h2>In conclusion</h2>
ea7cd80d 660 <p>The Catalyst team hope you will enjoy using Catalyst as much
4b8cb778 661 as we enjoyed making it, and rest assured that any and all
ea7cd80d 662 feedback is welcomed.</p>
4b8cb778 663 <p class="signature">-- #1. the first rule of the Cabal is, you do not
664 talk about the Cabal.<br/>
665 #2. the second rule of the Cabal is, you DO NOT
ea7cd80d 666 talk about the Cabal.</p>
ab2374d3 667 </div>
668 </div>
669 </body>
670</html>
671EOF
672}
673
fbcc39ad 674=back
675
676=head1 INTERNAL METHODS
677
678=over 4
679
680=item $c->benchmark($coderef)
681
682Takes a coderef with arguments and returns elapsed time as float.
683
684 my ( $elapsed, $status ) = $c->benchmark( sub { return 1 } );
685 $c->log->info( sprintf "Processing took %f seconds", $elapsed );
686
687=cut
688
689sub benchmark {
690 my $c = shift;
691 my $code = shift;
692 my $time = [gettimeofday];
693 my @return = &$code(@_);
694 my $elapsed = tv_interval $time;
695 return wantarray ? ( $elapsed, @return ) : $elapsed;
696}
697
698=item $c->components
699
700Contains the components.
701
702=item $c->counter
703
704Returns a hashref containing coderefs and execution counts.
705(Needed for deep recursion detection)
706
707=item $c->depth
708
709Returns the actual forward depth.
710
711=item $c->dispatch
712
713Dispatch request to actions.
714
715=cut
716
717sub dispatch { my $c = shift; $c->dispatcher->dispatch( $c, @_ ) }
718
719=item $c->execute($class, $coderef)
720
721Execute a coderef in given class and catch exceptions.
722Errors are available via $c->error.
723
724=cut
725
726sub execute {
727 my ( $c, $class, $code ) = @_;
728 $class = $c->components->{$class} || $class;
729 $c->state(0);
730 my $callsub = ( caller(1) )[3];
731
732 my $action = '';
733 if ( $c->debug ) {
734 $action = "$code";
735 $action = "/$action" unless $action =~ /\-\>/;
736 $c->counter->{"$code"}++;
737
738 if ( $c->counter->{"$code"} > $RECURSION ) {
739 my $error = qq/Deep recursion detected in "$action"/;
740 $c->log->error($error);
741 $c->error($error);
742 $c->state(0);
743 return $c->state;
744 }
745
746 $action = "-> $action" if $callsub =~ /forward$/;
747 }
748 $c->{depth}++;
749 eval {
750 if ( $c->debug )
751 {
752 my ( $elapsed, @state ) =
753 $c->benchmark( $code, $class, $c, @{ $c->req->args } );
754 push @{ $c->{stats} }, [ $action, sprintf( '%fs', $elapsed ) ];
755 $c->state(@state);
756 }
757 else { $c->state( &$code( $class, $c, @{ $c->req->args } ) || 0 ) }
758 };
759 $c->{depth}--;
760
761 if ( my $error = $@ ) {
762
763 if ( $error eq $DETACH ) { die $DETACH if $c->{depth} > 1 }
764 else {
765 unless ( ref $error ) {
766 chomp $error;
767 $error = qq/Caught exception "$error"/;
768 }
769
770 $c->log->error($error);
771 $c->error($error);
772 $c->state(0);
773 }
774 }
775 return $c->state;
776}
777
778=item $c->finalize
779
780Finalize request.
781
782=cut
783
784sub finalize {
785 my $c = shift;
786
787 $c->finalize_uploads;
788
789 # Error
790 if ( $#{ $c->error } >= 0 ) {
791 $c->finalize_error;
792 }
793
794 $c->finalize_headers;
795
796 # HEAD request
797 if ( $c->request->method eq 'HEAD' ) {
798 $c->response->body('');
799 }
800
801 $c->finalize_body;
802
803 return $c->response->status;
804}
805
806=item $c->finalize_body
807
808Finalize body.
809
810=cut
811
812sub finalize_body { my $c = shift; $c->engine->finalize_body( $c, @_ ) }
813
814=item $c->finalize_cookies
815
816Finalize cookies.
817
818=cut
819
820sub finalize_cookies { my $c = shift; $c->engine->finalize_cookies( $c, @_ ) }
821
822=item $c->finalize_error
823
824Finalize error.
825
826=cut
827
828sub finalize_error { my $c = shift; $c->engine->finalize_error( $c, @_ ) }
829
830=item $c->finalize_headers
831
832Finalize headers.
833
834=cut
835
836sub finalize_headers {
837 my $c = shift;
838
839 # Check if we already finalized headers
840 return if $c->response->{_finalized_headers};
841
842 # Handle redirects
843 if ( my $location = $c->response->redirect ) {
844 $c->log->debug(qq/Redirecting to "$location"/) if $c->debug;
845 $c->response->header( Location => $location );
846 }
847
848 # Content-Length
849 if ( $c->response->body && !$c->response->content_length ) {
850 $c->response->content_length( bytes::length( $c->response->body ) );
851 }
852
853 # Errors
854 if ( $c->response->status =~ /^(1\d\d|[23]04)$/ ) {
855 $c->response->headers->remove_header("Content-Length");
856 $c->response->body('');
857 }
858
859 $c->finalize_cookies;
860
861 $c->engine->finalize_headers( $c, @_ );
862
863 # Done
864 $c->response->{_finalized_headers} = 1;
865}
866
867=item $c->finalize_output
868
869An alias for finalize_body.
870
871=item $c->finalize_read
872
873Finalize the input after reading is complete.
874
875=cut
876
877sub finalize_read { my $c = shift; $c->engine->finalize_read( $c, @_ ) }
878
879=item $c->finalize_uploads
880
881Finalize uploads. Cleans up any temporary files.
882
883=cut
884
885sub finalize_uploads { my $c = shift; $c->engine->finalize_uploads( $c, @_ ) }
886
887=item $c->get_action( $action, $namespace, $inherit )
888
889Get an action in a given namespace.
890
891=cut
892
893sub get_action { my $c = shift; $c->dispatcher->get_action( $c, @_ ) }
894
895=item handle_request( $class, @arguments )
896
897Handles the request.
898
899=cut
900
901sub handle_request {
902 my ( $class, @arguments ) = @_;
903
904 # Always expect worst case!
905 my $status = -1;
906 eval {
907 my @stats = ();
908
909 my $handler = sub {
910 my $c = $class->prepare(@arguments);
911 $c->{stats} = \@stats;
912 $c->dispatch;
913 return $c->finalize;
914 };
915
916 if ( $class->debug ) {
917 my $elapsed;
918 ( $elapsed, $status ) = $class->benchmark($handler);
919 $elapsed = sprintf '%f', $elapsed;
920 my $av = sprintf '%.3f',
921 ( $elapsed == 0 ? '??' : ( 1 / $elapsed ) );
922 my $t = Text::ASCIITable->new;
923 $t->setCols( 'Action', 'Time' );
924 $t->setColWidth( 'Action', 64, 1 );
925 $t->setColWidth( 'Time', 9, 1 );
926
927 for my $stat (@stats) { $t->addRow( $stat->[0], $stat->[1] ) }
928 $class->log->info(
929 "Request took ${elapsed}s ($av/s)\n" . $t->draw );
930 }
931 else { $status = &$handler }
932
933 };
934
935 if ( my $error = $@ ) {
936 chomp $error;
937 $class->log->error(qq/Caught exception in engine "$error"/);
938 }
939
940 $COUNT++;
941 $class->log->_flush() if $class->log->can('_flush');
942 return $status;
943}
944
945=item $c->prepare(@arguments)
946
947Turns the engine-specific request( Apache, CGI ... )
948into a Catalyst context .
949
950=cut
951
952sub prepare {
953 my ( $class, @arguments ) = @_;
954
955 my $c = bless {
956 counter => {},
957 depth => 0,
958 request => Catalyst::Request->new(
959 {
960 arguments => [],
961 body_parameters => {},
962 cookies => {},
fbcc39ad 963 headers => HTTP::Headers->new,
964 parameters => {},
965 query_parameters => {},
966 secure => 0,
967 snippets => [],
968 uploads => {}
969 }
970 ),
971 response => Catalyst::Response->new(
972 {
973 body => '',
974 cookies => {},
fbcc39ad 975 headers => HTTP::Headers->new(),
976 status => 200
977 }
978 ),
979 stash => {},
980 state => 0
981 }, $class;
982
983 # For on-demand data
984 $c->request->{_context} = $c;
985 $c->response->{_context} = $c;
986 weaken( $c->request->{_context} );
987 weaken( $c->response->{_context} );
988
989 if ( $c->debug ) {
990 my $secs = time - $START || 1;
991 my $av = sprintf '%.3f', $COUNT / $secs;
992 $c->log->debug('**********************************');
993 $c->log->debug("* Request $COUNT ($av/s) [$$]");
994 $c->log->debug('**********************************');
995 $c->res->headers->header( 'X-Catalyst' => $Catalyst::VERSION );
996 }
997
998 $c->prepare_request(@arguments);
999 $c->prepare_connection;
1000 $c->prepare_query_parameters;
1001 $c->prepare_headers;
1002 $c->prepare_cookies;
1003 $c->prepare_path;
1004
1005 # On-demand parsing
1006 $c->prepare_body unless $c->config->{parse_on_demand};
1007
1008 $c->prepare_action;
1009 my $method = $c->req->method || '';
1010 my $path = $c->req->path || '';
1011 my $address = $c->req->address || '';
1012
1013 $c->log->debug(qq/"$method" request for "$path" from $address/)
1014 if $c->debug;
1015
1016 return $c;
1017}
1018
1019=item $c->prepare_action
1020
1021Prepare action.
1022
1023=cut
1024
1025sub prepare_action { my $c = shift; $c->dispatcher->prepare_action( $c, @_ ) }
1026
1027=item $c->prepare_body
1028
1029Prepare message body.
1030
1031=cut
1032
1033sub prepare_body {
1034 my $c = shift;
1035
1036 # Do we run for the first time?
1037 return if defined $c->request->{_body};
1038
1039 # Initialize on-demand data
1040 $c->engine->prepare_body( $c, @_ );
1041 $c->prepare_parameters;
1042 $c->prepare_uploads;
1043
1044 if ( $c->debug && keys %{ $c->req->body_parameters } ) {
1045 my $t = Text::ASCIITable->new;
1046 $t->setCols( 'Key', 'Value' );
1047 $t->setColWidth( 'Key', 37, 1 );
1048 $t->setColWidth( 'Value', 36, 1 );
1049 $t->alignCol( 'Value', 'right' );
1050 for my $key ( sort keys %{ $c->req->body_parameters } ) {
1051 my $param = $c->req->body_parameters->{$key};
1052 my $value = defined($param) ? $param : '';
1053 $t->addRow( $key,
1054 ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
1055 }
1056 $c->log->debug( "Body Parameters are:\n" . $t->draw );
1057 }
1058}
1059
4bd82c41 1060=item $c->prepare_body_chunk( $chunk )
1061
1062Prepare a chunk of data before sending it to HTTP::Body.
1063
1064=cut
1065
4f5ebacd 1066sub prepare_body_chunk {
1067 my $c = shift;
4bd82c41 1068 $c->engine->prepare_body_chunk( $c, @_ );
1069}
1070
fbcc39ad 1071=item $c->prepare_body_parameters
1072
1073Prepare body parameters.
1074
1075=cut
1076
1077sub prepare_body_parameters {
1078 my $c = shift;
1079 $c->engine->prepare_body_parameters( $c, @_ );
1080}
1081
1082=item $c->prepare_connection
1083
1084Prepare connection.
1085
1086=cut
1087
1088sub prepare_connection {
1089 my $c = shift;
1090 $c->engine->prepare_connection( $c, @_ );
1091}
1092
1093=item $c->prepare_cookies
1094
1095Prepare cookies.
1096
1097=cut
1098
1099sub prepare_cookies { my $c = shift; $c->engine->prepare_cookies( $c, @_ ) }
1100
1101=item $c->prepare_headers
1102
1103Prepare headers.
1104
1105=cut
1106
1107sub prepare_headers { my $c = shift; $c->engine->prepare_headers( $c, @_ ) }
1108
1109=item $c->prepare_parameters
1110
1111Prepare parameters.
1112
1113=cut
1114
1115sub prepare_parameters {
1116 my $c = shift;
1117 $c->prepare_body_parameters;
1118 $c->engine->prepare_parameters( $c, @_ );
1119}
1120
1121=item $c->prepare_path
1122
1123Prepare path and base.
1124
1125=cut
1126
1127sub prepare_path { my $c = shift; $c->engine->prepare_path( $c, @_ ) }
1128
1129=item $c->prepare_query_parameters
1130
1131Prepare query parameters.
1132
1133=cut
1134
1135sub prepare_query_parameters {
1136 my $c = shift;
1137
1138 $c->engine->prepare_query_parameters( $c, @_ );
1139
1140 if ( $c->debug && keys %{ $c->request->query_parameters } ) {
1141 my $t = Text::ASCIITable->new;
1142 $t->setCols( 'Key', 'Value' );
1143 $t->setColWidth( 'Key', 37, 1 );
1144 $t->setColWidth( 'Value', 36, 1 );
1145 $t->alignCol( 'Value', 'right' );
1146 for my $key ( sort keys %{ $c->req->query_parameters } ) {
1147 my $param = $c->req->query_parameters->{$key};
1148 my $value = defined($param) ? $param : '';
1149 $t->addRow( $key,
1150 ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
1151 }
1152 $c->log->debug( "Query Parameters are:\n" . $t->draw );
1153 }
1154}
1155
1156=item $c->prepare_read
1157
1158Prepare the input for reading.
1159
1160=cut
1161
1162sub prepare_read { my $c = shift; $c->engine->prepare_read( $c, @_ ) }
1163
1164=item $c->prepare_request
1165
1166Prepare the engine request.
1167
1168=cut
1169
1170sub prepare_request { my $c = shift; $c->engine->prepare_request( $c, @_ ) }
1171
1172=item $c->prepare_uploads
1173
1174Prepare uploads.
1175
1176=cut
1177
1178sub prepare_uploads {
1179 my $c = shift;
1180
1181 $c->engine->prepare_uploads( $c, @_ );
1182
1183 if ( $c->debug && keys %{ $c->request->uploads } ) {
1184 my $t = Text::ASCIITable->new;
bc2beef5 1185 $t->setCols( 'Key', 'Filename', 'Type', 'Size' );
1186 $t->setColWidth( 'Key', 12, 1 );
1187 $t->setColWidth( 'Filename', 28, 1 );
1188 $t->setColWidth( 'Type', 18, 1 );
fbcc39ad 1189 $t->setColWidth( 'Size', 9, 1 );
1190 $t->alignCol( 'Size', 'left' );
1191 for my $key ( sort keys %{ $c->request->uploads } ) {
1192 my $upload = $c->request->uploads->{$key};
1193 for my $u ( ref $upload eq 'ARRAY' ? @{$upload} : ($upload) ) {
bc2beef5 1194 $t->addRow( $key, $u->filename, $u->type, $u->size );
fbcc39ad 1195 }
1196 }
1197 $c->log->debug( "File Uploads are:\n" . $t->draw );
1198 }
1199}
1200
1201=item $c->prepare_write
1202
1203Prepare the output for writing.
1204
1205=cut
1206
1207sub prepare_write { my $c = shift; $c->engine->prepare_write( $c, @_ ) }
1208
1209=item $c->read( [$maxlength] )
1210
1211Read a chunk of data from the request body. This method is designed to be
1212used in a while loop, reading $maxlength bytes on every call. $maxlength
1213defaults to the size of the request if not specified.
1214
1215You have to set MyApp->config->{parse_on_demand} to use this directly.
1216
1217=cut
1218
1219sub read { my $c = shift; return $c->engine->read( $c, @_ ) }
1220
1221=item $c->run
1222
1223Starts the engine.
1224
1225=cut
1226
1227sub run { my $c = shift; return $c->engine->run( $c, @_ ) }
1228
1229=item $c->set_action( $action, $code, $namespace, $attrs )
1230
1231Set an action in a given namespace.
1232
1233=cut
1234
1235sub set_action { my $c = shift; $c->dispatcher->set_action( $c, @_ ) }
1236
1237=item $c->setup_actions($component)
1238
1239Setup actions for a component.
1240
1241=cut
1242
1243sub setup_actions { my $c = shift; $c->dispatcher->setup_actions( $c, @_ ) }
1244
1245=item $c->setup_components
1246
1247Setup components.
1248
1249=cut
1250
1251sub setup_components {
1252 my $class = shift;
1253
1254 my $callback = sub {
1255 my ( $component, $context ) = @_;
1256
1257 unless ( $component->isa('Catalyst::Base') ) {
1258 return $component;
1259 }
1260
1261 my $suffix = Catalyst::Utils::class2classsuffix($component);
1262 my $config = $class->config->{$suffix} || {};
1263
1264 my $instance;
1265
1266 eval { $instance = $component->new( $context, $config ); };
1267
1268 if ( my $error = $@ ) {
1269
1270 chomp $error;
1271
1272 Catalyst::Exception->throw( message =>
1273 qq/Couldn't instantiate component "$component", "$error"/ );
1274 }
1275
1276 Catalyst::Exception->throw( message =>
1277qq/Couldn't instantiate component "$component", "new() didn't return a object"/
1278 )
1279 unless ref $instance;
1280 return $instance;
1281 };
1282
1283 eval {
1284 Module::Pluggable::Fast->import(
1285 name => '_catalyst_components',
1286 search => [
1287 "$class\::Controller", "$class\::C",
1288 "$class\::Model", "$class\::M",
1289 "$class\::View", "$class\::V"
1290 ],
1291 callback => $callback
1292 );
1293 };
1294
1295 if ( my $error = $@ ) {
1296
1297 chomp $error;
1298
1299 Catalyst::Exception->throw(
1300 message => qq/Couldn't load components "$error"/ );
1301 }
1302
1303 for my $component ( $class->_catalyst_components($class) ) {
1304 $class->components->{ ref $component || $component } = $component;
1305 }
1306}
1307
1308=item $c->setup_dispatcher
1309
1310=cut
1311
1312sub setup_dispatcher {
1313 my ( $class, $dispatcher ) = @_;
1314
1315 if ($dispatcher) {
1316 $dispatcher = 'Catalyst::Dispatcher::' . $dispatcher;
1317 }
1318
1319 if ( $ENV{CATALYST_DISPATCHER} ) {
1320 $dispatcher = 'Catalyst::Dispatcher::' . $ENV{CATALYST_DISPATCHER};
1321 }
1322
1323 if ( $ENV{ uc($class) . '_DISPATCHER' } ) {
1324 $dispatcher =
1325 'Catalyst::Dispatcher::' . $ENV{ uc($class) . '_DISPATCHER' };
1326 }
1327
1328 unless ($dispatcher) {
1329 $dispatcher = 'Catalyst::Dispatcher';
1330 }
1331
1332 $dispatcher->require;
1333
1334 if ($@) {
1335 Catalyst::Exception->throw(
1336 message => qq/Couldn't load dispatcher "$dispatcher", "$@"/ );
1337 }
1338
1339 # dispatcher instance
1340 $class->dispatcher( $dispatcher->new );
1341}
1342
1343=item $c->setup_engine
1344
1345=cut
1346
1347sub setup_engine {
1348 my ( $class, $engine ) = @_;
1349
1350 if ($engine) {
1351 $engine = 'Catalyst::Engine::' . $engine;
1352 }
1353
1354 if ( $ENV{CATALYST_ENGINE} ) {
1355 $engine = 'Catalyst::Engine::' . $ENV{CATALYST_ENGINE};
1356 }
1357
1358 if ( $ENV{ uc($class) . '_ENGINE' } ) {
1359 $engine = 'Catalyst::Engine::' . $ENV{ uc($class) . '_ENGINE' };
1360 }
1361
1362 if ( !$engine && $ENV{MOD_PERL} ) {
1363
1364 # create the apache method
1365 {
1366 no strict 'refs';
1367 *{"$class\::apache"} = sub { shift->engine->apache };
1368 }
1369
1370 my ( $software, $version ) =
1371 $ENV{MOD_PERL} =~ /^(\S+)\/(\d+(?:[\.\_]\d+)+)/;
1372
1373 $version =~ s/_//g;
1374 $version =~ s/(\.[^.]+)\./$1/g;
1375
1376 if ( $software eq 'mod_perl' ) {
1377
1378 if ( $version >= 1.99922 ) {
1379 $engine = 'Catalyst::Engine::Apache2::MP20';
1380 }
1381
1382 elsif ( $version >= 1.9901 ) {
1383 $engine = 'Catalyst::Engine::Apache2::MP19';
1384 }
1385
1386 elsif ( $version >= 1.24 ) {
1387 $engine = 'Catalyst::Engine::Apache::MP13';
1388 }
1389
1390 else {
1391 Catalyst::Exception->throw( message =>
1392 qq/Unsupported mod_perl version: $ENV{MOD_PERL}/ );
1393 }
1394
1395 # install the correct mod_perl handler
1396 if ( $version >= 1.9901 ) {
1397 *handler = sub : method {
1398 shift->handle_request(@_);
1399 };
1400 }
1401 else {
1402 *handler = sub ($$) { shift->handle_request(@_) };
1403 }
1404
1405 }
1406
1407 elsif ( $software eq 'Zeus-Perl' ) {
1408 $engine = 'Catalyst::Engine::Zeus';
1409 }
1410
1411 else {
1412 Catalyst::Exception->throw(
1413 message => qq/Unsupported mod_perl: $ENV{MOD_PERL}/ );
1414 }
1415 }
1416
1417 unless ($engine) {
1418 $engine = 'Catalyst::Engine::CGI';
1419 }
1420
1421 $engine->require;
1422
1423 if ($@) {
1424 Catalyst::Exception->throw( message =>
1425qq/Couldn't load engine "$engine" (maybe you forgot to install it?), "$@"/
1426 );
1427 }
1428
1429 # engine instance
1430 $class->engine( $engine->new );
1431}
1432
1433=item $c->setup_home
1434
1435=cut
1436
1437sub setup_home {
1438 my ( $class, $home ) = @_;
1439
1440 if ( $ENV{CATALYST_HOME} ) {
1441 $home = $ENV{CATALYST_HOME};
1442 }
1443
1444 if ( $ENV{ uc($class) . '_HOME' } ) {
1445 $home = $ENV{ uc($class) . '_HOME' };
1446 }
1447
1448 unless ($home) {
1449 $home = Catalyst::Utils::home($class);
1450 }
1451
1452 if ($home) {
1453 $class->config->{home} ||= $home;
1454 $class->config->{root} ||= dir($home)->subdir('root');
1455 }
1456}
1457
1458=item $c->setup_log
1459
1460=cut
1461
1462sub setup_log {
1463 my ( $class, $debug ) = @_;
1464
1465 unless ( $class->log ) {
1466 $class->log( Catalyst::Log->new );
1467 }
1468
1469 if ( $ENV{CATALYST_DEBUG} || $ENV{ uc($class) . '_DEBUG' } || $debug ) {
1470 no strict 'refs';
1471 *{"$class\::debug"} = sub { 1 };
1472 $class->log->debug('Debug messages enabled');
1473 }
1474}
1475
1476=item $c->setup_plugins
1477
1478=cut
1479
1480sub setup_plugins {
1481 my ( $class, $plugins ) = @_;
1482
1483 $plugins ||= [];
1484 for my $plugin ( reverse @$plugins ) {
1485
1486 $plugin = "Catalyst::Plugin::$plugin";
1487
1488 $plugin->require;
1489
1490 if ($@) {
1491 Catalyst::Exception->throw(
1492 message => qq/Couldn't load plugin "$plugin", "$@"/ );
1493 }
1494
1495 {
1496 no strict 'refs';
1497 unshift @{"$class\::ISA"}, $plugin;
1498 }
1499 }
1500}
1501
1502=item $c->write( $data )
1503
1504Writes $data to the output stream. When using this method directly, you will
1505need to manually set the Content-Length header to the length of your output
1506data, if known.
1507
1508=cut
1509
4f5ebacd 1510sub write {
1511 my $c = shift;
1512
1513 # Finalize headers if someone manually writes output
1514 $c->finalize_headers;
1515
1516 return $c->engine->write( $c, @_ );
1517}
fbcc39ad 1518
23f9d934 1519=back
1520
d2ee9760 1521=head1 CASE SENSITIVITY
1522
1523By default Catalyst is not case sensitive, so C<MyApp::C::FOO::Bar> becomes
1524C</foo/bar>.
1525
1526But you can activate case sensitivity with a config parameter.
1527
1528 MyApp->config->{case_sensitive} = 1;
1529
fbcc39ad 1530So C<MyApp::C::Foo::Bar> becomes C</Foo/Bar>.
1531
1532=head1 ON-DEMAND PARSER
1533
1534The request body is usually parsed at the beginning of a request,
1535but if you want to handle input yourself or speed things up a bit
1536you can enable on-demand parsing with a config parameter.
1537
1538 MyApp->config->{parse_on_demand} = 1;
1539
1540=head1 PROXY SUPPORT
1541
1542Many production servers operate using the common double-server approach, with
1543a lightweight frontend web server passing requests to a larger backend
1544server. An application running on the backend server must deal with two
1545problems: the remote user always appears to be '127.0.0.1' and the server's
1546hostname will appear to be 'localhost' regardless of the virtual host the
1547user connected through.
1548
1549Catalyst will automatically detect this situation when you are running both
1550the frontend and backend servers on the same machine. The following changes
1551are made to the request.
1552
1553 $c->req->address is set to the user's real IP address, as read from the
1554 HTTP_X_FORWARDED_FOR header.
1555
1556 The host value for $c->req->base and $c->req->uri is set to the real host,
1557 as read from the HTTP_X_FORWARDED_HOST header.
1558
1559Obviously, your web server must support these 2 headers for this to work.
1560
1561In a more complex server farm environment where you may have your frontend
1562proxy server(s) on different machines, you will need to set a configuration
1563option to tell Catalyst to read the proxied data from the headers.
1564
1565 MyApp->config->{using_frontend_proxy} = 1;
1566
1567If you do not wish to use the proxy support at all, you may set:
d1a31ac6 1568
fbcc39ad 1569 MyApp->config->{ignore_frontend_proxy} = 1;
1570
1571=head1 THREAD SAFETY
1572
1573Catalyst has been tested under Apache 2's threading mpm_worker, mpm_winnt,
1574and the standalone forking HTTP server on Windows. We believe the Catalyst
1575core to be thread-safe.
1576
1577If you plan to operate in a threaded environment, remember that all other
1578modules you are using must also be thread-safe. Some modules, most notably
1579DBD::SQLite, are not thread-safe.
d1a31ac6 1580
3cb1db8c 1581=head1 SUPPORT
1582
1583IRC:
1584
1585 Join #catalyst on irc.perl.org.
1586
1587Mailing-Lists:
1588
1589 http://lists.rawmode.org/mailman/listinfo/catalyst
1590 http://lists.rawmode.org/mailman/listinfo/catalyst-dev
1985c30b 1591
432d507d 1592Web:
1593
1594 http://catalyst.perl.org
1595
fc7ec1d9 1596=head1 SEE ALSO
1597
61b1e958 1598=over 4
1599
1600=item L<Catalyst::Manual> - The Catalyst Manual
1601
1602=item L<Catalyst::Engine> - Core Engine
1603
1604=item L<Catalyst::Log> - The Log Class.
1605
1606=item L<Catalyst::Request> - The Request Object
1607
1608=item L<Catalyst::Response> - The Response Object
1609
1610=item L<Catalyst::Test> - The test suite.
1611
1612=back
fc7ec1d9 1613
15f0b5b7 1614=head1 CREDITS
fc7ec1d9 1615
15f0b5b7 1616Andy Grundman
1617
fbcc39ad 1618Andy Wardley
1619
15f0b5b7 1620Andrew Ford
1621
1622Andrew Ruthven
1623
fbcc39ad 1624Arthur Bergman
1625
15f0b5b7 1626Autrijus Tang
1627
1628Christian Hansen
1629
1630Christopher Hicks
1631
1632Dan Sully
1633
1634Danijel Milicevic
1635
1636David Naughton
1637
1638Gary Ashton Jones
1639
1640Geoff Richards
1641
1642Jesse Sheidlower
1643
fbcc39ad 1644Jesse Vincent
1645
15f0b5b7 1646Jody Belka
1647
1648Johan Lindstrom
1649
1650Juan Camacho
1651
1652Leon Brocard
1653
1654Marcus Ramberg
1655
1656Matt S Trout
1657
71c3bcc3 1658Robert Sedlacek
1659
15f0b5b7 1660Tatsuhiko Miyagawa
fc7ec1d9 1661
51f0308d 1662Ulf Edvinsson
1663
bdcb95ef 1664Yuval Kogman
1665
51f0308d 1666=head1 AUTHOR
1667
1668Sebastian Riedel, C<sri@oook.de>
1669
fc7ec1d9 1670=head1 LICENSE
1671
1672This library is free software . You can redistribute it and/or modify it under
1673the same terms as perl itself.
1674
1675=cut
1676
16771;