prepared for release.
[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
fbcc39ad 974 if ( $c->debug ) {
02d2619e 975 my $action = "$code";
fbcc39ad 976 $action = "/$action" unless $action =~ /\-\>/;
977 $c->counter->{"$code"}++;
978
979 if ( $c->counter->{"$code"} > $RECURSION ) {
980 my $error = qq/Deep recursion detected in "$action"/;
981 $c->log->error($error);
982 $c->error($error);
983 $c->state(0);
984 return $c->state;
985 }
22247e54 986
02d2619e 987 # determine if the call was the result of a forward
9d17368e 988 # this is done by walking up the call stack and looking for a calling
989 # sub of Catalyst::forward before the eval
990 my $callsub = q{};
b710ca30 991 for my $index ( 1 .. 10 ) {
992 last
993 if ( ( caller($index) )[0] eq 'Catalyst'
9d17368e 994 && ( caller($index) )[3] eq '(eval)' );
b710ca30 995
996 if ( ( caller($index) )[3] =~ /forward$/ ) {
997 $callsub = ( caller($index) )[3];
998 $action = "-> $action";
9d17368e 999 last;
1000 }
02d2619e 1001 }
5513038d 1002
22247e54 1003 my $node = Tree::Simple->new(
1004 {
1005 action => $action,
1006 elapsed => undef, # to be filled in later
1007 }
1008 );
5513038d 1009 $node->setUID( "$code" . $c->counter->{"$code"} );
22247e54 1010
5513038d 1011 unless ( ( $code->name =~ /^_.*/ )
1012 && ( !$c->config->{show_internal_actions} ) )
22247e54 1013 {
1014
5513038d 1015 # is this a root-level call or a forwarded call?
1016 if ( $callsub =~ /forward$/ ) {
22247e54 1017
5513038d 1018 # forward, locate the caller
1019 if ( my $parent = $c->stack->[-1] ) {
1020 my $visitor = Tree::Simple::Visitor::FindByUID->new;
22247e54 1021 $visitor->searchForUID(
5513038d 1022 "$parent" . $c->counter->{"$parent"} );
22247e54 1023 $c->{stats}->accept($visitor);
5513038d 1024 if ( my $result = $visitor->getResult ) {
22247e54 1025 $result->addChild($node);
5513038d 1026 }
1027 }
1028 else {
22247e54 1029
5513038d 1030 # forward with no caller may come from a plugin
22247e54 1031 $c->{stats}->addChild($node);
5513038d 1032 }
1033 }
1034 else {
22247e54 1035
5513038d 1036 # root-level call
22247e54 1037 $c->{stats}->addChild($node);
5513038d 1038 }
1039 }
fbcc39ad 1040 }
22247e54 1041
8767c5a3 1042 push( @{ $c->stack }, $code );
245ae014 1043 my $elapsed = 0;
1044 my $start = 0;
1045 $start = [gettimeofday] if $c->debug;
1046 eval { $c->state( &$code( $class, $c, @{ $c->req->args } ) || 0 ) };
1047 $elapsed = tv_interval($start) if $c->debug;
1048
1049 if ( $c->debug ) {
1050 unless ( ( $code->name =~ /^_.*/ )
1051 && ( !$c->config->{show_internal_actions} ) )
00e6a2b7 1052 {
22247e54 1053
5513038d 1054 # FindByUID uses an internal die, so we save the existing error
1055 my $error = $@;
22247e54 1056
5513038d 1057 # locate the node in the tree and update the elapsed time
1058 my $visitor = Tree::Simple::Visitor::FindByUID->new;
1059 $visitor->searchForUID( "$code" . $c->counter->{"$code"} );
22247e54 1060 $c->{stats}->accept($visitor);
5513038d 1061 if ( my $result = $visitor->getResult ) {
1062 my $value = $result->getNodeValue;
1063 $value->{elapsed} = sprintf( '%fs', $elapsed );
22247e54 1064 $result->setNodeValue($value);
5513038d 1065 }
22247e54 1066
5513038d 1067 # restore error
1068 $@ = $error || undef;
fbcc39ad 1069 }
245ae014 1070 }
f59def82 1071 my $last = ${ $c->stack }[-1];
8767c5a3 1072 pop( @{ $c->stack } );
fbcc39ad 1073
1074 if ( my $error = $@ ) {
28591cd7 1075 if ( $error eq $DETACH ) { die $DETACH if $c->depth > 1 }
fbcc39ad 1076 else {
1077 unless ( ref $error ) {
1078 chomp $error;
f59def82 1079 my $class = $last->class;
1080 my $name = $last->name;
1081 $error = qq/Caught exception in $class->$name "$error"/;
fbcc39ad 1082 }
fbcc39ad 1083 $c->error($error);
1084 $c->state(0);
1085 }
1086 }
1087 return $c->state;
1088}
1089
b5ecfcf0 1090=head2 $c->finalize
fbcc39ad 1091
e7f1cf73 1092Finalizes the request.
fbcc39ad 1093
1094=cut
1095
1096sub finalize {
1097 my $c = shift;
1098
369c09bc 1099 for my $error ( @{ $c->error } ) {
1100 $c->log->error($error);
1101 }
1102
fbcc39ad 1103 $c->finalize_uploads;
1104
1105 # Error
1106 if ( $#{ $c->error } >= 0 ) {
1107 $c->finalize_error;
1108 }
1109
1110 $c->finalize_headers;
1111
1112 # HEAD request
1113 if ( $c->request->method eq 'HEAD' ) {
1114 $c->response->body('');
1115 }
1116
1117 $c->finalize_body;
1118
1119 return $c->response->status;
1120}
1121
b5ecfcf0 1122=head2 $c->finalize_body
fbcc39ad 1123
e7f1cf73 1124Finalizes body.
fbcc39ad 1125
1126=cut
1127
1128sub finalize_body { my $c = shift; $c->engine->finalize_body( $c, @_ ) }
1129
b5ecfcf0 1130=head2 $c->finalize_cookies
fbcc39ad 1131
e7f1cf73 1132Finalizes cookies.
fbcc39ad 1133
1134=cut
1135
147821ea 1136sub finalize_cookies { my $c = shift; $c->engine->finalize_cookies( $c, @_ ) }
fbcc39ad 1137
b5ecfcf0 1138=head2 $c->finalize_error
fbcc39ad 1139
e7f1cf73 1140Finalizes error.
fbcc39ad 1141
1142=cut
1143
1144sub finalize_error { my $c = shift; $c->engine->finalize_error( $c, @_ ) }
1145
b5ecfcf0 1146=head2 $c->finalize_headers
fbcc39ad 1147
e7f1cf73 1148Finalizes headers.
fbcc39ad 1149
1150=cut
1151
1152sub finalize_headers {
1153 my $c = shift;
1154
1155 # Check if we already finalized headers
1156 return if $c->response->{_finalized_headers};
1157
1158 # Handle redirects
1159 if ( my $location = $c->response->redirect ) {
1160 $c->log->debug(qq/Redirecting to "$location"/) if $c->debug;
1161 $c->response->header( Location => $location );
1162 }
1163
1164 # Content-Length
1165 if ( $c->response->body && !$c->response->content_length ) {
775878ac 1166
8f62c91a 1167 # get the length from a filehandle
6a4a7002 1168 if ( blessed($c->response->body) && $c->response->body->can('read') ) {
8f62c91a 1169 if ( my $stat = stat $c->response->body ) {
1170 $c->response->content_length( $stat->size );
1171 }
1172 else {
775878ac 1173 $c->log->warn('Serving filehandle without a content-length');
8f62c91a 1174 }
1175 }
1176 else {
775878ac 1177 $c->response->content_length( bytes::length( $c->response->body ) );
8f62c91a 1178 }
fbcc39ad 1179 }
1180
1181 # Errors
1182 if ( $c->response->status =~ /^(1\d\d|[23]04)$/ ) {
1183 $c->response->headers->remove_header("Content-Length");
1184 $c->response->body('');
1185 }
1186
1187 $c->finalize_cookies;
1188
1189 $c->engine->finalize_headers( $c, @_ );
1190
1191 # Done
1192 $c->response->{_finalized_headers} = 1;
1193}
1194
b5ecfcf0 1195=head2 $c->finalize_output
fbcc39ad 1196
1197An alias for finalize_body.
1198
b5ecfcf0 1199=head2 $c->finalize_read
fbcc39ad 1200
e7f1cf73 1201Finalizes the input after reading is complete.
fbcc39ad 1202
1203=cut
1204
1205sub finalize_read { my $c = shift; $c->engine->finalize_read( $c, @_ ) }
1206
b5ecfcf0 1207=head2 $c->finalize_uploads
fbcc39ad 1208
ae1e6b59 1209Finalizes uploads. Cleans up any temporary files.
fbcc39ad 1210
1211=cut
1212
1213sub finalize_uploads { my $c = shift; $c->engine->finalize_uploads( $c, @_ ) }
1214
b5ecfcf0 1215=head2 $c->get_action( $action, $namespace )
fbcc39ad 1216
e7f1cf73 1217Gets an action in a given namespace.
fbcc39ad 1218
1219=cut
1220
684d10ed 1221sub get_action { my $c = shift; $c->dispatcher->get_action(@_) }
fbcc39ad 1222
b5ecfcf0 1223=head2 $c->get_actions( $action, $namespace )
a9dc674c 1224
ae1e6b59 1225Gets all actions of a given name in a namespace and all parent
1226namespaces.
a9dc674c 1227
1228=cut
1229
1230sub get_actions { my $c = shift; $c->dispatcher->get_actions( $c, @_ ) }
1231
b5ecfcf0 1232=head2 handle_request( $class, @arguments )
fbcc39ad 1233
e7f1cf73 1234Called to handle each HTTP request.
fbcc39ad 1235
1236=cut
1237
1238sub handle_request {
1239 my ( $class, @arguments ) = @_;
1240
1241 # Always expect worst case!
1242 my $status = -1;
1243 eval {
22247e54 1244 my $stats = ( $class->debug ) ? Tree::Simple->new: q{};
fbcc39ad 1245
1246 my $handler = sub {
1247 my $c = $class->prepare(@arguments);
5513038d 1248 $c->{stats} = $stats;
fbcc39ad 1249 $c->dispatch;
1250 return $c->finalize;
1251 };
1252
1253 if ( $class->debug ) {
245ae014 1254 my $start = [gettimeofday];
1255 $status = &$handler;
1256 my $elapsed = tv_interval $start;
fbcc39ad 1257 $elapsed = sprintf '%f', $elapsed;
1258 my $av = sprintf '%.3f',
1259 ( $elapsed == 0 ? '??' : ( 1 / $elapsed ) );
8c113188 1260 my $t = Text::SimpleTable->new( [ 64, 'Action' ], [ 9, 'Time' ] );
22247e54 1261
1262 $stats->traverse(
1263 sub {
1264 my $action = shift;
1265 my $stat = $action->getNodeValue;
1266 $t->row( ( q{ } x $action->getDepth ) . $stat->{action},
1267 $stat->{elapsed} || '??' );
1268 }
1269 );
1270
fbcc39ad 1271 $class->log->info(
1272 "Request took ${elapsed}s ($av/s)\n" . $t->draw );
1273 }
1274 else { $status = &$handler }
1275
1276 };
1277
1278 if ( my $error = $@ ) {
1279 chomp $error;
1280 $class->log->error(qq/Caught exception in engine "$error"/);
1281 }
1282
1283 $COUNT++;
1284 $class->log->_flush() if $class->log->can('_flush');
1285 return $status;
1286}
1287
b5ecfcf0 1288=head2 $c->prepare( @arguments )
fbcc39ad 1289
ae1e6b59 1290Creates a Catalyst context from an engine-specific request (Apache, CGI,
1291etc.).
fbcc39ad 1292
1293=cut
1294
1295sub prepare {
1296 my ( $class, @arguments ) = @_;
1297
3cec521a 1298 $class->context_class( ref $class || $class ) unless $class->context_class;
1299 my $c = $class->context_class->new(
1300 {
1301 counter => {},
28591cd7 1302 stack => [],
3cec521a 1303 request => $class->request_class->new(
1304 {
1305 arguments => [],
1306 body_parameters => {},
1307 cookies => {},
1308 headers => HTTP::Headers->new,
1309 parameters => {},
1310 query_parameters => {},
1311 secure => 0,
1312 snippets => [],
1313 uploads => {}
1314 }
1315 ),
1316 response => $class->response_class->new(
1317 {
1318 body => '',
1319 cookies => {},
1320 headers => HTTP::Headers->new(),
1321 status => 200
1322 }
1323 ),
1324 stash => {},
1325 state => 0
1326 }
1327 );
fbcc39ad 1328
1329 # For on-demand data
1330 $c->request->{_context} = $c;
1331 $c->response->{_context} = $c;
1332 weaken( $c->request->{_context} );
1333 weaken( $c->response->{_context} );
1334
1335 if ( $c->debug ) {
1336 my $secs = time - $START || 1;
1337 my $av = sprintf '%.3f', $COUNT / $secs;
1338 $c->log->debug('**********************************');
1339 $c->log->debug("* Request $COUNT ($av/s) [$$]");
1340 $c->log->debug('**********************************');
1341 $c->res->headers->header( 'X-Catalyst' => $Catalyst::VERSION );
1342 }
1343
1344 $c->prepare_request(@arguments);
1345 $c->prepare_connection;
1346 $c->prepare_query_parameters;
1347 $c->prepare_headers;
1348 $c->prepare_cookies;
1349 $c->prepare_path;
1350
1351 # On-demand parsing
1352 $c->prepare_body unless $c->config->{parse_on_demand};
1353
fbcc39ad 1354 my $method = $c->req->method || '';
1355 my $path = $c->req->path || '';
1356 my $address = $c->req->address || '';
1357
e3a13771 1358 $c->log->debug(qq/"$method" request for "$path" from "$address"/)
fbcc39ad 1359 if $c->debug;
1360
e3a13771 1361 $c->prepare_action;
1362
fbcc39ad 1363 return $c;
1364}
1365
b5ecfcf0 1366=head2 $c->prepare_action
fbcc39ad 1367
e7f1cf73 1368Prepares action.
fbcc39ad 1369
1370=cut
1371
1372sub prepare_action { my $c = shift; $c->dispatcher->prepare_action( $c, @_ ) }
1373
b5ecfcf0 1374=head2 $c->prepare_body
fbcc39ad 1375
e7f1cf73 1376Prepares message body.
fbcc39ad 1377
1378=cut
1379
1380sub prepare_body {
1381 my $c = shift;
1382
1383 # Do we run for the first time?
1384 return if defined $c->request->{_body};
1385
1386 # Initialize on-demand data
1387 $c->engine->prepare_body( $c, @_ );
1388 $c->prepare_parameters;
1389 $c->prepare_uploads;
1390
1391 if ( $c->debug && keys %{ $c->req->body_parameters } ) {
8c113188 1392 my $t = Text::SimpleTable->new( [ 37, 'Key' ], [ 36, 'Value' ] );
fbcc39ad 1393 for my $key ( sort keys %{ $c->req->body_parameters } ) {
1394 my $param = $c->req->body_parameters->{$key};
1395 my $value = defined($param) ? $param : '';
8c113188 1396 $t->row( $key,
fbcc39ad 1397 ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
1398 }
1399 $c->log->debug( "Body Parameters are:\n" . $t->draw );
1400 }
1401}
1402
b5ecfcf0 1403=head2 $c->prepare_body_chunk( $chunk )
4bd82c41 1404
e7f1cf73 1405Prepares a chunk of data before sending it to L<HTTP::Body>.
4bd82c41 1406
1407=cut
1408
4f5ebacd 1409sub prepare_body_chunk {
1410 my $c = shift;
4bd82c41 1411 $c->engine->prepare_body_chunk( $c, @_ );
1412}
1413
b5ecfcf0 1414=head2 $c->prepare_body_parameters
fbcc39ad 1415
e7f1cf73 1416Prepares body parameters.
fbcc39ad 1417
1418=cut
1419
1420sub prepare_body_parameters {
1421 my $c = shift;
1422 $c->engine->prepare_body_parameters( $c, @_ );
1423}
1424
b5ecfcf0 1425=head2 $c->prepare_connection
fbcc39ad 1426
e7f1cf73 1427Prepares connection.
fbcc39ad 1428
1429=cut
1430
1431sub prepare_connection {
1432 my $c = shift;
1433 $c->engine->prepare_connection( $c, @_ );
1434}
1435
b5ecfcf0 1436=head2 $c->prepare_cookies
fbcc39ad 1437
e7f1cf73 1438Prepares cookies.
fbcc39ad 1439
1440=cut
1441
1442sub prepare_cookies { my $c = shift; $c->engine->prepare_cookies( $c, @_ ) }
1443
b5ecfcf0 1444=head2 $c->prepare_headers
fbcc39ad 1445
e7f1cf73 1446Prepares headers.
fbcc39ad 1447
1448=cut
1449
1450sub prepare_headers { my $c = shift; $c->engine->prepare_headers( $c, @_ ) }
1451
b5ecfcf0 1452=head2 $c->prepare_parameters
fbcc39ad 1453
e7f1cf73 1454Prepares parameters.
fbcc39ad 1455
1456=cut
1457
1458sub prepare_parameters {
1459 my $c = shift;
1460 $c->prepare_body_parameters;
1461 $c->engine->prepare_parameters( $c, @_ );
1462}
1463
b5ecfcf0 1464=head2 $c->prepare_path
fbcc39ad 1465
e7f1cf73 1466Prepares path and base.
fbcc39ad 1467
1468=cut
1469
1470sub prepare_path { my $c = shift; $c->engine->prepare_path( $c, @_ ) }
1471
b5ecfcf0 1472=head2 $c->prepare_query_parameters
fbcc39ad 1473
e7f1cf73 1474Prepares query parameters.
fbcc39ad 1475
1476=cut
1477
1478sub prepare_query_parameters {
1479 my $c = shift;
1480
1481 $c->engine->prepare_query_parameters( $c, @_ );
1482
1483 if ( $c->debug && keys %{ $c->request->query_parameters } ) {
8c113188 1484 my $t = Text::SimpleTable->new( [ 37, 'Key' ], [ 36, 'Value' ] );
fbcc39ad 1485 for my $key ( sort keys %{ $c->req->query_parameters } ) {
1486 my $param = $c->req->query_parameters->{$key};
1487 my $value = defined($param) ? $param : '';
8c113188 1488 $t->row( $key,
fbcc39ad 1489 ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
1490 }
1491 $c->log->debug( "Query Parameters are:\n" . $t->draw );
1492 }
1493}
1494
b5ecfcf0 1495=head2 $c->prepare_read
fbcc39ad 1496
e7f1cf73 1497Prepares the input for reading.
fbcc39ad 1498
1499=cut
1500
1501sub prepare_read { my $c = shift; $c->engine->prepare_read( $c, @_ ) }
1502
b5ecfcf0 1503=head2 $c->prepare_request
fbcc39ad 1504
e7f1cf73 1505Prepares the engine request.
fbcc39ad 1506
1507=cut
1508
1509sub prepare_request { my $c = shift; $c->engine->prepare_request( $c, @_ ) }
1510
b5ecfcf0 1511=head2 $c->prepare_uploads
fbcc39ad 1512
e7f1cf73 1513Prepares uploads.
fbcc39ad 1514
1515=cut
1516
1517sub prepare_uploads {
1518 my $c = shift;
1519
1520 $c->engine->prepare_uploads( $c, @_ );
1521
1522 if ( $c->debug && keys %{ $c->request->uploads } ) {
8c113188 1523 my $t = Text::SimpleTable->new(
1524 [ 12, 'Key' ],
1525 [ 28, 'Filename' ],
1526 [ 18, 'Type' ],
1527 [ 9, 'Size' ]
1528 );
fbcc39ad 1529 for my $key ( sort keys %{ $c->request->uploads } ) {
1530 my $upload = $c->request->uploads->{$key};
1531 for my $u ( ref $upload eq 'ARRAY' ? @{$upload} : ($upload) ) {
8c113188 1532 $t->row( $key, $u->filename, $u->type, $u->size );
fbcc39ad 1533 }
1534 }
1535 $c->log->debug( "File Uploads are:\n" . $t->draw );
1536 }
1537}
1538
b5ecfcf0 1539=head2 $c->prepare_write
fbcc39ad 1540
e7f1cf73 1541Prepares the output for writing.
fbcc39ad 1542
1543=cut
1544
1545sub prepare_write { my $c = shift; $c->engine->prepare_write( $c, @_ ) }
1546
b5ecfcf0 1547=head2 $c->request_class
1f9cb7c1 1548
e7f1cf73 1549Returns or sets the request class.
1f9cb7c1 1550
b5ecfcf0 1551=head2 $c->response_class
1f9cb7c1 1552
e7f1cf73 1553Returns or sets the response class.
1f9cb7c1 1554
b5ecfcf0 1555=head2 $c->read( [$maxlength] )
fbcc39ad 1556
ae1e6b59 1557Reads a chunk of data from the request body. This method is designed to
1558be used in a while loop, reading C<$maxlength> bytes on every call.
1559C<$maxlength> defaults to the size of the request if not specified.
fbcc39ad 1560
ae1e6b59 1561You have to set C<MyApp-E<gt>config-E<gt>{parse_on_demand}> to use this
1562directly.
fbcc39ad 1563
1564=cut
1565
1566sub read { my $c = shift; return $c->engine->read( $c, @_ ) }
1567
b5ecfcf0 1568=head2 $c->run
fbcc39ad 1569
1570Starts the engine.
1571
1572=cut
1573
1574sub run { my $c = shift; return $c->engine->run( $c, @_ ) }
1575
b5ecfcf0 1576=head2 $c->set_action( $action, $code, $namespace, $attrs )
fbcc39ad 1577
e7f1cf73 1578Sets an action in a given namespace.
fbcc39ad 1579
1580=cut
1581
1582sub set_action { my $c = shift; $c->dispatcher->set_action( $c, @_ ) }
1583
b5ecfcf0 1584=head2 $c->setup_actions($component)
fbcc39ad 1585
e7f1cf73 1586Sets up actions for a component.
fbcc39ad 1587
1588=cut
1589
1590sub setup_actions { my $c = shift; $c->dispatcher->setup_actions( $c, @_ ) }
1591
b5ecfcf0 1592=head2 $c->setup_components
fbcc39ad 1593
e7f1cf73 1594Sets up components.
fbcc39ad 1595
1596=cut
1597
1598sub setup_components {
1599 my $class = shift;
1600
1601 my $callback = sub {
1602 my ( $component, $context ) = @_;
1603
31375184 1604 unless ( $component->can('COMPONENT') ) {
fbcc39ad 1605 return $component;
1606 }
1607
76cb6276 1608 my $suffix = Catalyst::Utils::class2classsuffix($component);
fbcc39ad 1609 my $config = $class->config->{$suffix} || {};
1610
1611 my $instance;
1612
22247e54 1613 eval { $instance = $component->COMPONENT( $context, $config ); };
fbcc39ad 1614
1615 if ( my $error = $@ ) {
1616
1617 chomp $error;
1618
1619 Catalyst::Exception->throw( message =>
1620 qq/Couldn't instantiate component "$component", "$error"/ );
1621 }
1622
1623 Catalyst::Exception->throw( message =>
6c5033cb 1624qq/Couldn't instantiate component "$component", "COMPONENT() didn't return a object"/
fbcc39ad 1625 )
1626 unless ref $instance;
1627 return $instance;
1628 };
1629
6f006bd6 1630 eval "package $class;\n" . q!Module::Pluggable::Fast->import(
fbcc39ad 1631 name => '_catalyst_components',
1632 search => [
1633 "$class\::Controller", "$class\::C",
1634 "$class\::Model", "$class\::M",
1635 "$class\::View", "$class\::V"
1636 ],
1637 callback => $callback
1638 );
4289f674 1639 !;
fbcc39ad 1640
1641 if ( my $error = $@ ) {
1642
1643 chomp $error;
1644
1645 Catalyst::Exception->throw(
1646 message => qq/Couldn't load components "$error"/ );
1647 }
1648
1649 for my $component ( $class->_catalyst_components($class) ) {
1650 $class->components->{ ref $component || $component } = $component;
1651 }
1652}
1653
b5ecfcf0 1654=head2 $c->setup_dispatcher
fbcc39ad 1655
ae1e6b59 1656Sets up dispatcher.
1657
fbcc39ad 1658=cut
1659
1660sub setup_dispatcher {
1661 my ( $class, $dispatcher ) = @_;
1662
1663 if ($dispatcher) {
1664 $dispatcher = 'Catalyst::Dispatcher::' . $dispatcher;
1665 }
1666
1667 if ( $ENV{CATALYST_DISPATCHER} ) {
1668 $dispatcher = 'Catalyst::Dispatcher::' . $ENV{CATALYST_DISPATCHER};
1669 }
1670
1671 if ( $ENV{ uc($class) . '_DISPATCHER' } ) {
1672 $dispatcher =
1673 'Catalyst::Dispatcher::' . $ENV{ uc($class) . '_DISPATCHER' };
1674 }
1675
1676 unless ($dispatcher) {
cb0354c6 1677 $dispatcher = $class->dispatcher_class;
fbcc39ad 1678 }
1679
1680 $dispatcher->require;
1681
1682 if ($@) {
1683 Catalyst::Exception->throw(
1684 message => qq/Couldn't load dispatcher "$dispatcher", "$@"/ );
1685 }
1686
1687 # dispatcher instance
1688 $class->dispatcher( $dispatcher->new );
1689}
1690
b5ecfcf0 1691=head2 $c->setup_engine
fbcc39ad 1692
ae1e6b59 1693Sets up engine.
1694
fbcc39ad 1695=cut
1696
1697sub setup_engine {
1698 my ( $class, $engine ) = @_;
1699
1700 if ($engine) {
1701 $engine = 'Catalyst::Engine::' . $engine;
1702 }
1703
1704 if ( $ENV{CATALYST_ENGINE} ) {
1705 $engine = 'Catalyst::Engine::' . $ENV{CATALYST_ENGINE};
1706 }
1707
1708 if ( $ENV{ uc($class) . '_ENGINE' } ) {
1709 $engine = 'Catalyst::Engine::' . $ENV{ uc($class) . '_ENGINE' };
1710 }
1711
9b0a3e0f 1712 if ( $ENV{MOD_PERL} ) {
fbcc39ad 1713
1714 # create the apache method
1715 {
1716 no strict 'refs';
1717 *{"$class\::apache"} = sub { shift->engine->apache };
1718 }
1719
1720 my ( $software, $version ) =
1721 $ENV{MOD_PERL} =~ /^(\S+)\/(\d+(?:[\.\_]\d+)+)/;
1722
1723 $version =~ s/_//g;
1724 $version =~ s/(\.[^.]+)\./$1/g;
1725
1726 if ( $software eq 'mod_perl' ) {
1727
9b0a3e0f 1728 if ( !$engine ) {
22247e54 1729
9b0a3e0f 1730 if ( $version >= 1.99922 ) {
1731 $engine = 'Catalyst::Engine::Apache2::MP20';
1732 }
22247e54 1733
9b0a3e0f 1734 elsif ( $version >= 1.9901 ) {
1735 $engine = 'Catalyst::Engine::Apache2::MP19';
1736 }
22247e54 1737
9b0a3e0f 1738 elsif ( $version >= 1.24 ) {
1739 $engine = 'Catalyst::Engine::Apache::MP13';
1740 }
22247e54 1741
9b0a3e0f 1742 else {
1743 Catalyst::Exception->throw( message =>
1744 qq/Unsupported mod_perl version: $ENV{MOD_PERL}/ );
1745 }
fbcc39ad 1746
fbcc39ad 1747 }
1748
1749 # install the correct mod_perl handler
1750 if ( $version >= 1.9901 ) {
1751 *handler = sub : method {
1752 shift->handle_request(@_);
1753 };
1754 }
1755 else {
1756 *handler = sub ($$) { shift->handle_request(@_) };
1757 }
1758
1759 }
1760
1761 elsif ( $software eq 'Zeus-Perl' ) {
1762 $engine = 'Catalyst::Engine::Zeus';
1763 }
1764
1765 else {
1766 Catalyst::Exception->throw(
1767 message => qq/Unsupported mod_perl: $ENV{MOD_PERL}/ );
1768 }
1769 }
1770
1771 unless ($engine) {
cb0354c6 1772 $engine = $class->engine_class;
fbcc39ad 1773 }
1774
1775 $engine->require;
1776
1777 if ($@) {
1778 Catalyst::Exception->throw( message =>
1779qq/Couldn't load engine "$engine" (maybe you forgot to install it?), "$@"/
1780 );
1781 }
0e7f5826 1782
d54484bf 1783 # check for old engines that are no longer compatible
1784 my $old_engine;
0e7f5826 1785 if ( $engine->isa('Catalyst::Engine::Apache')
1786 && !Catalyst::Engine::Apache->VERSION )
d54484bf 1787 {
1788 $old_engine = 1;
1789 }
0e7f5826 1790
d54484bf 1791 elsif ( $engine->isa('Catalyst::Engine::Server::Base')
0e7f5826 1792 && Catalyst::Engine::Server->VERSION le '0.02' )
d54484bf 1793 {
1794 $old_engine = 1;
1795 }
0e7f5826 1796
1797 elsif ($engine->isa('Catalyst::Engine::HTTP::POE')
1798 && $engine->VERSION eq '0.01' )
d54484bf 1799 {
1800 $old_engine = 1;
1801 }
0e7f5826 1802
1803 elsif ($engine->isa('Catalyst::Engine::Zeus')
1804 && $engine->VERSION eq '0.01' )
d54484bf 1805 {
1806 $old_engine = 1;
1807 }
fbcc39ad 1808
d54484bf 1809 if ($old_engine) {
1810 Catalyst::Exception->throw( message =>
0e7f5826 1811 qq/Engine "$engine" is not supported by this version of Catalyst/
d54484bf 1812 );
1813 }
0e7f5826 1814
fbcc39ad 1815 # engine instance
1816 $class->engine( $engine->new );
1817}
1818
b5ecfcf0 1819=head2 $c->setup_home
fbcc39ad 1820
ae1e6b59 1821Sets up the home directory.
1822
fbcc39ad 1823=cut
1824
1825sub setup_home {
1826 my ( $class, $home ) = @_;
1827
1828 if ( $ENV{CATALYST_HOME} ) {
1829 $home = $ENV{CATALYST_HOME};
1830 }
1831
1832 if ( $ENV{ uc($class) . '_HOME' } ) {
1833 $home = $ENV{ uc($class) . '_HOME' };
1834 }
1835
1836 unless ($home) {
1837 $home = Catalyst::Utils::home($class);
1838 }
1839
1840 if ($home) {
1841 $class->config->{home} ||= $home;
1842 $class->config->{root} ||= dir($home)->subdir('root');
1843 }
1844}
1845
b5ecfcf0 1846=head2 $c->setup_log
fbcc39ad 1847
ae1e6b59 1848Sets up log.
1849
fbcc39ad 1850=cut
1851
1852sub setup_log {
1853 my ( $class, $debug ) = @_;
1854
1855 unless ( $class->log ) {
1856 $class->log( Catalyst::Log->new );
1857 }
af3ff00e 1858
71f074a9 1859 my $app_flag = Catalyst::Utils::class2env($class) . '_DEBUG';
71f074a9 1860
af3ff00e 1861 if (
1862 ( defined( $ENV{CATALYST_DEBUG} ) || defined( $ENV{$app_flag} ) )
1863 ? ( $ENV{CATALYST_DEBUG} || $ENV{$app_flag} )
1864 : $debug
1865 )
1866 {
fbcc39ad 1867 no strict 'refs';
1868 *{"$class\::debug"} = sub { 1 };
1869 $class->log->debug('Debug messages enabled');
1870 }
1871}
1872
b5ecfcf0 1873=head2 $c->setup_plugins
fbcc39ad 1874
ae1e6b59 1875Sets up plugins.
1876
fbcc39ad 1877=cut
1878
836e1134 1879=head2 $c->registered_plugins
1880
1881Returns a sorted list of the plugins which have either been stated in the
1882import list or which have been added via C<< MyApp->plugin(@args); >>.
1883
1884If passed a given plugin name, it will report a boolean value indicating
1885whether or not that plugin is loaded. A fully qualified name is required if
1886the plugin name does not begin with C<Catalyst::Plugin::>.
1887
1888 if ($c->registered_plugins('Some::Plugin')) {
1889 ...
1890 }
1891
1892=cut
fbcc39ad 1893
836e1134 1894{
97b58e17 1895
1896 sub registered_plugins {
836e1134 1897 my $proto = shift;
d0d4d785 1898 return sort keys %{$proto->_plugins} unless @_;
836e1134 1899 my $plugin = shift;
d0d4d785 1900 return 1 if exists $proto->_plugins->{$plugin};
1901 return exists $proto->_plugins->{"Catalyst::Plugin::$plugin"};
836e1134 1902 }
fbcc39ad 1903
836e1134 1904 sub _register_plugin {
1905 my ( $proto, $plugin, $instant ) = @_;
1906 my $class = ref $proto || $proto;
fbcc39ad 1907
1908 $plugin->require;
1909
836e1134 1910 if ( my $error = $@ ) {
1911 my $type = $instant ? "instant " : '';
fbcc39ad 1912 Catalyst::Exception->throw(
836e1134 1913 message => qq/Couldn't load ${type}plugin "$plugin", $error/ );
fbcc39ad 1914 }
1915
d0d4d785 1916 $proto->_plugins->{$plugin} = 1;
836e1134 1917 unless ($instant) {
fbcc39ad 1918 no strict 'refs';
1919 unshift @{"$class\::ISA"}, $plugin;
1920 }
836e1134 1921 return $class;
1922 }
1923
1924 sub setup_plugins {
1925 my ( $class, $plugins ) = @_;
1926
d0d4d785 1927 $class->_plugins( {} ) unless $class->_plugins;
836e1134 1928 $plugins ||= [];
1929 for my $plugin ( reverse @$plugins ) {
1930
1931 unless ( $plugin =~ s/\A\+// ) {
1932 $plugin = "Catalyst::Plugin::$plugin";
1933 }
1934
1935 $class->_register_plugin($plugin);
1936 }
fbcc39ad 1937 }
1938}
1939
b5ecfcf0 1940=head2 $c->stack
8767c5a3 1941
0ef52a96 1942Returns the stack.
8767c5a3 1943
b5ecfcf0 1944=head2 $c->write( $data )
fbcc39ad 1945
ae1e6b59 1946Writes $data to the output stream. When using this method directly, you
1947will need to manually set the C<Content-Length> header to the length of
1948your output data, if known.
fbcc39ad 1949
1950=cut
1951
4f5ebacd 1952sub write {
1953 my $c = shift;
1954
1955 # Finalize headers if someone manually writes output
1956 $c->finalize_headers;
1957
1958 return $c->engine->write( $c, @_ );
1959}
fbcc39ad 1960
b5ecfcf0 1961=head2 version
bf88a181 1962
ae1e6b59 1963Returns the Catalyst version number. Mostly useful for "powered by"
1964messages in template systems.
bf88a181 1965
1966=cut
1967
1968sub version { return $Catalyst::VERSION }
1969
b0bb11ec 1970=head1 INTERNAL ACTIONS
1971
ae1e6b59 1972Catalyst uses internal actions like C<_DISPATCH>, C<_BEGIN>, C<_AUTO>,
1973C<_ACTION>, and C<_END>. These are by default not shown in the private
3e705254 1974action table, but you can make them visible with a config parameter.
b0bb11ec 1975
1976 MyApp->config->{show_internal_actions} = 1;
1977
d2ee9760 1978=head1 CASE SENSITIVITY
1979
3e705254 1980By default Catalyst is not case sensitive, so C<MyApp::C::FOO::Bar> is
ae1e6b59 1981mapped to C</foo/bar>. You can activate case sensitivity with a config
3e705254 1982parameter.
d2ee9760 1983
1984 MyApp->config->{case_sensitive} = 1;
1985
3e705254 1986This causes C<MyApp::C::Foo::Bar> to map to C</Foo/Bar>.
fbcc39ad 1987
1988=head1 ON-DEMAND PARSER
1989
1990The request body is usually parsed at the beginning of a request,
3e705254 1991but if you want to handle input yourself or speed things up a bit,
fbcc39ad 1992you can enable on-demand parsing with a config parameter.
1993
1994 MyApp->config->{parse_on_demand} = 1;
1995
1996=head1 PROXY SUPPORT
1997
ae1e6b59 1998Many production servers operate using the common double-server approach,
1999with a lightweight frontend web server passing requests to a larger
2000backend server. An application running on the backend server must deal
2001with two problems: the remote user always appears to be C<127.0.0.1> and
2002the server's hostname will appear to be C<localhost> regardless of the
2003virtual host that the user connected through.
fbcc39ad 2004
ae1e6b59 2005Catalyst will automatically detect this situation when you are running
2006the frontend and backend servers on the same machine. The following
2007changes are made to the request.
fbcc39ad 2008
ae1e6b59 2009 $c->req->address is set to the user's real IP address, as read from
2010 the HTTP X-Forwarded-For header.
fbcc39ad 2011
ae1e6b59 2012 The host value for $c->req->base and $c->req->uri is set to the real
2013 host, as read from the HTTP X-Forwarded-Host header.
fbcc39ad 2014
3e705254 2015Obviously, your web server must support these headers for this to work.
fbcc39ad 2016
ae1e6b59 2017In a more complex server farm environment where you may have your
2018frontend proxy server(s) on different machines, you will need to set a
2019configuration option to tell Catalyst to read the proxied data from the
2020headers.
fbcc39ad 2021
2022 MyApp->config->{using_frontend_proxy} = 1;
2023
2024If you do not wish to use the proxy support at all, you may set:
d1a31ac6 2025
fbcc39ad 2026 MyApp->config->{ignore_frontend_proxy} = 1;
2027
2028=head1 THREAD SAFETY
2029
2030Catalyst has been tested under Apache 2's threading mpm_worker, mpm_winnt,
3e705254 2031and the standalone forking HTTP server on Windows. We believe the Catalyst
fbcc39ad 2032core to be thread-safe.
2033
2034If you plan to operate in a threaded environment, remember that all other
3e705254 2035modules you are using must also be thread-safe. Some modules, most notably
2036L<DBD::SQLite>, are not thread-safe.
d1a31ac6 2037
3cb1db8c 2038=head1 SUPPORT
2039
2040IRC:
2041
2042 Join #catalyst on irc.perl.org.
2043
3e705254 2044Mailing Lists:
3cb1db8c 2045
2046 http://lists.rawmode.org/mailman/listinfo/catalyst
2047 http://lists.rawmode.org/mailman/listinfo/catalyst-dev
1985c30b 2048
432d507d 2049Web:
2050
2051 http://catalyst.perl.org
2052
0ef52a96 2053Wiki:
2054
2055 http://dev.catalyst.perl.org
2056
fc7ec1d9 2057=head1 SEE ALSO
2058
829a28ca 2059=head2 L<Task::Catalyst> - All you need to start with Catalyst
2060
b5ecfcf0 2061=head2 L<Catalyst::Manual> - The Catalyst Manual
e7f1cf73 2062
b5ecfcf0 2063=head2 L<Catalyst::Component>, L<Catalyst::Base> - Base classes for components
61b1e958 2064
b5ecfcf0 2065=head2 L<Catalyst::Engine> - Core engine
61b1e958 2066
b5ecfcf0 2067=head2 L<Catalyst::Log> - Log class.
61b1e958 2068
b5ecfcf0 2069=head2 L<Catalyst::Request> - Request object
61b1e958 2070
b5ecfcf0 2071=head2 L<Catalyst::Response> - Response object
61b1e958 2072
b5ecfcf0 2073=head2 L<Catalyst::Test> - The test suite.
fc7ec1d9 2074
15f0b5b7 2075=head1 CREDITS
fc7ec1d9 2076
15f0b5b7 2077Andy Grundman
2078
fbcc39ad 2079Andy Wardley
2080
33108eaf 2081Andreas Marienborg
2082
f4a57de4 2083Andrew Bramble
2084
15f0b5b7 2085Andrew Ford
2086
2087Andrew Ruthven
2088
fbcc39ad 2089Arthur Bergman
2090
15f0b5b7 2091Autrijus Tang
2092
0cf56dbc 2093Brian Cassidy
2094
6aaa1c60 2095Carl Franks
2096
15f0b5b7 2097Christian Hansen
2098
2099Christopher Hicks
2100
2101Dan Sully
2102
2103Danijel Milicevic
2104
0ef52a96 2105David Kamholz
2106
15f0b5b7 2107David Naughton
2108
61bef238 2109Drew Taylor
2110
15f0b5b7 2111Gary Ashton Jones
2112
2113Geoff Richards
2114
2115Jesse Sheidlower
2116
fbcc39ad 2117Jesse Vincent
2118
15f0b5b7 2119Jody Belka
2120
2121Johan Lindstrom
2122
2123Juan Camacho
2124
2125Leon Brocard
2126
2127Marcus Ramberg
2128
2129Matt S Trout
2130
71c3bcc3 2131Robert Sedlacek
2132
a727119f 2133Sam Vilain
2134
1cf1c56a 2135Sascha Kiefer
2136
15f0b5b7 2137Tatsuhiko Miyagawa
fc7ec1d9 2138
51f0308d 2139Ulf Edvinsson
2140
bdcb95ef 2141Yuval Kogman
2142
51f0308d 2143=head1 AUTHOR
2144
2145Sebastian Riedel, C<sri@oook.de>
2146
fc7ec1d9 2147=head1 LICENSE
2148
9ce5ab63 2149This library is free software, you can redistribute it and/or modify it under
41ca9ba7 2150the same terms as Perl itself.
fc7ec1d9 2151
2152=cut
2153
21541;