4 use base 'Catalyst::Component';
6 use UNIVERSAL::require;
7 use Catalyst::Exception;
10 use Catalyst::Request::Upload;
11 use Catalyst::Response;
13 use Catalyst::Controller;
16 use Text::SimpleTable;
18 use Path::Class::File;
19 use Time::HiRes qw/gettimeofday tv_interval/;
21 use Scalar::Util qw/weaken blessed/;
22 use Tree::Simple qw/use_weak_refs/;
23 use Tree::Simple::Visitor::FindByUID;
26 __PACKAGE__->mk_accessors(
27 qw/counter request response state action stack namespace/
30 attributes->import( __PACKAGE__, \&namespace, 'lvalue' );
32 sub depth { scalar @{ shift->stack || [] }; }
39 # For backwards compatibility
40 *finalize_output = \&finalize_body;
45 our $RECURSION = 1000;
46 our $DETACH = "catalyst_detach\n";
48 require Module::Pluggable::Fast;
50 # Helper script generation
51 our $CATALYST_SCRIPT_GEN = 27;
53 __PACKAGE__->mk_classdata($_)
54 for qw/components arguments dispatcher engine log dispatcher_class
55 engine_class context_class request_class response_class setup_finished/;
57 __PACKAGE__->dispatcher_class('Catalyst::Dispatcher');
58 __PACKAGE__->engine_class('Catalyst::Engine::CGI');
59 __PACKAGE__->request_class('Catalyst::Request');
60 __PACKAGE__->response_class('Catalyst::Response');
62 our $VERSION = '5.66';
65 my ( $class, @arguments ) = @_;
67 # We have to limit $class to Catalyst to avoid pushing Catalyst upon every
69 return unless $class eq 'Catalyst';
71 my $caller = caller(0);
73 unless ( $caller->isa('Catalyst') ) {
75 push @{"$caller\::ISA"}, $class, 'Catalyst::Controller';
78 $caller->arguments( [@arguments] );
84 Catalyst - The Elegant MVC Web Application Framework
88 # use the helper to start a new application
91 # add models, views, controllers
92 script/myapp_create.pl model Database DBIC dbi:SQLite:/path/to/db
93 script/myapp_create.pl view TT TT
94 script/myapp_create.pl controller Search
96 # built in testserver -- use -r to restart automatically on changes
97 script/myapp_server.pl
99 # command line testing interface
100 script/myapp_test.pl /yada
103 use Catalyst qw/-Debug/; # include plugins here as well
105 sub foo : Global { # called for /foo, /foo/1, /foo/1/2, etc.
106 my ( $self, $c, @args ) = @_; # args are qw/1 2/ for /foo/1/2
107 $c->stash->{template} = 'foo.tt'; # set the template
108 # lookup something from db -- stash vars are passed to TT
110 MyApp::Model::Database::Foo->search( { country => $args[0] } );
111 if ( $c->req->params->{bar} ) { # access GET or POST parameters
112 $c->forward( 'bar' ); # process another action
113 # do something else after forward returns
117 # The foo.tt TT template can use the stash data from the database
118 [% WHILE (item = data.next) %]
122 # called for /bar/of/soap, /bar/of/soap/10, etc.
123 sub bar : Path('/bar/of/soap') { ... }
125 # called for all actions, from the top-most controller downwards
127 my ( $self, $c ) = @_;
129 $c->res->redirect( '/login' ); # require login
130 return 0; # abort request and go immediately to end()
132 return 1; # success; carry on to next action
135 # called after all actions are finished
137 my ( $self, $c ) = @_;
138 if ( scalar @{ $c->error } ) { ... } # handle errors
139 return if $c->res->body; # already have a response
140 $c->forward( 'MyApp::View::TT' ); # render template
143 ### in MyApp/Controller/Foo.pm
144 # called for /foo/bar
145 sub bar : Local { ... }
147 # called for /blargle
148 sub blargle : Global { ... }
150 # an index action matches /foo, but not /foo/1, etc.
151 sub index : Private { ... }
153 ### in MyApp/Controller/Foo/Bar.pm
154 # called for /foo/bar/baz
155 sub baz : Local { ... }
157 # first MyApp auto is called, then Foo auto, then this
158 sub auto : Private { ... }
160 # powerful regular expression paths are also possible
161 sub details : Regex('^product/(\w+)/details$') {
162 my ( $self, $c ) = @_;
163 # extract the (\w+) from the URI
164 my $product = $c->req->snippets->[0];
167 See L<Catalyst::Manual::Intro> for additional information.
171 The key concept of Catalyst is DRY (Don't Repeat Yourself).
173 See L<Catalyst::Manual> for more documentation.
175 Catalyst plugins can be loaded by naming them as arguments to the "use
176 Catalyst" statement. Omit the C<Catalyst::Plugin::> prefix from the
177 plugin name, i.e., C<Catalyst::Plugin::My::Module> becomes
180 use Catalyst qw/My::Module/;
182 If your plugin starts with a name other than C<Catalyst::Plugin::>, you can
183 fully qualify the name by using a unary plus:
187 +Fully::Qualified::Plugin::Name
190 Special flags like C<-Debug> and C<-Engine> can also be specified as
191 arguments when Catalyst is loaded:
193 use Catalyst qw/-Debug My::Module/;
195 The position of plugins and flags in the chain is important, because
196 they are loaded in exactly the order in which they appear.
198 The following flags are supported:
202 Enables debug output.
206 Forces Catalyst to use a specific engine. Omit the
207 C<Catalyst::Engine::> prefix of the engine name, i.e.:
209 use Catalyst qw/-Engine=CGI/;
213 Forces Catalyst to use a specific home directory, e.g.:
215 use Catalyst qw[-Home=/usr/sri];
223 =head2 INFORMATION ABOUT THE CURRENT REQUEST
227 Returns a L<Catalyst::Action> object for the current action, which
228 stringifies to the action name. See L<Catalyst::Action>.
232 Returns the namespace of the current action, i.e., the uri prefix
233 corresponding to the controller of the current action. For example:
235 # in Controller::Foo::Bar
236 $c->namespace; # returns 'foo/bar';
242 Returns the current L<Catalyst::Request> object. See
243 L<Catalyst::Request>.
245 =head2 PROCESSING AND RESPONSE TO THE CURRENT REQUEST
247 =head2 $c->forward( $action [, \@arguments ] )
249 =head2 $c->forward( $class, $method, [, \@arguments ] )
251 Forwards processing to a private action. If you give a class name but no
252 method, C<process()> is called. You may also optionally pass arguments
253 in an arrayref. The action will receive the arguments in C<@_> and
254 C<$c-E<gt>req-E<gt>args>. Upon returning from the function,
255 C<$c-E<gt>req-E<gt>args> will be restored to the previous values.
257 Any data C<return>ed from the action forwarded to, will be returned by the
260 my $foodata = $c->forward('/foo');
261 $c->forward('index');
262 $c->forward(qw/MyApp::Model::DBIC::Foo do_stuff/);
263 $c->forward('MyApp::View::TT');
267 sub forward { my $c = shift; $c->dispatcher->forward( $c, @_ ) }
269 =head2 $c->detach( $action [, \@arguments ] )
271 =head2 $c->detach( $class, $method, [, \@arguments ] )
273 The same as C<forward>, but doesn't return.
277 sub detach { my $c = shift; $c->dispatcher->detach( $c, @_ ) }
281 =head2 $c->error($error, ...)
283 =head2 $c->error($arrayref)
285 Returns an arrayref containing error messages. If Catalyst encounters an
286 error while processing a request, it stores the error in $c->error. This
287 method should not be used to store non-fatal error messages.
289 my @error = @{ $c->error };
293 $c->error('Something bad happened');
300 my $error = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];
301 push @{ $c->{error} }, @$error;
303 elsif ( defined $_[0] ) { $c->{error} = undef }
304 return $c->{error} || [];
307 =head2 $c->clear_errors
309 Clear errors. You probably don't want to clear the errors unless you are
310 implementing a custom error screen.
312 This is equivalent to running
327 Returns the current L<Catalyst::Response> object.
331 Returns a hashref to the stash, which may be used to store data and pass
332 it between components during a request. You can also set hash keys by
333 passing arguments. The stash is automatically sent to the view. The
334 stash is cleared at the end of a request; it cannot be used for
337 $c->stash->{foo} = $bar;
338 $c->stash( { moose => 'majestic', qux => 0 } );
339 $c->stash( bar => 1, gorch => 2 ); # equivalent to passing a hashref
341 # stash is automatically passed to the view for use in a template
342 $c->forward( 'MyApp::V::TT' );
349 my $stash = @_ > 1 ? {@_} : $_[0];
350 while ( my ( $key, $val ) = each %$stash ) {
351 $c->{stash}->{$key} = $val;
359 Contains the return value of the last executed action.
365 my ( $c, @names ) = @_;
367 foreach my $name (@names) {
368 foreach my $component ( keys %{ $c->components } ) {
369 return $c->components->{$component} if $component =~ /$name/i;
376 # try explicit component names
378 my ( $c, @names ) = @_;
380 foreach my $try (@names) {
381 return $c->components->{$try} if ( exists $c->components->{$try} );
387 # like component, but try just these prefixes before regex searching,
388 # and do not try to return "sort keys %{ $c->components }"
390 my ( $c, $name, @prefixes ) = @_;
392 my $appclass = ref $c || $c;
394 my @names = map { "${appclass}::${_}::${name}" } @prefixes;
396 my $comp = $c->_comp_explicit(@names);
397 return $comp if defined($comp);
398 $comp = $c->_comp_search($name);
402 # Find possible names for a prefix
405 my ( $c, @prefixes ) = @_;
407 my $appclass = ref $c || $c;
409 my @pre = map { "${appclass}::${_}::" } @prefixes;
413 COMPONENT: foreach my $comp ($c->component) {
414 foreach my $p (@pre) {
415 if ($comp =~ s/^$p//) {
425 # Return a component if only one matches.
427 my ( $c, @prefixes ) = @_;
429 my $appclass = ref $c || $c;
431 my ( $comp, $rest ) =
432 map { $c->_comp_search("^${appclass}::${_}::") } @prefixes;
433 return $comp unless $rest;
436 # Filter a component before returning by calling ACCEPT_CONTEXT if available
437 sub _filter_component {
438 my ( $c, $comp, @args ) = @_;
439 if ( eval { $comp->can('ACCEPT_CONTEXT'); } ) {
440 return $comp->ACCEPT_CONTEXT( $c, @args );
442 else { return $comp }
445 =head2 COMPONENT ACCESSORS
447 =head2 $c->comp($name)
449 =head2 $c->component($name)
451 Gets a component object by name. This method is no longer recommended,
452 unless you want to get a specific component by full
453 class. C<$c-E<gt>controller>, C<$c-E<gt>model>, and C<$c-E<gt>view>
454 should be used instead.
465 my $appclass = ref $c || $c;
468 $name, "${appclass}::${name}",
469 map { "${appclass}::${_}::${name}" }
470 qw/Model M Controller C View V/
473 my $comp = $c->_comp_explicit(@names);
474 return $c->_filter_component( $comp, @_ ) if defined($comp);
476 $comp = $c->_comp_search($name);
477 return $c->_filter_component( $comp, @_ ) if defined($comp);
480 return sort keys %{ $c->components };
483 =head2 $c->controller($name)
485 Gets a L<Catalyst::Controller> instance by name.
487 $c->controller('Foo')->do_stuff;
489 If name is omitted, will return the controller for the dispatched action.
494 my ( $c, $name, @args ) = @_;
495 return $c->_filter_component( $c->_comp_prefixes( $name, qw/Controller C/ ),
498 return $c->component( $c->action->class );
501 =head2 $c->controllers
503 Returns the available names which can be passed to $c->controller
509 return $c->_comp_names(qw/Controller C/);
512 =head2 $c->model($name)
514 Gets a L<Catalyst::Model> instance by name.
516 $c->model('Foo')->do_stuff;
518 If the name is omitted, it will look for a config setting 'default_model',
519 or check if there is only one model, and forward to it if that's the case.
524 my ( $c, $name, @args ) = @_;
525 return $c->_filter_component( $c->_comp_prefixes( $name, qw/Model M/ ),
528 return $c->component( $c->config->{default_model} )
529 if $c->config->{default_model};
530 return $c->_filter_component( $c->_comp_singular(qw/Model M/), @args );
536 Returns the available names which can be passed to $c->model
542 return $c->_comp_names(qw/Model M/);
545 =head2 $c->view($name)
547 Gets a L<Catalyst::View> instance by name.
549 $c->view('Foo')->do_stuff;
551 If the name is omitted, it will look for a config setting 'default_view',
552 or check if there is only one view, and forward to it if that's the case.
557 my ( $c, $name, @args ) = @_;
558 return $c->_filter_component( $c->_comp_prefixes( $name, qw/View V/ ),
561 return $c->component( $c->config->{default_view} )
562 if $c->config->{default_view};
563 return $c->_filter_component( $c->_comp_singular(qw/View V/) );
568 Returns the available names which can be passed to $c->view
574 return $c->_comp_names(qw/View V/);
577 =head2 Class data and helper classes
581 Returns or takes a hashref containing the application's configuration.
583 __PACKAGE__->config( { db => 'dsn:SQLite:foo.db' } );
585 You can also use a L<YAML> config file like myapp.yml in your
586 applications home directory.
589 db: dsn:SQLite:foo.db
596 $c->log->warn("Setting config after setup has been run is not a good idea.")
597 if ( @_ and $c->setup_finished );
599 $c->NEXT::config(@_);
604 Overload to enable debug messages (same as -Debug option).
610 =head2 $c->dispatcher
612 Returns the dispatcher instance. Stringifies to class name. See
613 L<Catalyst::Dispatcher>.
617 Returns the engine instance. Stringifies to the class name. See
622 Returns the logging object instance. Unless it is already set, Catalyst sets
623 this up with a L<Catalyst::Log> object. To use your own log class, set the
624 logger with the C<< __PACKAGE__->log >> method prior to calling
625 C<< __PACKAGE__->setup >>.
627 __PACKAGE__->log( MyLogger->new );
632 $c->log->info( 'Now logging with my own logger!' );
634 Your log class should implement the methods described in the
635 L<Catalyst::Log> man page.
639 =head2 UTILITY METHODS
641 =head2 $c->path_to(@path)
643 Merges C<@path> with C<$c-E<gt>config-E<gt>{home}> and returns a
644 L<Path::Class> object.
648 $c->path_to( 'db', 'sqlite.db' );
653 my ( $c, @path ) = @_;
654 my $path = Path::Class::Dir->new( $c->config->{home}, @path );
655 if ( -d $path ) { return $path }
656 else { return Path::Class::File->new( $c->config->{home}, @path ) }
659 =head2 $c->plugin( $name, $class, @args )
661 Helper method for plugins. It creates a classdata accessor/mutator and
662 loads and instantiates the given class.
664 MyApp->plugin( 'prototype', 'HTML::Prototype' );
666 $c->prototype->define_javascript_functions;
671 my ( $class, $name, $plugin, @args ) = @_;
672 $class->_register_plugin( $plugin, 1 );
674 eval { $plugin->import };
675 $class->mk_classdata($name);
677 eval { $obj = $plugin->new(@args) };
680 Catalyst::Exception->throw( message =>
681 qq/Couldn't instantiate instant plugin "$plugin", "$@"/ );
685 $class->log->debug(qq/Initialized instant plugin "$plugin" as "$name"/)
691 Initializes the dispatcher and engine, loads any plugins, and loads the
692 model, view, and controller components. You may also specify an array
693 of plugins to load here, if you choose to not load them in the C<use
697 MyApp->setup( qw/-Debug/ );
702 my ( $class, @arguments ) = @_;
704 unless ( $class->isa('Catalyst') ) {
706 Catalyst::Exception->throw(
707 message => qq/'$class' does not inherit from Catalyst/ );
710 if ( $class->arguments ) {
711 @arguments = ( @arguments, @{ $class->arguments } );
717 foreach (@arguments) {
721 ( $flags->{log} ) ? 'debug,' . $flags->{log} : 'debug';
723 elsif (/^-(\w+)=?(.*)$/) {
724 $flags->{ lc $1 } = $2;
727 push @{ $flags->{plugins} }, $_;
731 $class->setup_home( delete $flags->{home} );
733 $class->setup_log( delete $flags->{log} );
734 $class->setup_plugins( delete $flags->{plugins} );
735 $class->setup_dispatcher( delete $flags->{dispatcher} );
736 $class->setup_engine( delete $flags->{engine} );
738 for my $flag ( sort keys %{$flags} ) {
740 if ( my $code = $class->can( 'setup_' . $flag ) ) {
741 &$code( $class, delete $flags->{$flag} );
744 $class->log->warn(qq/Unknown flag "$flag"/);
749 <<"EOF") if ( $ENV{CATALYST_SCRIPT_GEN} && ( $ENV{CATALYST_SCRIPT_GEN} < $Catalyst::CATALYST_SCRIPT_GEN ) );
750 You are running an old script!
752 Please update by running (this will overwrite existing files):
753 catalyst.pl -force -scripts $class
755 or (this will not overwrite existing files):
756 catalyst.pl -scripts $class
759 if ( $class->debug ) {
766 map { $_ . ' ' . ( $_->VERSION || '' ) }
767 grep { /^Catalyst::Plugin/ } @{"$class\::ISA"};
771 my $t = Text::SimpleTable->new(76);
772 $t->row($_) for @plugins;
773 $class->log->debug( "Loaded plugins:\n" . $t->draw );
776 my $dispatcher = $class->dispatcher;
777 my $engine = $class->engine;
778 my $home = $class->config->{home};
780 $class->log->debug(qq/Loaded dispatcher "$dispatcher"/);
781 $class->log->debug(qq/Loaded engine "$engine"/);
785 ? $class->log->debug(qq/Found home "$home"/)
786 : $class->log->debug(qq/Home "$home" doesn't exist/)
787 : $class->log->debug(q/Couldn't find home/);
792 no warnings qw/redefine/;
793 local *setup = sub { };
797 # Initialize our data structure
798 $class->components( {} );
800 $class->setup_components;
802 if ( $class->debug ) {
803 my $t = Text::SimpleTable->new( [ 65, 'Class' ], [ 8, 'Type' ] );
804 for my $comp ( sort keys %{ $class->components } ) {
805 my $type = ref $class->components->{$comp} ? 'instance' : 'class';
806 $t->row( $comp, $type );
808 $class->log->debug( "Loaded components:\n" . $t->draw )
809 if ( keys %{ $class->components } );
812 # Add our self to components, since we are also a component
813 $class->components->{$class} = $class;
815 $class->setup_actions;
817 if ( $class->debug ) {
818 my $name = $class->config->{name} || 'Application';
819 $class->log->info("$name powered by Catalyst $Catalyst::VERSION");
821 $class->log->_flush() if $class->log->can('_flush');
823 $class->setup_finished(1);
826 =head2 $c->uri_for( $path, @args?, \%query_values? )
828 Merges path with C<$c-E<gt>request-E<gt>base> for absolute uri's and
829 with C<$c-E<gt>namespace> for relative uri's, then returns a
830 normalized L<URI> object. If any args are passed, they are added at the
831 end of the path. If the last argument to uri_for is a hash reference,
832 it is assumed to contain GET parameter key/value pairs, which will be
833 appended to the URI in standard fashion.
838 my ( $c, $path, @args ) = @_;
839 my $base = $c->request->base->clone;
840 my $basepath = $base->path;
841 $basepath =~ s/\/$//;
843 my $namespace = $c->namespace;
845 # massage namespace, empty if absolute path
846 $namespace =~ s/^\///;
847 $namespace .= '/' if $namespace;
849 $namespace = '' if $path =~ /^\//;
853 ( scalar @args && ref $args[$#args] eq 'HASH' ? pop @args : {} );
855 # join args with '/', or a blank string
856 my $args = ( scalar @args ? '/' . join( '/', @args ) : '' );
857 $args =~ s/^\/// unless $path;
859 URI->new_abs( URI->new_abs( "$path$args", "$basepath$namespace" ), $base )
861 $res->query_form(%$params);
865 =head2 $c->welcome_message
867 Returns the Catalyst welcome HTML page.
871 sub welcome_message {
873 my $name = $c->config->{name};
874 my $logo = $c->uri_for('/static/images/catalyst_logo.png');
875 my $prefix = Catalyst::Utils::appprefix( ref $c );
876 $c->response->content_type('text/html; charset=utf-8');
878 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
879 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
880 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
882 <meta http-equiv="Content-Language" content="en" />
883 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
884 <title>$name on Catalyst $VERSION</title>
885 <style type="text/css">
888 background-color: #eee;
897 background-color: #ccc;
898 border: 1px solid #aaa;
899 -moz-border-radius: 10px;
904 font-family: verdana, tahoma, sans-serif;
907 font-family: verdana, tahoma, sans-serif;
910 text-decoration: none;
912 border-bottom: 1px dotted #bbb;
914 :link:hover, :visited:hover {
927 background-color: #fff;
928 border: 1px solid #aaa;
929 -moz-border-radius: 10px;
955 <h1><span id="appname">$name</span> on <a href="http://catalyst.perl.org">Catalyst</a>
960 <img src="$logo" alt="Catalyst Logo" />
962 <p>Welcome to the wonderful world of Catalyst.
963 This <a href="http://en.wikipedia.org/wiki/MVC">MVC</a>
964 framework will make web development something you had
965 never expected it to be: Fun, rewarding, and quick.</p>
966 <h2>What to do now?</h2>
967 <p>That really depends on what <b>you</b> want to do.
968 We do, however, provide you with a few starting points.</p>
969 <p>If you want to jump right into web development with Catalyst
970 you might want to check out the documentation.</p>
971 <pre><code>perldoc <a href="http://cpansearch.perl.org/dist/Catalyst/lib/Catalyst/Manual/Intro.pod">Catalyst::Manual::Intro</a>
972 perldoc <a href="http://cpansearch.perl.org/dist/Catalyst/lib/Catalyst/Manual/Tutorial.pod">Catalyst::Manual::Tutorial</a></code>
973 perldoc <a href="http://cpansearch.perl.org/dist/Catalyst/lib/Catalyst/Manual.pod">Catalyst::Manual</a></code></pre>
974 <h2>What to do next?</h2>
975 <p>Next it's time to write an actual application. Use the
976 helper scripts to generate <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AController%3A%3A&mode=all">controllers</a>,
977 <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AModel%3A%3A&mode=all">models</a>, and
978 <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AView%3A%3A&mode=all">views</a>;
979 they can save you a lot of work.</p>
980 <pre><code>script/${prefix}_create.pl -help</code></pre>
981 <p>Also, be sure to check out the vast and growing
982 collection of <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3APlugin%3A%3A&mode=all">plugins for Catalyst on CPAN</a>;
983 you are likely to find what you need there.
987 <p>Catalyst has a very active community. Here are the main places to
988 get in touch with us.</p>
991 <a href="http://dev.catalyst.perl.org">Wiki</a>
994 <a href="http://lists.rawmode.org/mailman/listinfo/catalyst">Mailing-List</a>
997 <a href="irc://irc.perl.org/catalyst">IRC channel #catalyst on irc.perl.org</a>
1000 <h2>In conclusion</h2>
1001 <p>The Catalyst team hopes you will enjoy using Catalyst as much
1002 as we enjoyed making it. Please contact us if you have ideas
1003 for improvement or other feedback.</p>
1011 =head1 INTERNAL METHODS
1013 These methods are not meant to be used by end users.
1015 =head2 $c->components
1017 Returns a hash of components.
1019 =head2 $c->context_class
1021 Returns or sets the context class.
1025 Returns a hashref containing coderefs and execution counts (needed for
1026 deep recursion detection).
1030 Returns the number of actions on the current internal execution stack.
1034 Dispatches a request to actions.
1038 sub dispatch { my $c = shift; $c->dispatcher->dispatch( $c, @_ ) }
1040 =head2 $c->dispatcher_class
1042 Returns or sets the dispatcher class.
1044 =head2 $c->dump_these
1046 Returns a list of 2-element array references (name, structure) pairs
1047 that will be dumped on the error page in debug mode.
1053 [ Request => $c->req ], [ Response => $c->res ], [ Stash => $c->stash ],;
1056 =head2 $c->engine_class
1058 Returns or sets the engine class.
1060 =head2 $c->execute( $class, $coderef )
1062 Execute a coderef in given class and catch exceptions. Errors are available
1068 my ( $c, $class, $code ) = @_;
1069 $class = $c->component($class) || $class;
1072 if ( $c->depth >= $RECURSION ) {
1073 my $action = "$code";
1074 $action = "/$action" unless $action =~ /\-\>/;
1075 my $error = qq/Deep recursion detected calling "$action"/;
1076 $c->log->error($error);
1083 my $action = "$code";
1084 $action = "/$action" unless $action =~ /\-\>/;
1085 $c->counter->{"$code"}++;
1087 # determine if the call was the result of a forward
1088 # this is done by walking up the call stack and looking for a calling
1089 # sub of Catalyst::forward before the eval
1091 for my $index ( 1 .. 10 ) {
1093 if ( ( caller($index) )[0] eq 'Catalyst'
1094 && ( caller($index) )[3] eq '(eval)' );
1096 if ( ( caller($index) )[3] =~ /forward$/ ) {
1097 $callsub = ( caller($index) )[3];
1098 $action = "-> $action";
1103 my $node = Tree::Simple->new(
1106 elapsed => undef, # to be filled in later
1109 $node->setUID( "$code" . $c->counter->{"$code"} );
1111 unless ( ( $code->name =~ /^_.*/ )
1112 && ( !$c->config->{show_internal_actions} ) )
1115 # is this a root-level call or a forwarded call?
1116 if ( $callsub =~ /forward$/ ) {
1118 # forward, locate the caller
1119 if ( my $parent = $c->stack->[-1] ) {
1120 my $visitor = Tree::Simple::Visitor::FindByUID->new;
1121 $visitor->searchForUID(
1122 "$parent" . $c->counter->{"$parent"} );
1123 $c->{stats}->accept($visitor);
1124 if ( my $result = $visitor->getResult ) {
1125 $result->addChild($node);
1130 # forward with no caller may come from a plugin
1131 $c->{stats}->addChild($node);
1137 $c->{stats}->addChild($node);
1142 push( @{ $c->stack }, $code );
1145 $start = [gettimeofday] if $c->debug;
1146 eval { $c->state( &$code( $class, $c, @{ $c->req->args } ) || 0 ) };
1147 $elapsed = tv_interval($start) if $c->debug;
1150 unless ( ( $code->name =~ /^_.*/ )
1151 && ( !$c->config->{show_internal_actions} ) )
1154 # FindByUID uses an internal die, so we save the existing error
1157 # locate the node in the tree and update the elapsed time
1158 my $visitor = Tree::Simple::Visitor::FindByUID->new;
1159 $visitor->searchForUID( "$code" . $c->counter->{"$code"} );
1160 $c->{stats}->accept($visitor);
1161 if ( my $result = $visitor->getResult ) {
1162 my $value = $result->getNodeValue;
1163 $value->{elapsed} = sprintf( '%fs', $elapsed );
1164 $result->setNodeValue($value);
1168 $@ = $error || undef;
1171 my $last = ${ $c->stack }[-1];
1172 pop( @{ $c->stack } );
1174 if ( my $error = $@ ) {
1175 if ( $error eq $DETACH ) { die $DETACH if $c->depth > 1 }
1177 unless ( ref $error ) {
1179 my $class = $last->class;
1180 my $name = $last->name;
1181 $error = qq/Caught exception in $class->$name "$error"/;
1192 Finalizes the request.
1199 for my $error ( @{ $c->error } ) {
1200 $c->log->error($error);
1203 # Allow engine to handle finalize flow (for POE)
1204 if ( $c->engine->can('finalize') ) {
1205 $c->engine->finalize( $c );
1209 $c->finalize_uploads;
1212 if ( $#{ $c->error } >= 0 ) {
1216 $c->finalize_headers;
1219 if ( $c->request->method eq 'HEAD' ) {
1220 $c->response->body('');
1226 return $c->response->status;
1229 =head2 $c->finalize_body
1235 sub finalize_body { my $c = shift; $c->engine->finalize_body( $c, @_ ) }
1237 =head2 $c->finalize_cookies
1243 sub finalize_cookies { my $c = shift; $c->engine->finalize_cookies( $c, @_ ) }
1245 =head2 $c->finalize_error
1251 sub finalize_error { my $c = shift; $c->engine->finalize_error( $c, @_ ) }
1253 =head2 $c->finalize_headers
1259 sub finalize_headers {
1262 # Check if we already finalized headers
1263 return if $c->response->{_finalized_headers};
1266 if ( my $location = $c->response->redirect ) {
1267 $c->log->debug(qq/Redirecting to "$location"/) if $c->debug;
1268 $c->response->header( Location => $location );
1272 if ( $c->response->body && !$c->response->content_length ) {
1274 # get the length from a filehandle
1275 if ( blessed( $c->response->body ) && $c->response->body->can('read') )
1277 if ( my $stat = stat $c->response->body ) {
1278 $c->response->content_length( $stat->size );
1281 $c->log->warn('Serving filehandle without a content-length');
1285 $c->response->content_length( bytes::length( $c->response->body ) );
1290 if ( $c->response->status =~ /^(1\d\d|[23]04)$/ ) {
1291 $c->response->headers->remove_header("Content-Length");
1292 $c->response->body('');
1295 $c->finalize_cookies;
1297 $c->engine->finalize_headers( $c, @_ );
1300 $c->response->{_finalized_headers} = 1;
1303 =head2 $c->finalize_output
1305 An alias for finalize_body.
1307 =head2 $c->finalize_read
1309 Finalizes the input after reading is complete.
1313 sub finalize_read { my $c = shift; $c->engine->finalize_read( $c, @_ ) }
1315 =head2 $c->finalize_uploads
1317 Finalizes uploads. Cleans up any temporary files.
1321 sub finalize_uploads { my $c = shift; $c->engine->finalize_uploads( $c, @_ ) }
1323 =head2 $c->get_action( $action, $namespace )
1325 Gets an action in a given namespace.
1329 sub get_action { my $c = shift; $c->dispatcher->get_action(@_) }
1331 =head2 $c->get_actions( $action, $namespace )
1333 Gets all actions of a given name in a namespace and all parent
1338 sub get_actions { my $c = shift; $c->dispatcher->get_actions( $c, @_ ) }
1340 =head2 $c->handle_request( $class, @arguments )
1342 Called to handle each HTTP request.
1346 sub handle_request {
1347 my ( $class, @arguments ) = @_;
1349 # Always expect worst case!
1352 my $stats = ( $class->debug ) ? Tree::Simple->new: q{};
1355 my $c = $class->prepare(@arguments);
1356 $c->{stats} = $stats;
1358 return $c->finalize;
1361 if ( $class->debug ) {
1362 my $start = [gettimeofday];
1363 $status = &$handler;
1364 my $elapsed = tv_interval $start;
1365 $elapsed = sprintf '%f', $elapsed;
1366 my $av = sprintf '%.3f',
1367 ( $elapsed == 0 ? '??' : ( 1 / $elapsed ) );
1368 my $t = Text::SimpleTable->new( [ 64, 'Action' ], [ 9, 'Time' ] );
1373 my $stat = $action->getNodeValue;
1374 $t->row( ( q{ } x $action->getDepth ) . $stat->{action},
1375 $stat->{elapsed} || '??' );
1380 "Request took ${elapsed}s ($av/s)\n" . $t->draw );
1382 else { $status = &$handler }
1386 if ( my $error = $@ ) {
1388 $class->log->error(qq/Caught exception in engine "$error"/);
1392 $class->log->_flush() if $class->log->can('_flush');
1396 =head2 $c->prepare( @arguments )
1398 Creates a Catalyst context from an engine-specific request (Apache, CGI,
1404 my ( $class, @arguments ) = @_;
1406 $class->context_class( ref $class || $class ) unless $class->context_class;
1407 my $c = $class->context_class->new(
1411 request => $class->request_class->new(
1414 body_parameters => {},
1416 headers => HTTP::Headers->new,
1418 query_parameters => {},
1424 response => $class->response_class->new(
1428 headers => HTTP::Headers->new(),
1437 # For on-demand data
1438 $c->request->{_context} = $c;
1439 $c->response->{_context} = $c;
1440 weaken( $c->request->{_context} );
1441 weaken( $c->response->{_context} );
1444 my $secs = time - $START || 1;
1445 my $av = sprintf '%.3f', $COUNT / $secs;
1446 $c->log->debug('**********************************');
1447 $c->log->debug("* Request $COUNT ($av/s) [$$]");
1448 $c->log->debug('**********************************');
1449 $c->res->headers->header( 'X-Catalyst' => $Catalyst::VERSION );
1452 # Allow engine to direct the prepare flow (for POE)
1453 if ( $c->engine->can('prepare') ) {
1454 $c->engine->prepare( $c, @arguments );
1457 $c->prepare_request(@arguments);
1458 $c->prepare_connection;
1459 $c->prepare_query_parameters;
1460 $c->prepare_headers;
1461 $c->prepare_cookies;
1465 $c->prepare_body unless $c->config->{parse_on_demand};
1468 my $method = $c->req->method || '';
1469 my $path = $c->req->path || '';
1470 my $address = $c->req->address || '';
1472 $c->log->debug(qq/"$method" request for "$path" from "$address"/)
1480 =head2 $c->prepare_action
1486 sub prepare_action { my $c = shift; $c->dispatcher->prepare_action( $c, @_ ) }
1488 =head2 $c->prepare_body
1490 Prepares message body.
1497 # Do we run for the first time?
1498 return if defined $c->request->{_body};
1500 # Initialize on-demand data
1501 $c->engine->prepare_body( $c, @_ );
1502 $c->prepare_parameters;
1503 $c->prepare_uploads;
1505 if ( $c->debug && keys %{ $c->req->body_parameters } ) {
1506 my $t = Text::SimpleTable->new( [ 37, 'Key' ], [ 36, 'Value' ] );
1507 for my $key ( sort keys %{ $c->req->body_parameters } ) {
1508 my $param = $c->req->body_parameters->{$key};
1509 my $value = defined($param) ? $param : '';
1511 ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
1513 $c->log->debug( "Body Parameters are:\n" . $t->draw );
1517 =head2 $c->prepare_body_chunk( $chunk )
1519 Prepares a chunk of data before sending it to L<HTTP::Body>.
1523 sub prepare_body_chunk {
1525 $c->engine->prepare_body_chunk( $c, @_ );
1528 =head2 $c->prepare_body_parameters
1530 Prepares body parameters.
1534 sub prepare_body_parameters {
1536 $c->engine->prepare_body_parameters( $c, @_ );
1539 =head2 $c->prepare_connection
1541 Prepares connection.
1545 sub prepare_connection {
1547 $c->engine->prepare_connection( $c, @_ );
1550 =head2 $c->prepare_cookies
1556 sub prepare_cookies { my $c = shift; $c->engine->prepare_cookies( $c, @_ ) }
1558 =head2 $c->prepare_headers
1564 sub prepare_headers { my $c = shift; $c->engine->prepare_headers( $c, @_ ) }
1566 =head2 $c->prepare_parameters
1568 Prepares parameters.
1572 sub prepare_parameters {
1574 $c->prepare_body_parameters;
1575 $c->engine->prepare_parameters( $c, @_ );
1578 =head2 $c->prepare_path
1580 Prepares path and base.
1584 sub prepare_path { my $c = shift; $c->engine->prepare_path( $c, @_ ) }
1586 =head2 $c->prepare_query_parameters
1588 Prepares query parameters.
1592 sub prepare_query_parameters {
1595 $c->engine->prepare_query_parameters( $c, @_ );
1597 if ( $c->debug && keys %{ $c->request->query_parameters } ) {
1598 my $t = Text::SimpleTable->new( [ 37, 'Key' ], [ 36, 'Value' ] );
1599 for my $key ( sort keys %{ $c->req->query_parameters } ) {
1600 my $param = $c->req->query_parameters->{$key};
1601 my $value = defined($param) ? $param : '';
1603 ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
1605 $c->log->debug( "Query Parameters are:\n" . $t->draw );
1609 =head2 $c->prepare_read
1611 Prepares the input for reading.
1615 sub prepare_read { my $c = shift; $c->engine->prepare_read( $c, @_ ) }
1617 =head2 $c->prepare_request
1619 Prepares the engine request.
1623 sub prepare_request { my $c = shift; $c->engine->prepare_request( $c, @_ ) }
1625 =head2 $c->prepare_uploads
1631 sub prepare_uploads {
1634 $c->engine->prepare_uploads( $c, @_ );
1636 if ( $c->debug && keys %{ $c->request->uploads } ) {
1637 my $t = Text::SimpleTable->new(
1643 for my $key ( sort keys %{ $c->request->uploads } ) {
1644 my $upload = $c->request->uploads->{$key};
1645 for my $u ( ref $upload eq 'ARRAY' ? @{$upload} : ($upload) ) {
1646 $t->row( $key, $u->filename, $u->type, $u->size );
1649 $c->log->debug( "File Uploads are:\n" . $t->draw );
1653 =head2 $c->prepare_write
1655 Prepares the output for writing.
1659 sub prepare_write { my $c = shift; $c->engine->prepare_write( $c, @_ ) }
1661 =head2 $c->request_class
1663 Returns or sets the request class.
1665 =head2 $c->response_class
1667 Returns or sets the response class.
1669 =head2 $c->read( [$maxlength] )
1671 Reads a chunk of data from the request body. This method is designed to
1672 be used in a while loop, reading C<$maxlength> bytes on every call.
1673 C<$maxlength> defaults to the size of the request if not specified.
1675 You have to set C<MyApp-E<gt>config-E<gt>{parse_on_demand}> to use this
1680 sub read { my $c = shift; return $c->engine->read( $c, @_ ) }
1688 sub run { my $c = shift; return $c->engine->run( $c, @_ ) }
1690 =head2 $c->set_action( $action, $code, $namespace, $attrs )
1692 Sets an action in a given namespace.
1696 sub set_action { my $c = shift; $c->dispatcher->set_action( $c, @_ ) }
1698 =head2 $c->setup_actions($component)
1700 Sets up actions for a component.
1704 sub setup_actions { my $c = shift; $c->dispatcher->setup_actions( $c, @_ ) }
1706 =head2 $c->setup_components
1712 sub setup_components {
1715 my $callback = sub {
1716 my ( $component, $context ) = @_;
1718 unless ( $component->can('COMPONENT') ) {
1722 my $suffix = Catalyst::Utils::class2classsuffix($component);
1723 my $config = $class->config->{$suffix} || {};
1727 eval { $instance = $component->COMPONENT( $context, $config ); };
1729 if ( my $error = $@ ) {
1733 Catalyst::Exception->throw( message =>
1734 qq/Couldn't instantiate component "$component", "$error"/ );
1737 Catalyst::Exception->throw( message =>
1738 qq/Couldn't instantiate component "$component", "COMPONENT() didn't return a object"/
1740 unless ref $instance;
1744 eval "package $class;\n" . q!Module::Pluggable::Fast->import(
1745 name => '_catalyst_components',
1747 "$class\::Controller", "$class\::C",
1748 "$class\::Model", "$class\::M",
1749 "$class\::View", "$class\::V"
1751 callback => $callback
1755 if ( my $error = $@ ) {
1759 Catalyst::Exception->throw(
1760 message => qq/Couldn't load components "$error"/ );
1763 for my $component ( $class->_catalyst_components($class) ) {
1764 $class->components->{ ref $component || $component } = $component;
1768 =head2 $c->setup_dispatcher
1774 sub setup_dispatcher {
1775 my ( $class, $dispatcher ) = @_;
1778 $dispatcher = 'Catalyst::Dispatcher::' . $dispatcher;
1781 if ( $ENV{CATALYST_DISPATCHER} ) {
1782 $dispatcher = 'Catalyst::Dispatcher::' . $ENV{CATALYST_DISPATCHER};
1785 if ( $ENV{ uc($class) . '_DISPATCHER' } ) {
1787 'Catalyst::Dispatcher::' . $ENV{ uc($class) . '_DISPATCHER' };
1790 unless ($dispatcher) {
1791 $dispatcher = $class->dispatcher_class;
1794 $dispatcher->require;
1797 Catalyst::Exception->throw(
1798 message => qq/Couldn't load dispatcher "$dispatcher", "$@"/ );
1801 # dispatcher instance
1802 $class->dispatcher( $dispatcher->new );
1805 =head2 $c->setup_engine
1812 my ( $class, $engine ) = @_;
1815 $engine = 'Catalyst::Engine::' . $engine;
1818 if ( $ENV{CATALYST_ENGINE} ) {
1819 $engine = 'Catalyst::Engine::' . $ENV{CATALYST_ENGINE};
1822 if ( $ENV{ uc($class) . '_ENGINE' } ) {
1823 $engine = 'Catalyst::Engine::' . $ENV{ uc($class) . '_ENGINE' };
1826 if ( $ENV{MOD_PERL} ) {
1828 # create the apache method
1831 *{"$class\::apache"} = sub { shift->engine->apache };
1834 my ( $software, $version ) =
1835 $ENV{MOD_PERL} =~ /^(\S+)\/(\d+(?:[\.\_]\d+)+)/;
1838 $version =~ s/(\.[^.]+)\./$1/g;
1840 if ( $software eq 'mod_perl' ) {
1844 if ( $version >= 1.99922 ) {
1845 $engine = 'Catalyst::Engine::Apache2::MP20';
1848 elsif ( $version >= 1.9901 ) {
1849 $engine = 'Catalyst::Engine::Apache2::MP19';
1852 elsif ( $version >= 1.24 ) {
1853 $engine = 'Catalyst::Engine::Apache::MP13';
1857 Catalyst::Exception->throw( message =>
1858 qq/Unsupported mod_perl version: $ENV{MOD_PERL}/ );
1863 # install the correct mod_perl handler
1864 if ( $version >= 1.9901 ) {
1865 *handler = sub : method {
1866 shift->handle_request(@_);
1870 *handler = sub ($$) { shift->handle_request(@_) };
1875 elsif ( $software eq 'Zeus-Perl' ) {
1876 $engine = 'Catalyst::Engine::Zeus';
1880 Catalyst::Exception->throw(
1881 message => qq/Unsupported mod_perl: $ENV{MOD_PERL}/ );
1886 $engine = $class->engine_class;
1892 Catalyst::Exception->throw( message =>
1893 qq/Couldn't load engine "$engine" (maybe you forgot to install it?), "$@"/
1897 # check for old engines that are no longer compatible
1899 if ( $engine->isa('Catalyst::Engine::Apache')
1900 && !Catalyst::Engine::Apache->VERSION )
1905 elsif ( $engine->isa('Catalyst::Engine::Server::Base')
1906 && Catalyst::Engine::Server->VERSION le '0.02' )
1911 elsif ($engine->isa('Catalyst::Engine::HTTP::POE')
1912 && $engine->VERSION eq '0.01' )
1917 elsif ($engine->isa('Catalyst::Engine::Zeus')
1918 && $engine->VERSION eq '0.01' )
1924 Catalyst::Exception->throw( message =>
1925 qq/Engine "$engine" is not supported by this version of Catalyst/
1930 $class->engine( $engine->new );
1933 =head2 $c->setup_home
1935 Sets up the home directory.
1940 my ( $class, $home ) = @_;
1942 if ( $ENV{CATALYST_HOME} ) {
1943 $home = $ENV{CATALYST_HOME};
1946 if ( $ENV{ uc($class) . '_HOME' } ) {
1947 $home = $ENV{ uc($class) . '_HOME' };
1951 $home = Catalyst::Utils::home($class);
1955 $class->config->{home} ||= $home;
1956 $class->config->{root} ||= Path::Class::Dir->new($home)->subdir('root');
1960 =head2 $c->setup_log
1967 my ( $class, $debug ) = @_;
1969 unless ( $class->log ) {
1970 $class->log( Catalyst::Log->new );
1973 my $app_flag = Catalyst::Utils::class2env($class) . '_DEBUG';
1976 ( defined( $ENV{CATALYST_DEBUG} ) || defined( $ENV{$app_flag} ) )
1977 ? ( $ENV{CATALYST_DEBUG} || $ENV{$app_flag} )
1982 *{"$class\::debug"} = sub { 1 };
1983 $class->log->debug('Debug messages enabled');
1987 =head2 $c->setup_plugins
1993 =head2 $c->registered_plugins
1995 Returns a sorted list of the plugins which have either been stated in the
1996 import list or which have been added via C<< MyApp->plugin(@args); >>.
1998 If passed a given plugin name, it will report a boolean value indicating
1999 whether or not that plugin is loaded. A fully qualified name is required if
2000 the plugin name does not begin with C<Catalyst::Plugin::>.
2002 if ($c->registered_plugins('Some::Plugin')) {
2010 sub registered_plugins {
2012 return sort keys %{ $proto->_plugins } unless @_;
2014 return 1 if exists $proto->_plugins->{$plugin};
2015 return exists $proto->_plugins->{"Catalyst::Plugin::$plugin"};
2018 sub _register_plugin {
2019 my ( $proto, $plugin, $instant ) = @_;
2020 my $class = ref $proto || $proto;
2024 if ( my $error = $@ ) {
2025 my $type = $instant ? "instant " : '';
2026 Catalyst::Exception->throw(
2027 message => qq/Couldn't load ${type}plugin "$plugin", $error/ );
2030 $proto->_plugins->{$plugin} = 1;
2033 unshift @{"$class\::ISA"}, $plugin;
2039 my ( $class, $plugins ) = @_;
2041 $class->_plugins( {} ) unless $class->_plugins;
2043 for my $plugin ( reverse @$plugins ) {
2045 unless ( $plugin =~ s/\A\+// ) {
2046 $plugin = "Catalyst::Plugin::$plugin";
2049 $class->_register_plugin($plugin);
2056 Returns an arrayref of the internal execution stack (actions that are currently
2059 =head2 $c->write( $data )
2061 Writes $data to the output stream. When using this method directly, you
2062 will need to manually set the C<Content-Length> header to the length of
2063 your output data, if known.
2070 # Finalize headers if someone manually writes output
2071 $c->finalize_headers;
2073 return $c->engine->write( $c, @_ );
2078 Returns the Catalyst version number. Mostly useful for "powered by"
2079 messages in template systems.
2083 sub version { return $Catalyst::VERSION }
2085 =head1 INTERNAL ACTIONS
2087 Catalyst uses internal actions like C<_DISPATCH>, C<_BEGIN>, C<_AUTO>,
2088 C<_ACTION>, and C<_END>. These are by default not shown in the private
2089 action table, but you can make them visible with a config parameter.
2091 MyApp->config->{show_internal_actions} = 1;
2093 =head1 CASE SENSITIVITY
2095 By default Catalyst is not case sensitive, so C<MyApp::C::FOO::Bar> is
2096 mapped to C</foo/bar>. You can activate case sensitivity with a config
2099 MyApp->config->{case_sensitive} = 1;
2101 This causes C<MyApp::C::Foo::Bar> to map to C</Foo/Bar>.
2103 =head1 ON-DEMAND PARSER
2105 The request body is usually parsed at the beginning of a request,
2106 but if you want to handle input yourself or speed things up a bit,
2107 you can enable on-demand parsing with a config parameter.
2109 MyApp->config->{parse_on_demand} = 1;
2111 =head1 PROXY SUPPORT
2113 Many production servers operate using the common double-server approach,
2114 with a lightweight frontend web server passing requests to a larger
2115 backend server. An application running on the backend server must deal
2116 with two problems: the remote user always appears to be C<127.0.0.1> and
2117 the server's hostname will appear to be C<localhost> regardless of the
2118 virtual host that the user connected through.
2120 Catalyst will automatically detect this situation when you are running
2121 the frontend and backend servers on the same machine. The following
2122 changes are made to the request.
2124 $c->req->address is set to the user's real IP address, as read from
2125 the HTTP X-Forwarded-For header.
2127 The host value for $c->req->base and $c->req->uri is set to the real
2128 host, as read from the HTTP X-Forwarded-Host header.
2130 Obviously, your web server must support these headers for this to work.
2132 In a more complex server farm environment where you may have your
2133 frontend proxy server(s) on different machines, you will need to set a
2134 configuration option to tell Catalyst to read the proxied data from the
2137 MyApp->config->{using_frontend_proxy} = 1;
2139 If you do not wish to use the proxy support at all, you may set:
2141 MyApp->config->{ignore_frontend_proxy} = 1;
2143 =head1 THREAD SAFETY
2145 Catalyst has been tested under Apache 2's threading mpm_worker, mpm_winnt,
2146 and the standalone forking HTTP server on Windows. We believe the Catalyst
2147 core to be thread-safe.
2149 If you plan to operate in a threaded environment, remember that all other
2150 modules you are using must also be thread-safe. Some modules, most notably
2151 L<DBD::SQLite>, are not thread-safe.
2157 Join #catalyst on irc.perl.org.
2161 http://lists.rawmode.org/mailman/listinfo/catalyst
2162 http://lists.rawmode.org/mailman/listinfo/catalyst-dev
2166 http://catalyst.perl.org
2170 http://dev.catalyst.perl.org
2174 =head2 L<Task::Catalyst> - All you need to start with Catalyst
2176 =head2 L<Catalyst::Manual> - The Catalyst Manual
2178 =head2 L<Catalyst::Component>, L<Catalyst::Base> - Base classes for components
2180 =head2 L<Catalyst::Engine> - Core engine
2182 =head2 L<Catalyst::Log> - Log class.
2184 =head2 L<Catalyst::Request> - Request object
2186 =head2 L<Catalyst::Response> - Response object
2188 =head2 L<Catalyst::Test> - The test suite.
2260 Sebastian Riedel, C<sri@oook.de>
2264 This library is free software, you can redistribute it and/or modify it under
2265 the same terms as Perl itself.