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