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