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