added docs for engine/dispatcher
[catagits/Catalyst-Runtime.git] / lib / Catalyst.pm
CommitLineData
fc7ec1d9 1package Catalyst;
2
3use strict;
31375184 4use base 'Catalyst::Component';
fbcc39ad 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;
31375184 13use Catalyst::Controller;
8f62c91a 14use File::stat;
5d9a6d47 15use NEXT;
8c113188 16use Text::SimpleTable;
4f6748f1 17use Path::Class;
fbcc39ad 18use Time::HiRes qw/gettimeofday tv_interval/;
19use URI;
6a4a7002 20use Scalar::Util qw/weaken blessed/;
5513038d 21use Tree::Simple qw/use_weak_refs/;
22use Tree::Simple::Visitor::FindByUID;
261c571e 23use attributes;
fc7ec1d9 24
66e28e3f 25__PACKAGE__->mk_accessors(
28591cd7 26 qw/counter request response state action stack namespace/
66e28e3f 27);
10dd6896 28
684d10ed 29attributes->import( __PACKAGE__, \&namespace, 'lvalue' );
261c571e 30
8767c5a3 31sub depth { scalar @{ shift->stack || [] }; }
28591cd7 32
fbcc39ad 33# Laziness++
34*comp = \&component;
35*req = \&request;
36*res = \&response;
37
38# For backwards compatibility
39*finalize_output = \&finalize_body;
40
41# For statistics
42our $COUNT = 1;
43our $START = time;
44our $RECURSION = 1000;
45our $DETACH = "catalyst_detach\n";
46
47require Module::Pluggable::Fast;
48
49# Helper script generation
92ffbc90 50our $CATALYST_SCRIPT_GEN = 27;
fbcc39ad 51
52__PACKAGE__->mk_classdata($_)
3cec521a 53 for qw/components arguments dispatcher engine log dispatcher_class
3643e890 54 engine_class context_class request_class response_class setup_finished/;
cb0354c6 55
3cec521a 56__PACKAGE__->dispatcher_class('Catalyst::Dispatcher');
57__PACKAGE__->engine_class('Catalyst::Engine::CGI');
58__PACKAGE__->request_class('Catalyst::Request');
59__PACKAGE__->response_class('Catalyst::Response');
fbcc39ad 60
97b58e17 61our $VERSION = '5.66';
189e2a51 62
fbcc39ad 63sub import {
64 my ( $class, @arguments ) = @_;
65
66 # We have to limit $class to Catalyst to avoid pushing Catalyst upon every
67 # callers @ISA.
68 return unless $class eq 'Catalyst';
69
70 my $caller = caller(0);
71
72 unless ( $caller->isa('Catalyst') ) {
73 no strict 'refs';
31375184 74 push @{"$caller\::ISA"}, $class, 'Catalyst::Controller';
fbcc39ad 75 }
76
77 $caller->arguments( [@arguments] );
78 $caller->setup_home;
79}
fc7ec1d9 80
81=head1 NAME
82
83Catalyst - The Elegant MVC Web Application Framework
84
85=head1 SYNOPSIS
86
87 # use the helper to start a new application
91864987 88 catalyst.pl MyApp
fc7ec1d9 89
90 # add models, views, controllers
0ef52a96 91 script/myapp_create.pl model Database DBIC dbi:SQLite:/path/to/db
92 script/myapp_create.pl view TT TT
93 script/myapp_create.pl controller Search
fc7ec1d9 94
e7f1cf73 95 # built in testserver -- use -r to restart automatically on changes
ae4e40a7 96 script/myapp_server.pl
fc7ec1d9 97
0ef52a96 98 # command line testing interface
ae4e40a7 99 script/myapp_test.pl /yada
fc7ec1d9 100
0ef52a96 101 ### in MyApp.pm
102 use Catalyst qw/-Debug/; # include plugins here as well
103
104 sub foo : Global { # called for /foo, /foo/1, /foo/1/2, etc.
ae1e6b59 105 my ( $self, $c, @args ) = @_; # args are qw/1 2/ for /foo/1/2
106 $c->stash->{template} = 'foo.tt'; # set the template
0ef52a96 107 # lookup something from db -- stash vars are passed to TT
ae1e6b59 108 $c->stash->{data} =
109 MyApp::Model::Database::Foo->search( { country => $args[0] } );
0ef52a96 110 if ( $c->req->params->{bar} ) { # access GET or POST parameters
111 $c->forward( 'bar' ); # process another action
112 # do something else after forward returns
113 }
114 }
115
ae1e6b59 116 # The foo.tt TT template can use the stash data from the database
0ef52a96 117 [% WHILE (item = data.next) %]
118 [% item.foo %]
119 [% END %]
120
121 # called for /bar/of/soap, /bar/of/soap/10, etc.
122 sub bar : Path('/bar/of/soap') { ... }
fc7ec1d9 123
ae1e6b59 124 # called for all actions, from the top-most controller downwards
0ef52a96 125 sub auto : Private {
126 my ( $self, $c ) = @_;
127 if ( !$c->user ) {
128 $c->res->redirect( '/login' ); # require login
129 return 0; # abort request and go immediately to end()
130 }
ae1e6b59 131 return 1; # success; carry on to next action
0ef52a96 132 }
133
ae1e6b59 134 # called after all actions are finished
0ef52a96 135 sub end : Private {
5a8ed4fe 136 my ( $self, $c ) = @_;
0ef52a96 137 if ( scalar @{ $c->error } ) { ... } # handle errors
138 return if $c->res->body; # already have a response
139 $c->forward( 'MyApp::View::TT' ); # render template
5a8ed4fe 140 }
141
0ef52a96 142 ### in MyApp/Controller/Foo.pm
143 # called for /foo/bar
144 sub bar : Local { ... }
145
5400c668 146 # called for /blargle
147 sub blargle : Global { ... }
148
149 # an index action matches /foo, but not /foo/1, etc.
150 sub index : Private { ... }
0ef52a96 151
152 ### in MyApp/Controller/Foo/Bar.pm
153 # called for /foo/bar/baz
154 sub baz : Local { ... }
155
156 # first MyApp auto is called, then Foo auto, then this
157 sub auto : Private { ... }
158
159 # powerful regular expression paths are also possible
160 sub details : Regex('^product/(\w+)/details$') {
5a8ed4fe 161 my ( $self, $c ) = @_;
0ef52a96 162 # extract the (\w+) from the URI
163 my $product = $c->req->snippets->[0];
5a8ed4fe 164 }
fc7ec1d9 165
0ef52a96 166See L<Catalyst::Manual::Intro> for additional information.
3803e98f 167
fc7ec1d9 168=head1 DESCRIPTION
169
fc7ec1d9 170The key concept of Catalyst is DRY (Don't Repeat Yourself).
171
172See L<Catalyst::Manual> for more documentation.
173
ae1e6b59 174Catalyst plugins can be loaded by naming them as arguments to the "use
175Catalyst" statement. Omit the C<Catalyst::Plugin::> prefix from the
176plugin name, i.e., C<Catalyst::Plugin::My::Module> becomes
177C<My::Module>.
fc7ec1d9 178
0ef52a96 179 use Catalyst qw/My::Module/;
fc7ec1d9 180
836e1134 181If your plugin starts with a name other than C<Catalyst::Plugin::>, you can
182fully qualify the name by using a unary plus:
183
184 use Catalyst qw/
185 My::Module
186 +Fully::Qualified::Plugin::Name
187 /;
188
ae1e6b59 189Special flags like C<-Debug> and C<-Engine> can also be specified as
190arguments when Catalyst is loaded:
fc7ec1d9 191
192 use Catalyst qw/-Debug My::Module/;
193
ae1e6b59 194The position of plugins and flags in the chain is important, because
195they are loaded in exactly the order in which they appear.
fc7ec1d9 196
23f9d934 197The following flags are supported:
198
b5ecfcf0 199=head2 -Debug
23f9d934 200
0ef52a96 201Enables debug output.
fc7ec1d9 202
b5ecfcf0 203=head2 -Engine
fc7ec1d9 204
ae1e6b59 205Forces Catalyst to use a specific engine. Omit the
206C<Catalyst::Engine::> prefix of the engine name, i.e.:
fc7ec1d9 207
0ef52a96 208 use Catalyst qw/-Engine=CGI/;
fc7ec1d9 209
b5ecfcf0 210=head2 -Home
fbcc39ad 211
ae1e6b59 212Forces Catalyst to use a specific home directory, e.g.:
213
214 use Catalyst qw[-Home=/usr/sri];
fbcc39ad 215
b5ecfcf0 216=head2 -Log
fbcc39ad 217
0ef52a96 218Specifies log level.
fbcc39ad 219
23f9d934 220=head1 METHODS
221
0ef52a96 222=head2 Information about the current request
223
b5ecfcf0 224=head2 $c->action
66e28e3f 225
ae1e6b59 226Returns a L<Catalyst::Action> object for the current action, which
227stringifies to the action name. See L<Catalyst::Action>.
0ef52a96 228
b5ecfcf0 229=head2 $c->namespace
0ef52a96 230
ae1e6b59 231Returns the namespace of the current action, i.e., the uri prefix
232corresponding to the controller of the current action. For example:
233
234 # in Controller::Foo::Bar
235 $c->namespace; # returns 'foo/bar';
0ef52a96 236
b5ecfcf0 237=head2 $c->request
0ef52a96 238
b5ecfcf0 239=head2 $c->req
0ef52a96 240
ae1e6b59 241Returns the current L<Catalyst::Request> object. See
242L<Catalyst::Request>.
0ef52a96 243
0ef52a96 244=head2 Processing and response to the current request
245
b5ecfcf0 246=head2 $c->forward( $action [, \@arguments ] )
0ef52a96 247
b5ecfcf0 248=head2 $c->forward( $class, $method, [, \@arguments ] )
0ef52a96 249
ae1e6b59 250Forwards processing to a private action. If you give a class name but no
251method, C<process()> is called. You may also optionally pass arguments
252in an arrayref. The action will receive the arguments in C<@_> and
253C<$c-E<gt>req-E<gt>args>. Upon returning from the function,
254C<$c-E<gt>req-E<gt>args> will be restored to the previous values.
0ef52a96 255
3b984c64 256Any data C<return>ed from the action forwarded to, will be returned by the
d759db1e 257call to forward.
3b984c64 258
259 my $foodata = $c->forward('/foo');
0ef52a96 260 $c->forward('index');
261 $c->forward(qw/MyApp::Model::CDBI::Foo do_stuff/);
262 $c->forward('MyApp::View::TT');
263
264=cut
265
266sub forward { my $c = shift; $c->dispatcher->forward( $c, @_ ) }
267
b5ecfcf0 268=head2 $c->detach( $action [, \@arguments ] )
0ef52a96 269
b5ecfcf0 270=head2 $c->detach( $class, $method, [, \@arguments ] )
0ef52a96 271
272The same as C<forward>, but doesn't return.
273
274=cut
275
276sub detach { my $c = shift; $c->dispatcher->detach( $c, @_ ) }
277
b5ecfcf0 278=head2 $c->error
0ef52a96 279
b5ecfcf0 280=head2 $c->error($error, ...)
0ef52a96 281
b5ecfcf0 282=head2 $c->error($arrayref)
0ef52a96 283
83a8fcac 284Returns an arrayref containing error messages. If Catalyst encounters an
285error while processing a request, it stores the error in $c->error. This
286method should not be used to store non-fatal error messages.
0ef52a96 287
288 my @error = @{ $c->error };
289
290Add a new error.
291
292 $c->error('Something bad happened');
293
83a8fcac 294Clear errors. You probably don't want to clear the errors unless you are
295implementing a custom error screen.
0ef52a96 296
297 $c->error(0);
298
299=cut
300
301sub error {
302 my $c = shift;
303 if ( $_[0] ) {
304 my $error = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];
305 push @{ $c->{error} }, @$error;
306 }
307 elsif ( defined $_[0] ) { $c->{error} = undef }
308 return $c->{error} || [];
309}
310
b5ecfcf0 311=head2 $c->response
0ef52a96 312
b5ecfcf0 313=head2 $c->res
0ef52a96 314
315Returns the current L<Catalyst::Response> object.
316
b5ecfcf0 317=head2 $c->stash
0ef52a96 318
ae1e6b59 319Returns a hashref to the stash, which may be used to store data and pass
320it between components during a request. You can also set hash keys by
321passing arguments. The stash is automatically sent to the view. The
322stash is cleared at the end of a request; it cannot be used for
323persistent storage.
0ef52a96 324
325 $c->stash->{foo} = $bar;
326 $c->stash( { moose => 'majestic', qux => 0 } );
327 $c->stash( bar => 1, gorch => 2 ); # equivalent to passing a hashref
328
329 # stash is automatically passed to the view for use in a template
330 $c->forward( 'MyApp::V::TT' );
331
332=cut
333
334sub stash {
335 my $c = shift;
336 if (@_) {
337 my $stash = @_ > 1 ? {@_} : $_[0];
338 while ( my ( $key, $val ) = each %$stash ) {
339 $c->{stash}->{$key} = $val;
340 }
341 }
342 return $c->{stash};
343}
344
b5ecfcf0 345=head2 $c->state
0ef52a96 346
347Contains the return value of the last executed action.
348
0756fe3b 349=cut
350
351# search via regex
352sub _comp_search {
353 my ($c, @names) = @_;
354
355 foreach my $name (@names) {
356 foreach my $component ( keys %{ $c->components } ) {
357 my $comp = $c->components->{$component} if $component =~ /$name/i;
358 if ($comp) {
359 if ( eval { $comp->can('ACCEPT_CONTEXT'); } ) {
360 return $comp->ACCEPT_CONTEXT($c);
361 }
362 else { return $comp }
363 }
364 }
365 }
366
367 return undef;
368}
369
370# try explicit component names
371sub _comp_explicit {
372 my ($c, @names) = @_;
373
374 foreach my $try (@names) {
375 if ( exists $c->components->{$try} ) {
376 my $comp = $c->components->{$try};
377 if ( eval { $comp->can('ACCEPT_CONTEXT'); } ) {
378 return $comp->ACCEPT_CONTEXT($c);
379 }
380 else { return $comp }
381 }
382 }
383
384 return undef;
385}
386
387# like component, but try just these prefixes before regex searching,
388# and do not try to return "sort keys %{ $c->components }"
389sub _comp_prefixes {
390 my ($c, $name, @prefixes) = @_;
391
392 my $appclass = ref $c || $c;
393
394 my @names = map { "${appclass}::${_}::${name}" } @prefixes;
395
396 my $comp = $c->_comp_explicit(@names);
397 return $comp if defined($comp);
398 $comp = $c->_comp_search($name);
399 return $comp;
400}
401
0ef52a96 402=head2 Component Accessors
403
b5ecfcf0 404=head2 $c->comp($name)
66e28e3f 405
b5ecfcf0 406=head2 $c->component($name)
fbcc39ad 407
ae1e6b59 408Gets a component object by name. This method is no longer recommended,
409unless you want to get a specific component by full
410class. C<$c-E<gt>controller>, C<$c-E<gt>model>, and C<$c-E<gt>view>
411should be used instead.
fbcc39ad 412
413=cut
414
415sub component {
416 my $c = shift;
417
418 if (@_) {
419
420 my $name = shift;
421
422 my $appclass = ref $c || $c;
423
424 my @names = (
425 $name, "${appclass}::${name}",
e3a13771 426 map { "${appclass}::${_}::${name}" }
427 qw/Model M Controller C View V/
fbcc39ad 428 );
429
0756fe3b 430 my $comp = $c->_comp_explicit(@names);
431 return $comp if defined($comp);
fbcc39ad 432
0756fe3b 433 $comp = $c->_comp_search($name);
434 return $comp if defined($comp);
fbcc39ad 435 }
436
437 return sort keys %{ $c->components };
438}
439
b5ecfcf0 440=head2 $c->controller($name)
af3ff00e 441
0ef52a96 442Gets a L<Catalyst::Controller> instance by name.
af3ff00e 443
444 $c->controller('Foo')->do_stuff;
445
446=cut
447
448sub controller {
449 my ( $c, $name ) = @_;
0756fe3b 450 return $c->_comp_prefixes($name, qw/Controller C/);
af3ff00e 451}
452
b5ecfcf0 453=head2 $c->model($name)
fc7ec1d9 454
0ef52a96 455Gets a L<Catalyst::Model> instance by name.
456
457 $c->model('Foo')->do_stuff;
fc7ec1d9 458
459=cut
460
0ef52a96 461sub model {
462 my ( $c, $name ) = @_;
0756fe3b 463 return $c->_comp_prefixes($name, qw/Model M/);
0ef52a96 464}
fc7ec1d9 465
b5ecfcf0 466=head2 $c->view($name)
0ef52a96 467
468Gets a L<Catalyst::View> instance by name.
fc7ec1d9 469
0ef52a96 470 $c->view('Foo')->do_stuff;
fc7ec1d9 471
472=cut
473
0ef52a96 474sub view {
475 my ( $c, $name ) = @_;
0756fe3b 476 return $c->_comp_prefixes($name, qw/View V/);
0ef52a96 477}
fbcc39ad 478
0ef52a96 479=head2 Class data and helper classes
fbcc39ad 480
b5ecfcf0 481=head2 $c->config
fbcc39ad 482
0ef52a96 483Returns or takes a hashref containing the application's configuration.
484
61b1d329 485 __PACKAGE__->config( { db => 'dsn:SQLite:foo.db' } );
81557adf 486
a6ad13b6 487You can also use a L<YAML> config file like myapp.yml in your
488applications home directory.
489
490 ---
491 db: dsn:SQLite:foo.db
492
3643e890 493=cut
494
495sub config {
496 my $c = shift;
497
498 $c->log->warn("Setting config after setup has been run is not a good idea.")
499 if ( @_ and $c->setup_finished );
500
501 $c->NEXT::config(@_);
502}
503
b5ecfcf0 504=head2 $c->debug
0ef52a96 505
506Overload to enable debug messages (same as -Debug option).
fbcc39ad 507
508=cut
509
0ef52a96 510sub debug { 0 }
fbcc39ad 511
b5ecfcf0 512=head2 $c->dispatcher
af3ff00e 513
ae1e6b59 514Returns the dispatcher instance. Stringifies to class name. See
515L<Catalyst::Dispatcher>.
af3ff00e 516
b5ecfcf0 517=head2 $c->engine
0ef52a96 518
ae1e6b59 519Returns the engine instance. Stringifies to the class name. See
520L<Catalyst::Engine>.
0ef52a96 521
b5ecfcf0 522=head2 $c->log
0ef52a96 523
9e7673af 524Returns the logging object instance. Unless it is already set, Catalyst sets
525this up with a L<Catalyst::Log> object. To use your own log class, set the
526logger with the C<< __PACKAGE__->log >> method prior to calling
527C<< __PACKAGE__->setup >>.
528
529 __PACKAGE__->log( MyLogger->new );
530 __PACKAGE__->setup;
531
532And later:
0ef52a96 533
ae1e6b59 534 $c->log->info( 'Now logging with my own logger!' );
0ef52a96 535
ae1e6b59 536Your log class should implement the methods described in the
537L<Catalyst::Log> man page.
af3ff00e 538
539=cut
540
0ef52a96 541=head2 Utility methods
66e28e3f 542
b5ecfcf0 543=head2 $c->path_to(@path)
01033d73 544
ae1e6b59 545Merges C<@path> with C<$c-E<gt>config-E<gt>{home}> and returns a
546L<Path::Class> object.
01033d73 547
548For example:
549
550 $c->path_to( 'db', 'sqlite.db' );
551
552=cut
553
554sub path_to {
555 my ( $c, @path ) = @_;
556 my $path = dir( $c->config->{home}, @path );
557 if ( -d $path ) { return $path }
558 else { return file( $c->config->{home}, @path ) }
559}
560
b5ecfcf0 561=head2 $c->plugin( $name, $class, @args )
0ef52a96 562
ae1e6b59 563Helper method for plugins. It creates a classdata accessor/mutator and
564loads and instantiates the given class.
0ef52a96 565
566 MyApp->plugin( 'prototype', 'HTML::Prototype' );
567
568 $c->prototype->define_javascript_functions;
569
570=cut
571
572sub plugin {
573 my ( $class, $name, $plugin, @args ) = @_;
97b58e17 574 $class->_register_plugin( $plugin, 1 );
0ef52a96 575
576 eval { $plugin->import };
577 $class->mk_classdata($name);
578 my $obj;
579 eval { $obj = $plugin->new(@args) };
580
581 if ($@) {
582 Catalyst::Exception->throw( message =>
583 qq/Couldn't instantiate instant plugin "$plugin", "$@"/ );
584 }
585
586 $class->$name($obj);
587 $class->log->debug(qq/Initialized instant plugin "$plugin" as "$name"/)
588 if $class->debug;
589}
590
b5ecfcf0 591=head2 MyApp->setup
fbcc39ad 592
e7f1cf73 593Initializes the dispatcher and engine, loads any plugins, and loads the
ae1e6b59 594model, view, and controller components. You may also specify an array
595of plugins to load here, if you choose to not load them in the C<use
596Catalyst> line.
fbcc39ad 597
0ef52a96 598 MyApp->setup;
599 MyApp->setup( qw/-Debug/ );
fbcc39ad 600
601=cut
602
603sub setup {
0319a12c 604 my ( $class, @arguments ) = @_;
599b5295 605
fbcc39ad 606 unless ( $class->isa('Catalyst') ) {
953b0e15 607
fbcc39ad 608 Catalyst::Exception->throw(
609 message => qq/'$class' does not inherit from Catalyst/ );
1c99e125 610 }
0319a12c 611
fbcc39ad 612 if ( $class->arguments ) {
613 @arguments = ( @arguments, @{ $class->arguments } );
614 }
615
616 # Process options
617 my $flags = {};
618
619 foreach (@arguments) {
620
621 if (/^-Debug$/) {
622 $flags->{log} =
623 ( $flags->{log} ) ? 'debug,' . $flags->{log} : 'debug';
624 }
625 elsif (/^-(\w+)=?(.*)$/) {
626 $flags->{ lc $1 } = $2;
627 }
628 else {
629 push @{ $flags->{plugins} }, $_;
630 }
631 }
632
99f187d6 633 $class->setup_home( delete $flags->{home} );
634
fbcc39ad 635 $class->setup_log( delete $flags->{log} );
636 $class->setup_plugins( delete $flags->{plugins} );
637 $class->setup_dispatcher( delete $flags->{dispatcher} );
638 $class->setup_engine( delete $flags->{engine} );
fbcc39ad 639
640 for my $flag ( sort keys %{$flags} ) {
641
642 if ( my $code = $class->can( 'setup_' . $flag ) ) {
643 &$code( $class, delete $flags->{$flag} );
644 }
645 else {
646 $class->log->warn(qq/Unknown flag "$flag"/);
647 }
648 }
649
4ff0d824 650 $class->log->warn(
651 <<"EOF") if ( $ENV{CATALYST_SCRIPT_GEN} && ( $ENV{CATALYST_SCRIPT_GEN} < $Catalyst::CATALYST_SCRIPT_GEN ) );
652You are running an old script!
653
34a83d89 654 Please update by running (this will overwrite existing files):
655 catalyst.pl -force -scripts $class
656
657 or (this will not overwrite existing files):
658 catalyst.pl -scripts $class
4ff0d824 659EOF
fbcc39ad 660
661 if ( $class->debug ) {
662
663 my @plugins = ();
664
665 {
666 no strict 'refs';
22247e54 667 @plugins =
668 map { $_ . ' ' . ( $_->VERSION || '' ) }
669 grep { /^Catalyst::Plugin/ } @{"$class\::ISA"};
fbcc39ad 670 }
671
672 if (@plugins) {
8c113188 673 my $t = Text::SimpleTable->new(76);
674 $t->row($_) for @plugins;
fbcc39ad 675 $class->log->debug( "Loaded plugins:\n" . $t->draw );
676 }
677
678 my $dispatcher = $class->dispatcher;
679 my $engine = $class->engine;
680 my $home = $class->config->{home};
681
682 $class->log->debug(qq/Loaded dispatcher "$dispatcher"/);
683 $class->log->debug(qq/Loaded engine "$engine"/);
684
685 $home
686 ? ( -d $home )
687 ? $class->log->debug(qq/Found home "$home"/)
688 : $class->log->debug(qq/Home "$home" doesn't exist/)
689 : $class->log->debug(q/Couldn't find home/);
690 }
691
692 # Call plugins setup
693 {
694 no warnings qw/redefine/;
695 local *setup = sub { };
696 $class->setup;
697 }
698
699 # Initialize our data structure
700 $class->components( {} );
701
702 $class->setup_components;
703
704 if ( $class->debug ) {
9d3e016e 705 my $t = Text::SimpleTable->new( [ 65, 'Class' ], [ 8, 'Type' ] );
684d10ed 706 for my $comp ( sort keys %{ $class->components } ) {
707 my $type = ref $class->components->{$comp} ? 'instance' : 'class';
708 $t->row( $comp, $type );
709 }
fbcc39ad 710 $class->log->debug( "Loaded components:\n" . $t->draw )
8c113188 711 if ( keys %{ $class->components } );
fbcc39ad 712 }
713
714 # Add our self to components, since we are also a component
715 $class->components->{$class} = $class;
716
717 $class->setup_actions;
718
719 if ( $class->debug ) {
720 my $name = $class->config->{name} || 'Application';
721 $class->log->info("$name powered by Catalyst $Catalyst::VERSION");
722 }
723 $class->log->_flush() if $class->log->can('_flush');
3643e890 724
725 $class->setup_finished(1);
fbcc39ad 726}
727
b5ecfcf0 728=head2 $c->uri_for( $path, [ @args ] )
fbcc39ad 729
ae1e6b59 730Merges path with C<$c-E<gt>request-E<gt>base> for absolute uri's and
ccc9f8aa 731with C<$c-E<gt>namespace> for relative uri's, then returns a
ae1e6b59 732normalized L<URI> object. If any args are passed, they are added at the
4f67eddf 733end of the path. If the last argument to uri_for is a hash reference,
734it is assumed to contain GET parameter key/value pairs, which will be
735appended to the URI in standard fashion.
fbcc39ad 736
737=cut
738
739sub uri_for {
00e6a2b7 740 my ( $c, $path, @args ) = @_;
fbcc39ad 741 my $base = $c->request->base->clone;
742 my $basepath = $base->path;
743 $basepath =~ s/\/$//;
fdba7a9d 744 $basepath .= '/';
ccc9f8aa 745 my $namespace = $c->namespace;
00e6a2b7 746
ccc9f8aa 747 # massage namespace, empty if absolute path
748 $namespace =~ s/^\///;
749 $namespace .= '/' if $namespace;
6e0c45c9 750 $path ||= '';
ccc9f8aa 751 $namespace = '' if $path =~ /^\//;
fbcc39ad 752 $path =~ s/^\///;
00e6a2b7 753
97b58e17 754 my $params =
755 ( scalar @args && ref $args[$#args] eq 'HASH' ? pop @args : {} );
8327e2e2 756
189e2a51 757 # join args with '/', or a blank string
00e6a2b7 758 my $args = ( scalar @args ? '/' . join( '/', @args ) : '' );
d3e7a648 759 $args =~ s/^\/// unless $path;
760 my $res =
761 URI->new_abs( URI->new_abs( "$path$args", "$basepath$namespace" ), $base )
762 ->canonical;
8327e2e2 763 $res->query_form(%$params);
d3e7a648 764 $res;
fbcc39ad 765}
766
b5ecfcf0 767=head2 $c->welcome_message
ab2374d3 768
769Returns the Catalyst welcome HTML page.
770
771=cut
772
773sub welcome_message {
bf1f2c60 774 my $c = shift;
775 my $name = $c->config->{name};
776 my $logo = $c->uri_for('/static/images/catalyst_logo.png');
777 my $prefix = Catalyst::Utils::appprefix( ref $c );
80cdbbff 778 $c->response->content_type('text/html; charset=utf-8');
ab2374d3 779 return <<"EOF";
80cdbbff 780<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
781 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
782<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
ab2374d3 783 <head>
80cdbbff 784 <meta http-equiv="Content-Language" content="en" />
785 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
ab2374d3 786 <title>$name on Catalyst $VERSION</title>
787 <style type="text/css">
788 body {
ab2374d3 789 color: #000;
790 background-color: #eee;
791 }
792 div#content {
793 width: 640px;
80cdbbff 794 margin-left: auto;
795 margin-right: auto;
ab2374d3 796 margin-top: 10px;
797 margin-bottom: 10px;
798 text-align: left;
799 background-color: #ccc;
800 border: 1px solid #aaa;
801 -moz-border-radius: 10px;
802 }
d84c4dab 803 p, h1, h2 {
ab2374d3 804 margin-left: 20px;
805 margin-right: 20px;
16215972 806 font-family: verdana, tahoma, sans-serif;
ab2374d3 807 }
d84c4dab 808 a {
809 font-family: verdana, tahoma, sans-serif;
810 }
d114e033 811 :link, :visited {
812 text-decoration: none;
813 color: #b00;
814 border-bottom: 1px dotted #bbb;
815 }
816 :link:hover, :visited:hover {
d114e033 817 color: #555;
818 }
ab2374d3 819 div#topbar {
820 margin: 0px;
821 }
3e82a295 822 pre {
3e82a295 823 margin: 10px;
824 padding: 8px;
825 }
ab2374d3 826 div#answers {
827 padding: 8px;
828 margin: 10px;
d114e033 829 background-color: #fff;
ab2374d3 830 border: 1px solid #aaa;
831 -moz-border-radius: 10px;
832 }
833 h1 {
33108eaf 834 font-size: 0.9em;
835 font-weight: normal;
ab2374d3 836 text-align: center;
837 }
838 h2 {
839 font-size: 1.0em;
840 }
841 p {
842 font-size: 0.9em;
843 }
ae7c5252 844 p img {
845 float: right;
846 margin-left: 10px;
847 }
9619f23c 848 span#appname {
849 font-weight: bold;
33108eaf 850 font-size: 1.6em;
ab2374d3 851 }
852 </style>
853 </head>
854 <body>
855 <div id="content">
856 <div id="topbar">
9619f23c 857 <h1><span id="appname">$name</span> on <a href="http://catalyst.perl.org">Catalyst</a>
d84c4dab 858 $VERSION</h1>
ab2374d3 859 </div>
860 <div id="answers">
ae7c5252 861 <p>
80cdbbff 862 <img src="$logo" alt="Catalyst Logo" />
ae7c5252 863 </p>
4b8cb778 864 <p>Welcome to the wonderful world of Catalyst.
f92fd545 865 This <a href="http://en.wikipedia.org/wiki/MVC">MVC</a>
866 framework will make web development something you had
60dd6e1d 867 never expected it to be: Fun, rewarding, and quick.</p>
ab2374d3 868 <h2>What to do now?</h2>
4b8cb778 869 <p>That really depends on what <b>you</b> want to do.
ab2374d3 870 We do, however, provide you with a few starting points.</p>
871 <p>If you want to jump right into web development with Catalyst
5db7f9a1 872 you might want to check out the documentation.</p>
bf1f2c60 873 <pre><code>perldoc <a href="http://cpansearch.perl.org/dist/Catalyst/lib/Catalyst/Manual/Intro.pod">Catalyst::Manual::Intro</a>
1667d231 874perldoc <a href="http://cpansearch.perl.org/dist/Catalyst/lib/Catalyst/Manual/Tutorial.pod">Catalyst::Manual::Tutorial</a></code>
bf1f2c60 875perldoc <a href="http://cpansearch.perl.org/dist/Catalyst/lib/Catalyst/Manual.pod">Catalyst::Manual</a></code></pre>
ab2374d3 876 <h2>What to do next?</h2>
f5681c92 877 <p>Next it's time to write an actual application. Use the
80cdbbff 878 helper scripts to generate <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AController%3A%3A&amp;mode=all">controllers</a>,
60dd6e1d 879 <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AModel%3A%3A&amp;mode=all">models</a>, and
880 <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AView%3A%3A&amp;mode=all">views</a>;
bf1f2c60 881 they can save you a lot of work.</p>
882 <pre><code>script/${prefix}_create.pl -help</code></pre>
883 <p>Also, be sure to check out the vast and growing
60dd6e1d 884 collection of <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3APlugin%3A%3A&amp;mode=all">plugins for Catalyst on CPAN</a>;
bf1f2c60 885 you are likely to find what you need there.
f5681c92 886 </p>
887
82245cc4 888 <h2>Need help?</h2>
f5681c92 889 <p>Catalyst has a very active community. Here are the main places to
890 get in touch with us.</p>
16215972 891 <ul>
892 <li>
2b9a7d76 893 <a href="http://dev.catalyst.perl.org">Wiki</a>
16215972 894 </li>
895 <li>
896 <a href="http://lists.rawmode.org/mailman/listinfo/catalyst">Mailing-List</a>
897 </li>
898 <li>
ea7cd80d 899 <a href="irc://irc.perl.org/catalyst">IRC channel #catalyst on irc.perl.org</a>
16215972 900 </li>
901 </ul>
ab2374d3 902 <h2>In conclusion</h2>
4e7aa2ea 903 <p>The Catalyst team hopes you will enjoy using Catalyst as much
f5681c92 904 as we enjoyed making it. Please contact us if you have ideas
905 for improvement or other feedback.</p>
ab2374d3 906 </div>
907 </div>
908 </body>
909</html>
910EOF
911}
912
fbcc39ad 913=head1 INTERNAL METHODS
914
ae1e6b59 915These methods are not meant to be used by end users.
916
b5ecfcf0 917=head2 $c->components
fbcc39ad 918
e7f1cf73 919Returns a hash of components.
fbcc39ad 920
b5ecfcf0 921=head2 $c->context_class
1f9cb7c1 922
e7f1cf73 923Returns or sets the context class.
1f9cb7c1 924
b5ecfcf0 925=head2 $c->counter
fbcc39ad 926
ae1e6b59 927Returns a hashref containing coderefs and execution counts (needed for
928deep recursion detection).
fbcc39ad 929
b5ecfcf0 930=head2 $c->depth
fbcc39ad 931
e7f1cf73 932Returns the number of actions on the current internal execution stack.
fbcc39ad 933
b5ecfcf0 934=head2 $c->dispatch
fbcc39ad 935
e7f1cf73 936Dispatches a request to actions.
fbcc39ad 937
938=cut
939
940sub dispatch { my $c = shift; $c->dispatcher->dispatch( $c, @_ ) }
941
b5ecfcf0 942=head2 $c->dispatcher_class
1f9cb7c1 943
e7f1cf73 944Returns or sets the dispatcher class.
1f9cb7c1 945
b5ecfcf0 946=head2 $c->dump_these
7f92deef 947
ae1e6b59 948Returns a list of 2-element array references (name, structure) pairs
949that will be dumped on the error page in debug mode.
7f92deef 950
951=cut
952
953sub dump_these {
954 my $c = shift;
955 [ Request => $c->req ], [ Response => $c->res ], [ Stash => $c->stash ],;
956}
957
b5ecfcf0 958=head2 $c->engine_class
1f9cb7c1 959
e7f1cf73 960Returns or sets the engine class.
1f9cb7c1 961
b5ecfcf0 962=head2 $c->execute( $class, $coderef )
fbcc39ad 963
0ef52a96 964Execute a coderef in given class and catch exceptions. Errors are available
965via $c->error.
fbcc39ad 966
967=cut
968
969sub execute {
970 my ( $c, $class, $code ) = @_;
858828dd 971 $class = $c->component($class) || $class;
fbcc39ad 972 $c->state(0);
a0eca838 973
1627551a 974 if ($c->depth >= $RECURSION) {
975 my $action = "$code";
976 $action = "/$action" unless $action =~ /\-\>/;
977 my $error = qq/Deep recursion detected calling "$action"/;
978 $c->log->error($error);
979 $c->error($error);
980 $c->state(0);
981 return $c->state;
982 }
983
984
fbcc39ad 985 if ( $c->debug ) {
02d2619e 986 my $action = "$code";
fbcc39ad 987 $action = "/$action" unless $action =~ /\-\>/;
988 $c->counter->{"$code"}++;
989
02d2619e 990 # determine if the call was the result of a forward
9d17368e 991 # this is done by walking up the call stack and looking for a calling
992 # sub of Catalyst::forward before the eval
993 my $callsub = q{};
b710ca30 994 for my $index ( 1 .. 10 ) {
995 last
996 if ( ( caller($index) )[0] eq 'Catalyst'
9d17368e 997 && ( caller($index) )[3] eq '(eval)' );
b710ca30 998
999 if ( ( caller($index) )[3] =~ /forward$/ ) {
1000 $callsub = ( caller($index) )[3];
1001 $action = "-> $action";
9d17368e 1002 last;
1003 }
02d2619e 1004 }
5513038d 1005
22247e54 1006 my $node = Tree::Simple->new(
1007 {
1008 action => $action,
1009 elapsed => undef, # to be filled in later
1010 }
1011 );
5513038d 1012 $node->setUID( "$code" . $c->counter->{"$code"} );
22247e54 1013
5513038d 1014 unless ( ( $code->name =~ /^_.*/ )
1015 && ( !$c->config->{show_internal_actions} ) )
22247e54 1016 {
1017
5513038d 1018 # is this a root-level call or a forwarded call?
1019 if ( $callsub =~ /forward$/ ) {
22247e54 1020
5513038d 1021 # forward, locate the caller
1022 if ( my $parent = $c->stack->[-1] ) {
1023 my $visitor = Tree::Simple::Visitor::FindByUID->new;
22247e54 1024 $visitor->searchForUID(
5513038d 1025 "$parent" . $c->counter->{"$parent"} );
22247e54 1026 $c->{stats}->accept($visitor);
5513038d 1027 if ( my $result = $visitor->getResult ) {
22247e54 1028 $result->addChild($node);
5513038d 1029 }
1030 }
1031 else {
22247e54 1032
5513038d 1033 # forward with no caller may come from a plugin
22247e54 1034 $c->{stats}->addChild($node);
5513038d 1035 }
1036 }
1037 else {
22247e54 1038
5513038d 1039 # root-level call
22247e54 1040 $c->{stats}->addChild($node);
5513038d 1041 }
1042 }
fbcc39ad 1043 }
22247e54 1044
8767c5a3 1045 push( @{ $c->stack }, $code );
245ae014 1046 my $elapsed = 0;
1047 my $start = 0;
1048 $start = [gettimeofday] if $c->debug;
1049 eval { $c->state( &$code( $class, $c, @{ $c->req->args } ) || 0 ) };
1050 $elapsed = tv_interval($start) if $c->debug;
1051
1052 if ( $c->debug ) {
1053 unless ( ( $code->name =~ /^_.*/ )
1054 && ( !$c->config->{show_internal_actions} ) )
00e6a2b7 1055 {
22247e54 1056
5513038d 1057 # FindByUID uses an internal die, so we save the existing error
1058 my $error = $@;
22247e54 1059
5513038d 1060 # locate the node in the tree and update the elapsed time
1061 my $visitor = Tree::Simple::Visitor::FindByUID->new;
1062 $visitor->searchForUID( "$code" . $c->counter->{"$code"} );
22247e54 1063 $c->{stats}->accept($visitor);
5513038d 1064 if ( my $result = $visitor->getResult ) {
1065 my $value = $result->getNodeValue;
1066 $value->{elapsed} = sprintf( '%fs', $elapsed );
22247e54 1067 $result->setNodeValue($value);
5513038d 1068 }
22247e54 1069
5513038d 1070 # restore error
1071 $@ = $error || undef;
fbcc39ad 1072 }
245ae014 1073 }
f59def82 1074 my $last = ${ $c->stack }[-1];
8767c5a3 1075 pop( @{ $c->stack } );
fbcc39ad 1076
1077 if ( my $error = $@ ) {
28591cd7 1078 if ( $error eq $DETACH ) { die $DETACH if $c->depth > 1 }
fbcc39ad 1079 else {
1080 unless ( ref $error ) {
1081 chomp $error;
f59def82 1082 my $class = $last->class;
1083 my $name = $last->name;
1084 $error = qq/Caught exception in $class->$name "$error"/;
fbcc39ad 1085 }
fbcc39ad 1086 $c->error($error);
1087 $c->state(0);
1088 }
1089 }
1090 return $c->state;
1091}
1092
b5ecfcf0 1093=head2 $c->finalize
fbcc39ad 1094
e7f1cf73 1095Finalizes the request.
fbcc39ad 1096
1097=cut
1098
1099sub finalize {
1100 my $c = shift;
1101
369c09bc 1102 for my $error ( @{ $c->error } ) {
1103 $c->log->error($error);
1104 }
1105
fbcc39ad 1106 $c->finalize_uploads;
1107
1108 # Error
1109 if ( $#{ $c->error } >= 0 ) {
1110 $c->finalize_error;
1111 }
1112
1113 $c->finalize_headers;
1114
1115 # HEAD request
1116 if ( $c->request->method eq 'HEAD' ) {
1117 $c->response->body('');
1118 }
1119
1120 $c->finalize_body;
1121
1122 return $c->response->status;
1123}
1124
b5ecfcf0 1125=head2 $c->finalize_body
fbcc39ad 1126
e7f1cf73 1127Finalizes body.
fbcc39ad 1128
1129=cut
1130
1131sub finalize_body { my $c = shift; $c->engine->finalize_body( $c, @_ ) }
1132
b5ecfcf0 1133=head2 $c->finalize_cookies
fbcc39ad 1134
e7f1cf73 1135Finalizes cookies.
fbcc39ad 1136
1137=cut
1138
147821ea 1139sub finalize_cookies { my $c = shift; $c->engine->finalize_cookies( $c, @_ ) }
fbcc39ad 1140
b5ecfcf0 1141=head2 $c->finalize_error
fbcc39ad 1142
e7f1cf73 1143Finalizes error.
fbcc39ad 1144
1145=cut
1146
1147sub finalize_error { my $c = shift; $c->engine->finalize_error( $c, @_ ) }
1148
b5ecfcf0 1149=head2 $c->finalize_headers
fbcc39ad 1150
e7f1cf73 1151Finalizes headers.
fbcc39ad 1152
1153=cut
1154
1155sub finalize_headers {
1156 my $c = shift;
1157
1158 # Check if we already finalized headers
1159 return if $c->response->{_finalized_headers};
1160
1161 # Handle redirects
1162 if ( my $location = $c->response->redirect ) {
1163 $c->log->debug(qq/Redirecting to "$location"/) if $c->debug;
1164 $c->response->header( Location => $location );
1165 }
1166
1167 # Content-Length
1168 if ( $c->response->body && !$c->response->content_length ) {
775878ac 1169
8f62c91a 1170 # get the length from a filehandle
6a4a7002 1171 if ( blessed($c->response->body) && $c->response->body->can('read') ) {
8f62c91a 1172 if ( my $stat = stat $c->response->body ) {
1173 $c->response->content_length( $stat->size );
1174 }
1175 else {
775878ac 1176 $c->log->warn('Serving filehandle without a content-length');
8f62c91a 1177 }
1178 }
1179 else {
775878ac 1180 $c->response->content_length( bytes::length( $c->response->body ) );
8f62c91a 1181 }
fbcc39ad 1182 }
1183
1184 # Errors
1185 if ( $c->response->status =~ /^(1\d\d|[23]04)$/ ) {
1186 $c->response->headers->remove_header("Content-Length");
1187 $c->response->body('');
1188 }
1189
1190 $c->finalize_cookies;
1191
1192 $c->engine->finalize_headers( $c, @_ );
1193
1194 # Done
1195 $c->response->{_finalized_headers} = 1;
1196}
1197
b5ecfcf0 1198=head2 $c->finalize_output
fbcc39ad 1199
1200An alias for finalize_body.
1201
b5ecfcf0 1202=head2 $c->finalize_read
fbcc39ad 1203
e7f1cf73 1204Finalizes the input after reading is complete.
fbcc39ad 1205
1206=cut
1207
1208sub finalize_read { my $c = shift; $c->engine->finalize_read( $c, @_ ) }
1209
b5ecfcf0 1210=head2 $c->finalize_uploads
fbcc39ad 1211
ae1e6b59 1212Finalizes uploads. Cleans up any temporary files.
fbcc39ad 1213
1214=cut
1215
1216sub finalize_uploads { my $c = shift; $c->engine->finalize_uploads( $c, @_ ) }
1217
b5ecfcf0 1218=head2 $c->get_action( $action, $namespace )
fbcc39ad 1219
e7f1cf73 1220Gets an action in a given namespace.
fbcc39ad 1221
1222=cut
1223
684d10ed 1224sub get_action { my $c = shift; $c->dispatcher->get_action(@_) }
fbcc39ad 1225
b5ecfcf0 1226=head2 $c->get_actions( $action, $namespace )
a9dc674c 1227
ae1e6b59 1228Gets all actions of a given name in a namespace and all parent
1229namespaces.
a9dc674c 1230
1231=cut
1232
1233sub get_actions { my $c = shift; $c->dispatcher->get_actions( $c, @_ ) }
1234
b5ecfcf0 1235=head2 handle_request( $class, @arguments )
fbcc39ad 1236
e7f1cf73 1237Called to handle each HTTP request.
fbcc39ad 1238
1239=cut
1240
1241sub handle_request {
1242 my ( $class, @arguments ) = @_;
1243
1244 # Always expect worst case!
1245 my $status = -1;
1246 eval {
22247e54 1247 my $stats = ( $class->debug ) ? Tree::Simple->new: q{};
fbcc39ad 1248
1249 my $handler = sub {
1250 my $c = $class->prepare(@arguments);
5513038d 1251 $c->{stats} = $stats;
fbcc39ad 1252 $c->dispatch;
1253 return $c->finalize;
1254 };
1255
1256 if ( $class->debug ) {
245ae014 1257 my $start = [gettimeofday];
1258 $status = &$handler;
1259 my $elapsed = tv_interval $start;
fbcc39ad 1260 $elapsed = sprintf '%f', $elapsed;
1261 my $av = sprintf '%.3f',
1262 ( $elapsed == 0 ? '??' : ( 1 / $elapsed ) );
8c113188 1263 my $t = Text::SimpleTable->new( [ 64, 'Action' ], [ 9, 'Time' ] );
22247e54 1264
1265 $stats->traverse(
1266 sub {
1267 my $action = shift;
1268 my $stat = $action->getNodeValue;
1269 $t->row( ( q{ } x $action->getDepth ) . $stat->{action},
1270 $stat->{elapsed} || '??' );
1271 }
1272 );
1273
fbcc39ad 1274 $class->log->info(
1275 "Request took ${elapsed}s ($av/s)\n" . $t->draw );
1276 }
1277 else { $status = &$handler }
1278
1279 };
1280
1281 if ( my $error = $@ ) {
1282 chomp $error;
1283 $class->log->error(qq/Caught exception in engine "$error"/);
1284 }
1285
1286 $COUNT++;
1287 $class->log->_flush() if $class->log->can('_flush');
1288 return $status;
1289}
1290
b5ecfcf0 1291=head2 $c->prepare( @arguments )
fbcc39ad 1292
ae1e6b59 1293Creates a Catalyst context from an engine-specific request (Apache, CGI,
1294etc.).
fbcc39ad 1295
1296=cut
1297
1298sub prepare {
1299 my ( $class, @arguments ) = @_;
1300
3cec521a 1301 $class->context_class( ref $class || $class ) unless $class->context_class;
1302 my $c = $class->context_class->new(
1303 {
1304 counter => {},
28591cd7 1305 stack => [],
3cec521a 1306 request => $class->request_class->new(
1307 {
1308 arguments => [],
1309 body_parameters => {},
1310 cookies => {},
1311 headers => HTTP::Headers->new,
1312 parameters => {},
1313 query_parameters => {},
1314 secure => 0,
1315 snippets => [],
1316 uploads => {}
1317 }
1318 ),
1319 response => $class->response_class->new(
1320 {
1321 body => '',
1322 cookies => {},
1323 headers => HTTP::Headers->new(),
1324 status => 200
1325 }
1326 ),
1327 stash => {},
1328 state => 0
1329 }
1330 );
fbcc39ad 1331
1332 # For on-demand data
1333 $c->request->{_context} = $c;
1334 $c->response->{_context} = $c;
1335 weaken( $c->request->{_context} );
1336 weaken( $c->response->{_context} );
1337
1338 if ( $c->debug ) {
1339 my $secs = time - $START || 1;
1340 my $av = sprintf '%.3f', $COUNT / $secs;
1341 $c->log->debug('**********************************');
1342 $c->log->debug("* Request $COUNT ($av/s) [$$]");
1343 $c->log->debug('**********************************');
1344 $c->res->headers->header( 'X-Catalyst' => $Catalyst::VERSION );
1345 }
1346
1347 $c->prepare_request(@arguments);
1348 $c->prepare_connection;
1349 $c->prepare_query_parameters;
1350 $c->prepare_headers;
1351 $c->prepare_cookies;
1352 $c->prepare_path;
1353
1354 # On-demand parsing
1355 $c->prepare_body unless $c->config->{parse_on_demand};
1356
fbcc39ad 1357 my $method = $c->req->method || '';
1358 my $path = $c->req->path || '';
1359 my $address = $c->req->address || '';
1360
e3a13771 1361 $c->log->debug(qq/"$method" request for "$path" from "$address"/)
fbcc39ad 1362 if $c->debug;
1363
e3a13771 1364 $c->prepare_action;
1365
fbcc39ad 1366 return $c;
1367}
1368
b5ecfcf0 1369=head2 $c->prepare_action
fbcc39ad 1370
e7f1cf73 1371Prepares action.
fbcc39ad 1372
1373=cut
1374
1375sub prepare_action { my $c = shift; $c->dispatcher->prepare_action( $c, @_ ) }
1376
b5ecfcf0 1377=head2 $c->prepare_body
fbcc39ad 1378
e7f1cf73 1379Prepares message body.
fbcc39ad 1380
1381=cut
1382
1383sub prepare_body {
1384 my $c = shift;
1385
1386 # Do we run for the first time?
1387 return if defined $c->request->{_body};
1388
1389 # Initialize on-demand data
1390 $c->engine->prepare_body( $c, @_ );
1391 $c->prepare_parameters;
1392 $c->prepare_uploads;
1393
1394 if ( $c->debug && keys %{ $c->req->body_parameters } ) {
8c113188 1395 my $t = Text::SimpleTable->new( [ 37, 'Key' ], [ 36, 'Value' ] );
fbcc39ad 1396 for my $key ( sort keys %{ $c->req->body_parameters } ) {
1397 my $param = $c->req->body_parameters->{$key};
1398 my $value = defined($param) ? $param : '';
8c113188 1399 $t->row( $key,
fbcc39ad 1400 ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
1401 }
1402 $c->log->debug( "Body Parameters are:\n" . $t->draw );
1403 }
1404}
1405
b5ecfcf0 1406=head2 $c->prepare_body_chunk( $chunk )
4bd82c41 1407
e7f1cf73 1408Prepares a chunk of data before sending it to L<HTTP::Body>.
4bd82c41 1409
1410=cut
1411
4f5ebacd 1412sub prepare_body_chunk {
1413 my $c = shift;
4bd82c41 1414 $c->engine->prepare_body_chunk( $c, @_ );
1415}
1416
b5ecfcf0 1417=head2 $c->prepare_body_parameters
fbcc39ad 1418
e7f1cf73 1419Prepares body parameters.
fbcc39ad 1420
1421=cut
1422
1423sub prepare_body_parameters {
1424 my $c = shift;
1425 $c->engine->prepare_body_parameters( $c, @_ );
1426}
1427
b5ecfcf0 1428=head2 $c->prepare_connection
fbcc39ad 1429
e7f1cf73 1430Prepares connection.
fbcc39ad 1431
1432=cut
1433
1434sub prepare_connection {
1435 my $c = shift;
1436 $c->engine->prepare_connection( $c, @_ );
1437}
1438
b5ecfcf0 1439=head2 $c->prepare_cookies
fbcc39ad 1440
e7f1cf73 1441Prepares cookies.
fbcc39ad 1442
1443=cut
1444
1445sub prepare_cookies { my $c = shift; $c->engine->prepare_cookies( $c, @_ ) }
1446
b5ecfcf0 1447=head2 $c->prepare_headers
fbcc39ad 1448
e7f1cf73 1449Prepares headers.
fbcc39ad 1450
1451=cut
1452
1453sub prepare_headers { my $c = shift; $c->engine->prepare_headers( $c, @_ ) }
1454
b5ecfcf0 1455=head2 $c->prepare_parameters
fbcc39ad 1456
e7f1cf73 1457Prepares parameters.
fbcc39ad 1458
1459=cut
1460
1461sub prepare_parameters {
1462 my $c = shift;
1463 $c->prepare_body_parameters;
1464 $c->engine->prepare_parameters( $c, @_ );
1465}
1466
b5ecfcf0 1467=head2 $c->prepare_path
fbcc39ad 1468
e7f1cf73 1469Prepares path and base.
fbcc39ad 1470
1471=cut
1472
1473sub prepare_path { my $c = shift; $c->engine->prepare_path( $c, @_ ) }
1474
b5ecfcf0 1475=head2 $c->prepare_query_parameters
fbcc39ad 1476
e7f1cf73 1477Prepares query parameters.
fbcc39ad 1478
1479=cut
1480
1481sub prepare_query_parameters {
1482 my $c = shift;
1483
1484 $c->engine->prepare_query_parameters( $c, @_ );
1485
1486 if ( $c->debug && keys %{ $c->request->query_parameters } ) {
8c113188 1487 my $t = Text::SimpleTable->new( [ 37, 'Key' ], [ 36, 'Value' ] );
fbcc39ad 1488 for my $key ( sort keys %{ $c->req->query_parameters } ) {
1489 my $param = $c->req->query_parameters->{$key};
1490 my $value = defined($param) ? $param : '';
8c113188 1491 $t->row( $key,
fbcc39ad 1492 ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
1493 }
1494 $c->log->debug( "Query Parameters are:\n" . $t->draw );
1495 }
1496}
1497
b5ecfcf0 1498=head2 $c->prepare_read
fbcc39ad 1499
e7f1cf73 1500Prepares the input for reading.
fbcc39ad 1501
1502=cut
1503
1504sub prepare_read { my $c = shift; $c->engine->prepare_read( $c, @_ ) }
1505
b5ecfcf0 1506=head2 $c->prepare_request
fbcc39ad 1507
e7f1cf73 1508Prepares the engine request.
fbcc39ad 1509
1510=cut
1511
1512sub prepare_request { my $c = shift; $c->engine->prepare_request( $c, @_ ) }
1513
b5ecfcf0 1514=head2 $c->prepare_uploads
fbcc39ad 1515
e7f1cf73 1516Prepares uploads.
fbcc39ad 1517
1518=cut
1519
1520sub prepare_uploads {
1521 my $c = shift;
1522
1523 $c->engine->prepare_uploads( $c, @_ );
1524
1525 if ( $c->debug && keys %{ $c->request->uploads } ) {
8c113188 1526 my $t = Text::SimpleTable->new(
1527 [ 12, 'Key' ],
1528 [ 28, 'Filename' ],
1529 [ 18, 'Type' ],
1530 [ 9, 'Size' ]
1531 );
fbcc39ad 1532 for my $key ( sort keys %{ $c->request->uploads } ) {
1533 my $upload = $c->request->uploads->{$key};
1534 for my $u ( ref $upload eq 'ARRAY' ? @{$upload} : ($upload) ) {
8c113188 1535 $t->row( $key, $u->filename, $u->type, $u->size );
fbcc39ad 1536 }
1537 }
1538 $c->log->debug( "File Uploads are:\n" . $t->draw );
1539 }
1540}
1541
b5ecfcf0 1542=head2 $c->prepare_write
fbcc39ad 1543
e7f1cf73 1544Prepares the output for writing.
fbcc39ad 1545
1546=cut
1547
1548sub prepare_write { my $c = shift; $c->engine->prepare_write( $c, @_ ) }
1549
b5ecfcf0 1550=head2 $c->request_class
1f9cb7c1 1551
e7f1cf73 1552Returns or sets the request class.
1f9cb7c1 1553
b5ecfcf0 1554=head2 $c->response_class
1f9cb7c1 1555
e7f1cf73 1556Returns or sets the response class.
1f9cb7c1 1557
b5ecfcf0 1558=head2 $c->read( [$maxlength] )
fbcc39ad 1559
ae1e6b59 1560Reads a chunk of data from the request body. This method is designed to
1561be used in a while loop, reading C<$maxlength> bytes on every call.
1562C<$maxlength> defaults to the size of the request if not specified.
fbcc39ad 1563
ae1e6b59 1564You have to set C<MyApp-E<gt>config-E<gt>{parse_on_demand}> to use this
1565directly.
fbcc39ad 1566
1567=cut
1568
1569sub read { my $c = shift; return $c->engine->read( $c, @_ ) }
1570
b5ecfcf0 1571=head2 $c->run
fbcc39ad 1572
1573Starts the engine.
1574
1575=cut
1576
1577sub run { my $c = shift; return $c->engine->run( $c, @_ ) }
1578
b5ecfcf0 1579=head2 $c->set_action( $action, $code, $namespace, $attrs )
fbcc39ad 1580
e7f1cf73 1581Sets an action in a given namespace.
fbcc39ad 1582
1583=cut
1584
1585sub set_action { my $c = shift; $c->dispatcher->set_action( $c, @_ ) }
1586
b5ecfcf0 1587=head2 $c->setup_actions($component)
fbcc39ad 1588
e7f1cf73 1589Sets up actions for a component.
fbcc39ad 1590
1591=cut
1592
1593sub setup_actions { my $c = shift; $c->dispatcher->setup_actions( $c, @_ ) }
1594
b5ecfcf0 1595=head2 $c->setup_components
fbcc39ad 1596
e7f1cf73 1597Sets up components.
fbcc39ad 1598
1599=cut
1600
1601sub setup_components {
1602 my $class = shift;
1603
1604 my $callback = sub {
1605 my ( $component, $context ) = @_;
1606
31375184 1607 unless ( $component->can('COMPONENT') ) {
fbcc39ad 1608 return $component;
1609 }
1610
76cb6276 1611 my $suffix = Catalyst::Utils::class2classsuffix($component);
fbcc39ad 1612 my $config = $class->config->{$suffix} || {};
1613
1614 my $instance;
1615
22247e54 1616 eval { $instance = $component->COMPONENT( $context, $config ); };
fbcc39ad 1617
1618 if ( my $error = $@ ) {
1619
1620 chomp $error;
1621
1622 Catalyst::Exception->throw( message =>
1623 qq/Couldn't instantiate component "$component", "$error"/ );
1624 }
1625
1626 Catalyst::Exception->throw( message =>
6c5033cb 1627qq/Couldn't instantiate component "$component", "COMPONENT() didn't return a object"/
fbcc39ad 1628 )
1629 unless ref $instance;
1630 return $instance;
1631 };
1632
6f006bd6 1633 eval "package $class;\n" . q!Module::Pluggable::Fast->import(
fbcc39ad 1634 name => '_catalyst_components',
1635 search => [
1636 "$class\::Controller", "$class\::C",
1637 "$class\::Model", "$class\::M",
1638 "$class\::View", "$class\::V"
1639 ],
1640 callback => $callback
1641 );
4289f674 1642 !;
fbcc39ad 1643
1644 if ( my $error = $@ ) {
1645
1646 chomp $error;
1647
1648 Catalyst::Exception->throw(
1649 message => qq/Couldn't load components "$error"/ );
1650 }
1651
1652 for my $component ( $class->_catalyst_components($class) ) {
1653 $class->components->{ ref $component || $component } = $component;
1654 }
1655}
1656
b5ecfcf0 1657=head2 $c->setup_dispatcher
fbcc39ad 1658
ae1e6b59 1659Sets up dispatcher.
1660
fbcc39ad 1661=cut
1662
1663sub setup_dispatcher {
1664 my ( $class, $dispatcher ) = @_;
1665
1666 if ($dispatcher) {
1667 $dispatcher = 'Catalyst::Dispatcher::' . $dispatcher;
1668 }
1669
1670 if ( $ENV{CATALYST_DISPATCHER} ) {
1671 $dispatcher = 'Catalyst::Dispatcher::' . $ENV{CATALYST_DISPATCHER};
1672 }
1673
1674 if ( $ENV{ uc($class) . '_DISPATCHER' } ) {
1675 $dispatcher =
1676 'Catalyst::Dispatcher::' . $ENV{ uc($class) . '_DISPATCHER' };
1677 }
1678
1679 unless ($dispatcher) {
cb0354c6 1680 $dispatcher = $class->dispatcher_class;
fbcc39ad 1681 }
1682
1683 $dispatcher->require;
1684
1685 if ($@) {
1686 Catalyst::Exception->throw(
1687 message => qq/Couldn't load dispatcher "$dispatcher", "$@"/ );
1688 }
1689
1690 # dispatcher instance
1691 $class->dispatcher( $dispatcher->new );
1692}
1693
b5ecfcf0 1694=head2 $c->setup_engine
fbcc39ad 1695
ae1e6b59 1696Sets up engine.
1697
fbcc39ad 1698=cut
1699
1700sub setup_engine {
1701 my ( $class, $engine ) = @_;
1702
1703 if ($engine) {
1704 $engine = 'Catalyst::Engine::' . $engine;
1705 }
1706
1707 if ( $ENV{CATALYST_ENGINE} ) {
1708 $engine = 'Catalyst::Engine::' . $ENV{CATALYST_ENGINE};
1709 }
1710
1711 if ( $ENV{ uc($class) . '_ENGINE' } ) {
1712 $engine = 'Catalyst::Engine::' . $ENV{ uc($class) . '_ENGINE' };
1713 }
1714
9b0a3e0f 1715 if ( $ENV{MOD_PERL} ) {
fbcc39ad 1716
1717 # create the apache method
1718 {
1719 no strict 'refs';
1720 *{"$class\::apache"} = sub { shift->engine->apache };
1721 }
1722
1723 my ( $software, $version ) =
1724 $ENV{MOD_PERL} =~ /^(\S+)\/(\d+(?:[\.\_]\d+)+)/;
1725
1726 $version =~ s/_//g;
1727 $version =~ s/(\.[^.]+)\./$1/g;
1728
1729 if ( $software eq 'mod_perl' ) {
1730
9b0a3e0f 1731 if ( !$engine ) {
22247e54 1732
9b0a3e0f 1733 if ( $version >= 1.99922 ) {
1734 $engine = 'Catalyst::Engine::Apache2::MP20';
1735 }
22247e54 1736
9b0a3e0f 1737 elsif ( $version >= 1.9901 ) {
1738 $engine = 'Catalyst::Engine::Apache2::MP19';
1739 }
22247e54 1740
9b0a3e0f 1741 elsif ( $version >= 1.24 ) {
1742 $engine = 'Catalyst::Engine::Apache::MP13';
1743 }
22247e54 1744
9b0a3e0f 1745 else {
1746 Catalyst::Exception->throw( message =>
1747 qq/Unsupported mod_perl version: $ENV{MOD_PERL}/ );
1748 }
fbcc39ad 1749
fbcc39ad 1750 }
1751
1752 # install the correct mod_perl handler
1753 if ( $version >= 1.9901 ) {
1754 *handler = sub : method {
1755 shift->handle_request(@_);
1756 };
1757 }
1758 else {
1759 *handler = sub ($$) { shift->handle_request(@_) };
1760 }
1761
1762 }
1763
1764 elsif ( $software eq 'Zeus-Perl' ) {
1765 $engine = 'Catalyst::Engine::Zeus';
1766 }
1767
1768 else {
1769 Catalyst::Exception->throw(
1770 message => qq/Unsupported mod_perl: $ENV{MOD_PERL}/ );
1771 }
1772 }
1773
1774 unless ($engine) {
cb0354c6 1775 $engine = $class->engine_class;
fbcc39ad 1776 }
1777
1778 $engine->require;
1779
1780 if ($@) {
1781 Catalyst::Exception->throw( message =>
1782qq/Couldn't load engine "$engine" (maybe you forgot to install it?), "$@"/
1783 );
1784 }
0e7f5826 1785
d54484bf 1786 # check for old engines that are no longer compatible
1787 my $old_engine;
0e7f5826 1788 if ( $engine->isa('Catalyst::Engine::Apache')
1789 && !Catalyst::Engine::Apache->VERSION )
d54484bf 1790 {
1791 $old_engine = 1;
1792 }
0e7f5826 1793
d54484bf 1794 elsif ( $engine->isa('Catalyst::Engine::Server::Base')
0e7f5826 1795 && Catalyst::Engine::Server->VERSION le '0.02' )
d54484bf 1796 {
1797 $old_engine = 1;
1798 }
0e7f5826 1799
1800 elsif ($engine->isa('Catalyst::Engine::HTTP::POE')
1801 && $engine->VERSION eq '0.01' )
d54484bf 1802 {
1803 $old_engine = 1;
1804 }
0e7f5826 1805
1806 elsif ($engine->isa('Catalyst::Engine::Zeus')
1807 && $engine->VERSION eq '0.01' )
d54484bf 1808 {
1809 $old_engine = 1;
1810 }
fbcc39ad 1811
d54484bf 1812 if ($old_engine) {
1813 Catalyst::Exception->throw( message =>
0e7f5826 1814 qq/Engine "$engine" is not supported by this version of Catalyst/
d54484bf 1815 );
1816 }
0e7f5826 1817
fbcc39ad 1818 # engine instance
1819 $class->engine( $engine->new );
1820}
1821
b5ecfcf0 1822=head2 $c->setup_home
fbcc39ad 1823
ae1e6b59 1824Sets up the home directory.
1825
fbcc39ad 1826=cut
1827
1828sub setup_home {
1829 my ( $class, $home ) = @_;
1830
1831 if ( $ENV{CATALYST_HOME} ) {
1832 $home = $ENV{CATALYST_HOME};
1833 }
1834
1835 if ( $ENV{ uc($class) . '_HOME' } ) {
1836 $home = $ENV{ uc($class) . '_HOME' };
1837 }
1838
1839 unless ($home) {
1840 $home = Catalyst::Utils::home($class);
1841 }
1842
1843 if ($home) {
1844 $class->config->{home} ||= $home;
1845 $class->config->{root} ||= dir($home)->subdir('root');
1846 }
1847}
1848
b5ecfcf0 1849=head2 $c->setup_log
fbcc39ad 1850
ae1e6b59 1851Sets up log.
1852
fbcc39ad 1853=cut
1854
1855sub setup_log {
1856 my ( $class, $debug ) = @_;
1857
1858 unless ( $class->log ) {
1859 $class->log( Catalyst::Log->new );
1860 }
af3ff00e 1861
71f074a9 1862 my $app_flag = Catalyst::Utils::class2env($class) . '_DEBUG';
71f074a9 1863
af3ff00e 1864 if (
1865 ( defined( $ENV{CATALYST_DEBUG} ) || defined( $ENV{$app_flag} ) )
1866 ? ( $ENV{CATALYST_DEBUG} || $ENV{$app_flag} )
1867 : $debug
1868 )
1869 {
fbcc39ad 1870 no strict 'refs';
1871 *{"$class\::debug"} = sub { 1 };
1872 $class->log->debug('Debug messages enabled');
1873 }
1874}
1875
b5ecfcf0 1876=head2 $c->setup_plugins
fbcc39ad 1877
ae1e6b59 1878Sets up plugins.
1879
fbcc39ad 1880=cut
1881
836e1134 1882=head2 $c->registered_plugins
1883
1884Returns a sorted list of the plugins which have either been stated in the
1885import list or which have been added via C<< MyApp->plugin(@args); >>.
1886
1887If passed a given plugin name, it will report a boolean value indicating
1888whether or not that plugin is loaded. A fully qualified name is required if
1889the plugin name does not begin with C<Catalyst::Plugin::>.
1890
1891 if ($c->registered_plugins('Some::Plugin')) {
1892 ...
1893 }
1894
1895=cut
fbcc39ad 1896
836e1134 1897{
97b58e17 1898
1899 sub registered_plugins {
836e1134 1900 my $proto = shift;
d0d4d785 1901 return sort keys %{$proto->_plugins} unless @_;
836e1134 1902 my $plugin = shift;
d0d4d785 1903 return 1 if exists $proto->_plugins->{$plugin};
1904 return exists $proto->_plugins->{"Catalyst::Plugin::$plugin"};
836e1134 1905 }
fbcc39ad 1906
836e1134 1907 sub _register_plugin {
1908 my ( $proto, $plugin, $instant ) = @_;
1909 my $class = ref $proto || $proto;
fbcc39ad 1910
1911 $plugin->require;
1912
836e1134 1913 if ( my $error = $@ ) {
1914 my $type = $instant ? "instant " : '';
fbcc39ad 1915 Catalyst::Exception->throw(
836e1134 1916 message => qq/Couldn't load ${type}plugin "$plugin", $error/ );
fbcc39ad 1917 }
1918
d0d4d785 1919 $proto->_plugins->{$plugin} = 1;
836e1134 1920 unless ($instant) {
fbcc39ad 1921 no strict 'refs';
1922 unshift @{"$class\::ISA"}, $plugin;
1923 }
836e1134 1924 return $class;
1925 }
1926
1927 sub setup_plugins {
1928 my ( $class, $plugins ) = @_;
1929
d0d4d785 1930 $class->_plugins( {} ) unless $class->_plugins;
836e1134 1931 $plugins ||= [];
1932 for my $plugin ( reverse @$plugins ) {
1933
1934 unless ( $plugin =~ s/\A\+// ) {
1935 $plugin = "Catalyst::Plugin::$plugin";
1936 }
1937
1938 $class->_register_plugin($plugin);
1939 }
fbcc39ad 1940 }
1941}
1942
b5ecfcf0 1943=head2 $c->stack
8767c5a3 1944
0ef52a96 1945Returns the stack.
8767c5a3 1946
b5ecfcf0 1947=head2 $c->write( $data )
fbcc39ad 1948
ae1e6b59 1949Writes $data to the output stream. When using this method directly, you
1950will need to manually set the C<Content-Length> header to the length of
1951your output data, if known.
fbcc39ad 1952
1953=cut
1954
4f5ebacd 1955sub write {
1956 my $c = shift;
1957
1958 # Finalize headers if someone manually writes output
1959 $c->finalize_headers;
1960
1961 return $c->engine->write( $c, @_ );
1962}
fbcc39ad 1963
b5ecfcf0 1964=head2 version
bf88a181 1965
ae1e6b59 1966Returns the Catalyst version number. Mostly useful for "powered by"
1967messages in template systems.
bf88a181 1968
1969=cut
1970
1971sub version { return $Catalyst::VERSION }
1972
b0bb11ec 1973=head1 INTERNAL ACTIONS
1974
ae1e6b59 1975Catalyst uses internal actions like C<_DISPATCH>, C<_BEGIN>, C<_AUTO>,
1976C<_ACTION>, and C<_END>. These are by default not shown in the private
3e705254 1977action table, but you can make them visible with a config parameter.
b0bb11ec 1978
1979 MyApp->config->{show_internal_actions} = 1;
1980
d2ee9760 1981=head1 CASE SENSITIVITY
1982
3e705254 1983By default Catalyst is not case sensitive, so C<MyApp::C::FOO::Bar> is
ae1e6b59 1984mapped to C</foo/bar>. You can activate case sensitivity with a config
3e705254 1985parameter.
d2ee9760 1986
1987 MyApp->config->{case_sensitive} = 1;
1988
3e705254 1989This causes C<MyApp::C::Foo::Bar> to map to C</Foo/Bar>.
fbcc39ad 1990
1991=head1 ON-DEMAND PARSER
1992
1993The request body is usually parsed at the beginning of a request,
3e705254 1994but if you want to handle input yourself or speed things up a bit,
fbcc39ad 1995you can enable on-demand parsing with a config parameter.
1996
1997 MyApp->config->{parse_on_demand} = 1;
1998
1999=head1 PROXY SUPPORT
2000
ae1e6b59 2001Many production servers operate using the common double-server approach,
2002with a lightweight frontend web server passing requests to a larger
2003backend server. An application running on the backend server must deal
2004with two problems: the remote user always appears to be C<127.0.0.1> and
2005the server's hostname will appear to be C<localhost> regardless of the
2006virtual host that the user connected through.
fbcc39ad 2007
ae1e6b59 2008Catalyst will automatically detect this situation when you are running
2009the frontend and backend servers on the same machine. The following
2010changes are made to the request.
fbcc39ad 2011
ae1e6b59 2012 $c->req->address is set to the user's real IP address, as read from
2013 the HTTP X-Forwarded-For header.
fbcc39ad 2014
ae1e6b59 2015 The host value for $c->req->base and $c->req->uri is set to the real
2016 host, as read from the HTTP X-Forwarded-Host header.
fbcc39ad 2017
3e705254 2018Obviously, your web server must support these headers for this to work.
fbcc39ad 2019
ae1e6b59 2020In a more complex server farm environment where you may have your
2021frontend proxy server(s) on different machines, you will need to set a
2022configuration option to tell Catalyst to read the proxied data from the
2023headers.
fbcc39ad 2024
2025 MyApp->config->{using_frontend_proxy} = 1;
2026
2027If you do not wish to use the proxy support at all, you may set:
d1a31ac6 2028
fbcc39ad 2029 MyApp->config->{ignore_frontend_proxy} = 1;
2030
2031=head1 THREAD SAFETY
2032
2033Catalyst has been tested under Apache 2's threading mpm_worker, mpm_winnt,
3e705254 2034and the standalone forking HTTP server on Windows. We believe the Catalyst
fbcc39ad 2035core to be thread-safe.
2036
2037If you plan to operate in a threaded environment, remember that all other
3e705254 2038modules you are using must also be thread-safe. Some modules, most notably
2039L<DBD::SQLite>, are not thread-safe.
d1a31ac6 2040
3cb1db8c 2041=head1 SUPPORT
2042
2043IRC:
2044
2045 Join #catalyst on irc.perl.org.
2046
3e705254 2047Mailing Lists:
3cb1db8c 2048
2049 http://lists.rawmode.org/mailman/listinfo/catalyst
2050 http://lists.rawmode.org/mailman/listinfo/catalyst-dev
1985c30b 2051
432d507d 2052Web:
2053
2054 http://catalyst.perl.org
2055
0ef52a96 2056Wiki:
2057
2058 http://dev.catalyst.perl.org
2059
fc7ec1d9 2060=head1 SEE ALSO
2061
829a28ca 2062=head2 L<Task::Catalyst> - All you need to start with Catalyst
2063
b5ecfcf0 2064=head2 L<Catalyst::Manual> - The Catalyst Manual
e7f1cf73 2065
b5ecfcf0 2066=head2 L<Catalyst::Component>, L<Catalyst::Base> - Base classes for components
61b1e958 2067
b5ecfcf0 2068=head2 L<Catalyst::Engine> - Core engine
61b1e958 2069
b5ecfcf0 2070=head2 L<Catalyst::Log> - Log class.
61b1e958 2071
b5ecfcf0 2072=head2 L<Catalyst::Request> - Request object
61b1e958 2073
b5ecfcf0 2074=head2 L<Catalyst::Response> - Response object
61b1e958 2075
b5ecfcf0 2076=head2 L<Catalyst::Test> - The test suite.
fc7ec1d9 2077
15f0b5b7 2078=head1 CREDITS
fc7ec1d9 2079
15f0b5b7 2080Andy Grundman
2081
fbcc39ad 2082Andy Wardley
2083
33108eaf 2084Andreas Marienborg
2085
f4a57de4 2086Andrew Bramble
2087
15f0b5b7 2088Andrew Ford
2089
2090Andrew Ruthven
2091
fbcc39ad 2092Arthur Bergman
2093
15f0b5b7 2094Autrijus Tang
2095
0cf56dbc 2096Brian Cassidy
2097
6aaa1c60 2098Carl Franks
2099
15f0b5b7 2100Christian Hansen
2101
2102Christopher Hicks
2103
2104Dan Sully
2105
2106Danijel Milicevic
2107
0ef52a96 2108David Kamholz
2109
15f0b5b7 2110David Naughton
2111
61bef238 2112Drew Taylor
2113
15f0b5b7 2114Gary Ashton Jones
2115
2116Geoff Richards
2117
2118Jesse Sheidlower
2119
fbcc39ad 2120Jesse Vincent
2121
15f0b5b7 2122Jody Belka
2123
2124Johan Lindstrom
2125
2126Juan Camacho
2127
2128Leon Brocard
2129
2130Marcus Ramberg
2131
2132Matt S Trout
2133
71c3bcc3 2134Robert Sedlacek
2135
a727119f 2136Sam Vilain
2137
1cf1c56a 2138Sascha Kiefer
2139
15f0b5b7 2140Tatsuhiko Miyagawa
fc7ec1d9 2141
51f0308d 2142Ulf Edvinsson
2143
bdcb95ef 2144Yuval Kogman
2145
51f0308d 2146=head1 AUTHOR
2147
2148Sebastian Riedel, C<sri@oook.de>
2149
fc7ec1d9 2150=head1 LICENSE
2151
9ce5ab63 2152This library is free software, you can redistribute it and/or modify it under
41ca9ba7 2153the same terms as Perl itself.
fc7ec1d9 2154
2155=cut
2156
21571;