Updated helper template
[catagits/Catalyst-Runtime.git] / lib / Catalyst.pm
CommitLineData
fc7ec1d9 1package Catalyst;
2
3use strict;
fbcc39ad 4use base 'Catalyst::Base';
5use bytes;
fc7ec1d9 6use UNIVERSAL::require;
a2f2cde9 7use Catalyst::Exception;
fc7ec1d9 8use Catalyst::Log;
fbcc39ad 9use Catalyst::Request;
10use Catalyst::Request::Upload;
11use Catalyst::Response;
812a28c9 12use Catalyst::Utils;
5d9a6d47 13use NEXT;
fbcc39ad 14use Text::ASCIITable;
4f6748f1 15use Path::Class;
fbcc39ad 16use Time::HiRes qw/gettimeofday tv_interval/;
17use URI;
18use Scalar::Util qw/weaken/;
fc7ec1d9 19
66e28e3f 20__PACKAGE__->mk_accessors(
21 qw/counter depth request response state action namespace/
22);
10dd6896 23
fbcc39ad 24# Laziness++
25*comp = \&component;
26*req = \&request;
27*res = \&response;
28
29# For backwards compatibility
30*finalize_output = \&finalize_body;
31
32# For statistics
33our $COUNT = 1;
34our $START = time;
35our $RECURSION = 1000;
36our $DETACH = "catalyst_detach\n";
37
38require Module::Pluggable::Fast;
39
40# Helper script generation
8e86b7f5 41our $CATALYST_SCRIPT_GEN = 10;
fbcc39ad 42
43__PACKAGE__->mk_classdata($_)
44 for qw/components arguments dispatcher engine log/;
45
bf88a181 46our $VERSION = '5.49_02';
189e2a51 47
fbcc39ad 48sub import {
49 my ( $class, @arguments ) = @_;
50
51 # We have to limit $class to Catalyst to avoid pushing Catalyst upon every
52 # callers @ISA.
53 return unless $class eq 'Catalyst';
54
55 my $caller = caller(0);
56
57 unless ( $caller->isa('Catalyst') ) {
58 no strict 'refs';
59 push @{"$caller\::ISA"}, $class;
60 }
61
62 $caller->arguments( [@arguments] );
63 $caller->setup_home;
64}
fc7ec1d9 65
66=head1 NAME
67
68Catalyst - The Elegant MVC Web Application Framework
69
70=head1 SYNOPSIS
71
72 # use the helper to start a new application
91864987 73 catalyst.pl MyApp
fc7ec1d9 74 cd MyApp
75
76 # add models, views, controllers
ae4e40a7 77 script/myapp_create.pl model Something
78 script/myapp_create.pl view Stuff
79 script/myapp_create.pl controller Yada
fc7ec1d9 80
81 # built in testserver
ae4e40a7 82 script/myapp_server.pl
fc7ec1d9 83
84 # command line interface
ae4e40a7 85 script/myapp_test.pl /yada
fc7ec1d9 86
87
fc7ec1d9 88 use Catalyst;
89
90 use Catalyst qw/My::Module My::OtherModule/;
91
92 use Catalyst '-Debug';
93
94 use Catalyst qw/-Debug -Engine=CGI/;
95
5a8ed4fe 96 sub default : Private { $_[1]->res->output('Hello') } );
97
e3dc9d78 98 sub index : Path('/index.html') {
5a8ed4fe 99 my ( $self, $c ) = @_;
100 $c->res->output('Hello');
064834ea 101 $c->forward('foo');
5a8ed4fe 102 }
103
064834ea 104 sub product : Regex('^product[_]*(\d*).html$') {
5a8ed4fe 105 my ( $self, $c ) = @_;
106 $c->stash->{template} = 'product.tt';
107 $c->stash->{product} = $c->req->snippets->[0];
108 }
fc7ec1d9 109
3803e98f 110See also L<Catalyst::Manual::Intro>
111
fc7ec1d9 112=head1 DESCRIPTION
113
fc7ec1d9 114The key concept of Catalyst is DRY (Don't Repeat Yourself).
115
116See L<Catalyst::Manual> for more documentation.
117
23f9d934 118Catalyst plugins can be loaded by naming them as arguments to the "use Catalyst" statement.
1985c30b 119Omit the C<Catalyst::Plugin::> prefix from the plugin name,
23f9d934 120so C<Catalyst::Plugin::My::Module> becomes C<My::Module>.
fc7ec1d9 121
122 use Catalyst 'My::Module';
123
26e73131 124Special flags like -Debug and -Engine can also be specified as arguments when
23f9d934 125Catalyst is loaded:
fc7ec1d9 126
127 use Catalyst qw/-Debug My::Module/;
128
23f9d934 129The position of plugins and flags in the chain is important, because they are
130loaded in exactly the order that they appear.
fc7ec1d9 131
23f9d934 132The following flags are supported:
133
134=over 4
135
136=item -Debug
137
138enables debug output, i.e.:
fc7ec1d9 139
140 use Catalyst '-Debug';
141
23f9d934 142this is equivalent to:
fc7ec1d9 143
144 use Catalyst;
145 sub debug { 1 }
146
fbcc39ad 147=item -Dispatcher
148
149Force Catalyst to use a specific dispatcher.
150
23f9d934 151=item -Engine
fc7ec1d9 152
153Force Catalyst to use a specific engine.
23f9d934 154Omit the C<Catalyst::Engine::> prefix of the engine name, i.e.:
fc7ec1d9 155
156 use Catalyst '-Engine=CGI';
157
fbcc39ad 158=item -Home
159
160Force Catalyst to use a specific home directory.
161
162=item -Log
163
164Specify log level.
165
23f9d934 166=back
fc7ec1d9 167
23f9d934 168=head1 METHODS
169
170=over 4
171
66e28e3f 172=item $c->action
173
174Accessor for the current action
175
fbcc39ad 176=item $c->comp($name)
177
178=item $c->component($name)
179
180Get a component object by name.
181
182 $c->comp('MyApp::Model::MyModel')->do_stuff;
183
184=cut
185
186sub component {
187 my $c = shift;
188
189 if (@_) {
190
191 my $name = shift;
192
193 my $appclass = ref $c || $c;
194
195 my @names = (
196 $name, "${appclass}::${name}",
197 map { "${appclass}::${_}::${name}" } qw/M V C/
198 );
199
200 foreach my $try (@names) {
201
202 if ( exists $c->components->{$try} ) {
203
204 return $c->components->{$try};
205 }
206 }
207
208 foreach my $component ( keys %{ $c->components } ) {
209
210 return $c->components->{$component} if $component =~ /$name/i;
211 }
212
213 }
214
215 return sort keys %{ $c->components };
216}
217
218=item config
219
220Returns a hashref containing your applications settings.
221
23f9d934 222=item debug
fc7ec1d9 223
224Overload to enable debug messages.
225
226=cut
227
228sub debug { 0 }
229
fbcc39ad 230=item $c->detach( $command [, \@arguments ] )
fc7ec1d9 231
fbcc39ad 232Like C<forward> but doesn't return.
fc7ec1d9 233
234=cut
235
fbcc39ad 236sub detach { my $c = shift; $c->dispatcher->detach( $c, @_ ) }
237
238=item $c->dispatcher
239
240Contains the dispatcher instance.
241Stringifies to class.
242
243=item $c->forward( $command [, \@arguments ] )
244
245Forward processing to a private action or a method from a class.
246If you define a class without method it will default to process().
247also takes an optional arrayref containing arguments to be passed
248to the new function. $c->req->args will be reset upon returning
249from the function.
250
251 $c->forward('/foo');
252 $c->forward('index');
253 $c->forward(qw/MyApp::Model::CDBI::Foo do_stuff/);
254 $c->forward('MyApp::View::TT');
255
256=cut
257
258sub forward { my $c = shift; $c->dispatcher->forward( $c, @_ ) }
259
66e28e3f 260=item $c->namespace
261
262Accessor to the namespace of the current action
263
01033d73 264=item $c->path_to(@path)
265
266Merges C<@path> with $c->config->{home} and returns a L<Path::Class> object.
267
268For example:
269
270 $c->path_to( 'db', 'sqlite.db' );
271
272=cut
273
274sub path_to {
275 my ( $c, @path ) = @_;
276 my $path = dir( $c->config->{home}, @path );
277 if ( -d $path ) { return $path }
278 else { return file( $c->config->{home}, @path ) }
279}
280
fbcc39ad 281=item $c->setup
282
283Setup.
284
285 $c->setup;
286
287=cut
288
289sub setup {
0319a12c 290 my ( $class, @arguments ) = @_;
599b5295 291
fbcc39ad 292 unless ( $class->isa('Catalyst') ) {
953b0e15 293
fbcc39ad 294 Catalyst::Exception->throw(
295 message => qq/'$class' does not inherit from Catalyst/ );
1c99e125 296 }
0319a12c 297
fbcc39ad 298 if ( $class->arguments ) {
299 @arguments = ( @arguments, @{ $class->arguments } );
300 }
301
302 # Process options
303 my $flags = {};
304
305 foreach (@arguments) {
306
307 if (/^-Debug$/) {
308 $flags->{log} =
309 ( $flags->{log} ) ? 'debug,' . $flags->{log} : 'debug';
310 }
311 elsif (/^-(\w+)=?(.*)$/) {
312 $flags->{ lc $1 } = $2;
313 }
314 else {
315 push @{ $flags->{plugins} }, $_;
316 }
317 }
318
319 $class->setup_log( delete $flags->{log} );
320 $class->setup_plugins( delete $flags->{plugins} );
321 $class->setup_dispatcher( delete $flags->{dispatcher} );
322 $class->setup_engine( delete $flags->{engine} );
323 $class->setup_home( delete $flags->{home} );
324
325 for my $flag ( sort keys %{$flags} ) {
326
327 if ( my $code = $class->can( 'setup_' . $flag ) ) {
328 &$code( $class, delete $flags->{$flag} );
329 }
330 else {
331 $class->log->warn(qq/Unknown flag "$flag"/);
332 }
333 }
334
335 $class->log->warn( "You are running an old helper script! "
336 . "Please update your scripts by regenerating the "
337 . "application and copying over the new scripts." )
338 if ( $ENV{CATALYST_SCRIPT_GEN}
339 && ( $ENV{CATALYST_SCRIPT_GEN} < $Catalyst::CATALYST_SCRIPT_GEN ) );
340
341 if ( $class->debug ) {
342
343 my @plugins = ();
344
345 {
346 no strict 'refs';
347 @plugins = grep { /^Catalyst::Plugin/ } @{"$class\::ISA"};
348 }
349
350 if (@plugins) {
351 my $t = Text::ASCIITable->new;
352 $t->setOptions( 'hide_HeadRow', 1 );
353 $t->setOptions( 'hide_HeadLine', 1 );
354 $t->setCols('Class');
355 $t->setColWidth( 'Class', 75, 1 );
356 $t->addRow($_) for @plugins;
357 $class->log->debug( "Loaded plugins:\n" . $t->draw );
358 }
359
360 my $dispatcher = $class->dispatcher;
361 my $engine = $class->engine;
362 my $home = $class->config->{home};
363
364 $class->log->debug(qq/Loaded dispatcher "$dispatcher"/);
365 $class->log->debug(qq/Loaded engine "$engine"/);
366
367 $home
368 ? ( -d $home )
369 ? $class->log->debug(qq/Found home "$home"/)
370 : $class->log->debug(qq/Home "$home" doesn't exist/)
371 : $class->log->debug(q/Couldn't find home/);
372 }
373
374 # Call plugins setup
375 {
376 no warnings qw/redefine/;
377 local *setup = sub { };
378 $class->setup;
379 }
380
381 # Initialize our data structure
382 $class->components( {} );
383
384 $class->setup_components;
385
386 if ( $class->debug ) {
387 my $t = Text::ASCIITable->new;
388 $t->setOptions( 'hide_HeadRow', 1 );
389 $t->setOptions( 'hide_HeadLine', 1 );
390 $t->setCols('Class');
391 $t->setColWidth( 'Class', 75, 1 );
392 $t->addRow($_) for sort keys %{ $class->components };
393 $class->log->debug( "Loaded components:\n" . $t->draw )
394 if ( @{ $t->{tbl_rows} } );
395 }
396
397 # Add our self to components, since we are also a component
398 $class->components->{$class} = $class;
399
400 $class->setup_actions;
401
402 if ( $class->debug ) {
403 my $name = $class->config->{name} || 'Application';
404 $class->log->info("$name powered by Catalyst $Catalyst::VERSION");
405 }
406 $class->log->_flush() if $class->log->can('_flush');
407}
408
189e2a51 409=item $c->uri_for($path,[@args])
fbcc39ad 410
411Merges path with $c->request->base for absolute uri's and with
412$c->request->match for relative uri's, then returns a normalized
189e2a51 413L<URI> object. If any args are passed, they are added at the end
414of the path.
fbcc39ad 415
416=cut
417
418sub uri_for {
00e6a2b7 419 my ( $c, $path, @args ) = @_;
fbcc39ad 420 my $base = $c->request->base->clone;
421 my $basepath = $base->path;
422 $basepath =~ s/\/$//;
fdba7a9d 423 $basepath .= '/';
fbcc39ad 424 my $match = $c->request->match;
00e6a2b7 425
189e2a51 426 # massage match, empty if absolute path
fbcc39ad 427 $match =~ s/^\///;
428 $match .= '/' if $match;
6e0c45c9 429 $path ||= '';
fbcc39ad 430 $match = '' if $path =~ /^\//;
431 $path =~ s/^\///;
00e6a2b7 432
189e2a51 433 # join args with '/', or a blank string
00e6a2b7 434 my $args = ( scalar @args ? '/' . join( '/', @args ) : '' );
435 return URI->new_abs( URI->new_abs( "$path$args", "$basepath$match" ),
436 $base )->canonical;
fbcc39ad 437}
438
439=item $c->error
440
441=item $c->error($error, ...)
442
443=item $c->error($arrayref)
444
445Returns an arrayref containing error messages.
446
447 my @error = @{ $c->error };
448
449Add a new error.
450
451 $c->error('Something bad happened');
452
00e6a2b7 453Clean errors.
454
455 $c->error(0);
456
fbcc39ad 457=cut
458
459sub error {
460 my $c = shift;
00e6a2b7 461 if ( $_[0] ) {
462 my $error = ref $_[0] eq 'ARRAY' ? $_[0] : [@_];
463 push @{ $c->{error} }, @$error;
464 }
465 elsif ( defined $_[0] ) { $c->{error} = undef }
466 return $c->{error} || [];
0319a12c 467}
468
469=item $c->engine
470
fbcc39ad 471Contains the engine instance.
472Stringifies to the class.
fc7ec1d9 473
0319a12c 474=item $c->log
475
476Contains the logging object. Unless it is already set Catalyst sets this up with a
477C<Catalyst::Log> object. To use your own log class:
478
479 $c->log( MyLogger->new );
480 $c->log->info("now logging with my own logger!");
481
482Your log class should implement the methods described in the C<Catalyst::Log>
483man page.
484
485=item $c->plugin( $name, $class, @args )
486
487Instant plugins for Catalyst.
488Classdata accessor/mutator will be created, class loaded and instantiated.
489
490 MyApp->plugin( 'prototype', 'HTML::Prototype' );
491
492 $c->prototype->define_javascript_functions;
493
494=cut
495
496sub plugin {
497 my ( $class, $name, $plugin, @args ) = @_;
498 $plugin->require;
499
500 if ( my $error = $UNIVERSAL::require::ERROR ) {
501 Catalyst::Exception->throw(
fbcc39ad 502 message => qq/Couldn't load instant plugin "$plugin", "$error"/ );
0319a12c 503 }
504
505 eval { $plugin->import };
506 $class->mk_classdata($name);
507 my $obj;
508 eval { $obj = $plugin->new(@args) };
509
fbcc39ad 510 if ($@) {
511 Catalyst::Exception->throw( message =>
512 qq/Couldn't instantiate instant plugin "$plugin", "$@"/ );
0319a12c 513 }
514
515 $class->$name($obj);
516 $class->log->debug(qq/Initialized instant plugin "$plugin" as "$name"/)
517 if $class->debug;
518}
519
fbcc39ad 520=item $c->request
521
522=item $c->req
523
524Returns a C<Catalyst::Request> object.
525
526 my $req = $c->req;
527
528=item $c->response
529
530=item $c->res
531
532Returns a C<Catalyst::Response> object.
533
534 my $res = $c->res;
535
536=item $c->state
537
538Contains the return value of the last executed action.
539
540=item $c->stash
541
542Returns a hashref containing all your data.
543
fbcc39ad 544 print $c->stash->{foo};
545
23eb3f51 546Keys may be set in the stash by assigning to the hash reference, or by passing
547either a single hash reference or a list of key/value pairs as arguments.
548
549For example:
550
551 $c->stash->{foo} ||= 'yada';
552 $c->stash( { moose => 'majestic', qux => 0 } );
553 $c->stash( bar => 1, gorch => 2 );
554
fbcc39ad 555=cut
556
557sub stash {
558 my $c = shift;
559 if (@_) {
560 my $stash = @_ > 1 ? {@_} : $_[0];
561 while ( my ( $key, $val ) = each %$stash ) {
562 $c->{stash}->{$key} = $val;
563 }
564 }
565 return $c->{stash};
566}
567
2c63fc07 568=item $c->welcome_message
ab2374d3 569
570Returns the Catalyst welcome HTML page.
571
572=cut
573
574sub welcome_message {
bf1f2c60 575 my $c = shift;
576 my $name = $c->config->{name};
577 my $logo = $c->uri_for('/static/images/catalyst_logo.png');
578 my $prefix = Catalyst::Utils::appprefix( ref $c );
80cdbbff 579 $c->response->content_type('text/html; charset=utf-8');
ab2374d3 580 return <<"EOF";
80cdbbff 581<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
582 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
583<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
ab2374d3 584 <head>
80cdbbff 585 <meta http-equiv="Content-Language" content="en" />
586 <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
ab2374d3 587 <title>$name on Catalyst $VERSION</title>
588 <style type="text/css">
589 body {
ab2374d3 590 color: #000;
591 background-color: #eee;
592 }
593 div#content {
594 width: 640px;
80cdbbff 595 margin-left: auto;
596 margin-right: auto;
ab2374d3 597 margin-top: 10px;
598 margin-bottom: 10px;
599 text-align: left;
600 background-color: #ccc;
601 border: 1px solid #aaa;
602 -moz-border-radius: 10px;
603 }
d84c4dab 604 p, h1, h2 {
ab2374d3 605 margin-left: 20px;
606 margin-right: 20px;
16215972 607 font-family: verdana, tahoma, sans-serif;
ab2374d3 608 }
d84c4dab 609 a {
610 font-family: verdana, tahoma, sans-serif;
611 }
d114e033 612 :link, :visited {
613 text-decoration: none;
614 color: #b00;
615 border-bottom: 1px dotted #bbb;
616 }
617 :link:hover, :visited:hover {
d114e033 618 color: #555;
619 }
ab2374d3 620 div#topbar {
621 margin: 0px;
622 }
3e82a295 623 pre {
3e82a295 624 margin: 10px;
625 padding: 8px;
626 }
ab2374d3 627 div#answers {
628 padding: 8px;
629 margin: 10px;
d114e033 630 background-color: #fff;
ab2374d3 631 border: 1px solid #aaa;
632 -moz-border-radius: 10px;
633 }
634 h1 {
33108eaf 635 font-size: 0.9em;
636 font-weight: normal;
ab2374d3 637 text-align: center;
638 }
639 h2 {
640 font-size: 1.0em;
641 }
642 p {
643 font-size: 0.9em;
644 }
ae7c5252 645 p img {
646 float: right;
647 margin-left: 10px;
648 }
9619f23c 649 span#appname {
650 font-weight: bold;
33108eaf 651 font-size: 1.6em;
ab2374d3 652 }
653 </style>
654 </head>
655 <body>
656 <div id="content">
657 <div id="topbar">
9619f23c 658 <h1><span id="appname">$name</span> on <a href="http://catalyst.perl.org">Catalyst</a>
d84c4dab 659 $VERSION</h1>
ab2374d3 660 </div>
661 <div id="answers">
ae7c5252 662 <p>
80cdbbff 663 <img src="$logo" alt="Catalyst Logo" />
ae7c5252 664 </p>
4b8cb778 665 <p>Welcome to the wonderful world of Catalyst.
f92fd545 666 This <a href="http://en.wikipedia.org/wiki/MVC">MVC</a>
667 framework will make web development something you had
668 never expected it to be: Fun, rewarding and quick.</p>
ab2374d3 669 <h2>What to do now?</h2>
4b8cb778 670 <p>That really depends on what <b>you</b> want to do.
ab2374d3 671 We do, however, provide you with a few starting points.</p>
672 <p>If you want to jump right into web development with Catalyst
5db7f9a1 673 you might want to check out the documentation.</p>
bf1f2c60 674 <pre><code>perldoc <a href="http://cpansearch.perl.org/dist/Catalyst/lib/Catalyst/Manual/Intro.pod">Catalyst::Manual::Intro</a>
675perldoc <a href="http://cpansearch.perl.org/dist/Catalyst/lib/Catalyst/Manual.pod">Catalyst::Manual</a></code></pre>
ab2374d3 676 <h2>What to do next?</h2>
f5681c92 677 <p>Next it's time to write an actual application. Use the
80cdbbff 678 helper scripts to generate <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AController%3A%3A&amp;mode=all">controllers</a>,
679 <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AModel%3A%3A&amp;mode=all">models</a> and
680 <a href="http://cpansearch.perl.org/search?query=Catalyst%3A%3AView%3A%3A&amp;mode=all">views</a>,
bf1f2c60 681 they can save you a lot of work.</p>
682 <pre><code>script/${prefix}_create.pl -help</code></pre>
683 <p>Also, be sure to check out the vast and growing
80cdbbff 684 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 685 you are likely to find what you need there.
f5681c92 686 </p>
687
82245cc4 688 <h2>Need help?</h2>
f5681c92 689 <p>Catalyst has a very active community. Here are the main places to
690 get in touch with us.</p>
16215972 691 <ul>
692 <li>
2b9a7d76 693 <a href="http://dev.catalyst.perl.org">Wiki</a>
16215972 694 </li>
695 <li>
696 <a href="http://lists.rawmode.org/mailman/listinfo/catalyst">Mailing-List</a>
697 </li>
698 <li>
ea7cd80d 699 <a href="irc://irc.perl.org/catalyst">IRC channel #catalyst on irc.perl.org</a>
16215972 700 </li>
701 </ul>
ab2374d3 702 <h2>In conclusion</h2>
4e7aa2ea 703 <p>The Catalyst team hopes you will enjoy using Catalyst as much
f5681c92 704 as we enjoyed making it. Please contact us if you have ideas
705 for improvement or other feedback.</p>
ab2374d3 706 </div>
707 </div>
708 </body>
709</html>
710EOF
711}
712
fbcc39ad 713=back
714
715=head1 INTERNAL METHODS
716
717=over 4
718
719=item $c->benchmark($coderef)
720
721Takes a coderef with arguments and returns elapsed time as float.
722
723 my ( $elapsed, $status ) = $c->benchmark( sub { return 1 } );
724 $c->log->info( sprintf "Processing took %f seconds", $elapsed );
725
726=cut
727
728sub benchmark {
729 my $c = shift;
730 my $code = shift;
731 my $time = [gettimeofday];
732 my @return = &$code(@_);
733 my $elapsed = tv_interval $time;
734 return wantarray ? ( $elapsed, @return ) : $elapsed;
735}
736
737=item $c->components
738
739Contains the components.
740
741=item $c->counter
742
743Returns a hashref containing coderefs and execution counts.
744(Needed for deep recursion detection)
745
746=item $c->depth
747
748Returns the actual forward depth.
749
750=item $c->dispatch
751
752Dispatch request to actions.
753
754=cut
755
756sub dispatch { my $c = shift; $c->dispatcher->dispatch( $c, @_ ) }
757
7f92deef 758=item dump_these
759
760Returns a list of 2-element array references (name, structure) pairs that will
761be dumped on the error page in debug mode.
762
763=cut
764
765sub dump_these {
766 my $c = shift;
767 [ Request => $c->req ], [ Response => $c->res ], [ Stash => $c->stash ],;
768}
769
fbcc39ad 770=item $c->execute($class, $coderef)
771
772Execute a coderef in given class and catch exceptions.
773Errors are available via $c->error.
774
775=cut
776
777sub execute {
778 my ( $c, $class, $code ) = @_;
779 $class = $c->components->{$class} || $class;
780 $c->state(0);
781 my $callsub = ( caller(1) )[3];
782
783 my $action = '';
784 if ( $c->debug ) {
785 $action = "$code";
786 $action = "/$action" unless $action =~ /\-\>/;
787 $c->counter->{"$code"}++;
788
789 if ( $c->counter->{"$code"} > $RECURSION ) {
790 my $error = qq/Deep recursion detected in "$action"/;
791 $c->log->error($error);
792 $c->error($error);
793 $c->state(0);
794 return $c->state;
795 }
796
797 $action = "-> $action" if $callsub =~ /forward$/;
798 }
799 $c->{depth}++;
800 eval {
00e6a2b7 801 if ( $c->debug )
802 {
fbcc39ad 803 my ( $elapsed, @state ) =
804 $c->benchmark( $code, $class, $c, @{ $c->req->args } );
0e7f5826 805 unless ( ( $code->name =~ /^_.*/ )
806 && ( !$c->config->{show_internal_actions} ) )
807 {
808 push @{ $c->{stats} }, [ $action, sprintf( '%fs', $elapsed ) ];
809 }
fbcc39ad 810 $c->state(@state);
811 }
7cfcfd27 812 else {
00e6a2b7 813 $c->state( &$code( $class, $c, @{ $c->req->args } ) || 0 );
7cfcfd27 814 }
fbcc39ad 815 };
816 $c->{depth}--;
817
818 if ( my $error = $@ ) {
819
820 if ( $error eq $DETACH ) { die $DETACH if $c->{depth} > 1 }
821 else {
822 unless ( ref $error ) {
823 chomp $error;
824 $error = qq/Caught exception "$error"/;
825 }
826
827 $c->log->error($error);
828 $c->error($error);
829 $c->state(0);
830 }
831 }
832 return $c->state;
833}
834
835=item $c->finalize
836
837Finalize request.
838
839=cut
840
841sub finalize {
842 my $c = shift;
843
844 $c->finalize_uploads;
845
846 # Error
847 if ( $#{ $c->error } >= 0 ) {
848 $c->finalize_error;
849 }
850
851 $c->finalize_headers;
852
853 # HEAD request
854 if ( $c->request->method eq 'HEAD' ) {
855 $c->response->body('');
856 }
857
858 $c->finalize_body;
859
860 return $c->response->status;
861}
862
863=item $c->finalize_body
864
865Finalize body.
866
867=cut
868
869sub finalize_body { my $c = shift; $c->engine->finalize_body( $c, @_ ) }
870
871=item $c->finalize_cookies
872
873Finalize cookies.
874
875=cut
876
877sub finalize_cookies { my $c = shift; $c->engine->finalize_cookies( $c, @_ ) }
878
879=item $c->finalize_error
880
881Finalize error.
882
883=cut
884
885sub finalize_error { my $c = shift; $c->engine->finalize_error( $c, @_ ) }
886
887=item $c->finalize_headers
888
889Finalize headers.
890
891=cut
892
893sub finalize_headers {
894 my $c = shift;
895
896 # Check if we already finalized headers
897 return if $c->response->{_finalized_headers};
898
899 # Handle redirects
900 if ( my $location = $c->response->redirect ) {
901 $c->log->debug(qq/Redirecting to "$location"/) if $c->debug;
902 $c->response->header( Location => $location );
903 }
904
905 # Content-Length
906 if ( $c->response->body && !$c->response->content_length ) {
907 $c->response->content_length( bytes::length( $c->response->body ) );
908 }
909
910 # Errors
911 if ( $c->response->status =~ /^(1\d\d|[23]04)$/ ) {
912 $c->response->headers->remove_header("Content-Length");
913 $c->response->body('');
914 }
915
916 $c->finalize_cookies;
917
918 $c->engine->finalize_headers( $c, @_ );
919
920 # Done
921 $c->response->{_finalized_headers} = 1;
922}
923
924=item $c->finalize_output
925
926An alias for finalize_body.
927
928=item $c->finalize_read
929
930Finalize the input after reading is complete.
931
932=cut
933
934sub finalize_read { my $c = shift; $c->engine->finalize_read( $c, @_ ) }
935
936=item $c->finalize_uploads
937
938Finalize uploads. Cleans up any temporary files.
939
940=cut
941
942sub finalize_uploads { my $c = shift; $c->engine->finalize_uploads( $c, @_ ) }
943
a9dc674c 944=item $c->get_action( $action, $namespace )
fbcc39ad 945
946Get an action in a given namespace.
947
948=cut
949
950sub get_action { my $c = shift; $c->dispatcher->get_action( $c, @_ ) }
951
a9dc674c 952=item $c->get_actions( $action, $namespace )
953
954Get all actions of a given name in a namespace and all base namespaces.
955
956=cut
957
958sub get_actions { my $c = shift; $c->dispatcher->get_actions( $c, @_ ) }
959
fbcc39ad 960=item handle_request( $class, @arguments )
961
962Handles the request.
963
964=cut
965
966sub handle_request {
967 my ( $class, @arguments ) = @_;
968
969 # Always expect worst case!
970 my $status = -1;
971 eval {
972 my @stats = ();
973
974 my $handler = sub {
975 my $c = $class->prepare(@arguments);
976 $c->{stats} = \@stats;
977 $c->dispatch;
978 return $c->finalize;
979 };
980
981 if ( $class->debug ) {
982 my $elapsed;
983 ( $elapsed, $status ) = $class->benchmark($handler);
984 $elapsed = sprintf '%f', $elapsed;
985 my $av = sprintf '%.3f',
986 ( $elapsed == 0 ? '??' : ( 1 / $elapsed ) );
987 my $t = Text::ASCIITable->new;
988 $t->setCols( 'Action', 'Time' );
989 $t->setColWidth( 'Action', 64, 1 );
990 $t->setColWidth( 'Time', 9, 1 );
991
992 for my $stat (@stats) { $t->addRow( $stat->[0], $stat->[1] ) }
993 $class->log->info(
994 "Request took ${elapsed}s ($av/s)\n" . $t->draw );
995 }
996 else { $status = &$handler }
997
998 };
999
1000 if ( my $error = $@ ) {
1001 chomp $error;
1002 $class->log->error(qq/Caught exception in engine "$error"/);
1003 }
1004
1005 $COUNT++;
1006 $class->log->_flush() if $class->log->can('_flush');
1007 return $status;
1008}
1009
1010=item $c->prepare(@arguments)
1011
1012Turns the engine-specific request( Apache, CGI ... )
1013into a Catalyst context .
1014
1015=cut
1016
1017sub prepare {
1018 my ( $class, @arguments ) = @_;
1019
1020 my $c = bless {
1021 counter => {},
1022 depth => 0,
1023 request => Catalyst::Request->new(
1024 {
1025 arguments => [],
1026 body_parameters => {},
1027 cookies => {},
fbcc39ad 1028 headers => HTTP::Headers->new,
1029 parameters => {},
1030 query_parameters => {},
1031 secure => 0,
1032 snippets => [],
1033 uploads => {}
1034 }
1035 ),
1036 response => Catalyst::Response->new(
1037 {
1038 body => '',
1039 cookies => {},
fbcc39ad 1040 headers => HTTP::Headers->new(),
1041 status => 200
1042 }
1043 ),
1044 stash => {},
1045 state => 0
1046 }, $class;
1047
1048 # For on-demand data
1049 $c->request->{_context} = $c;
1050 $c->response->{_context} = $c;
1051 weaken( $c->request->{_context} );
1052 weaken( $c->response->{_context} );
1053
1054 if ( $c->debug ) {
1055 my $secs = time - $START || 1;
1056 my $av = sprintf '%.3f', $COUNT / $secs;
1057 $c->log->debug('**********************************');
1058 $c->log->debug("* Request $COUNT ($av/s) [$$]");
1059 $c->log->debug('**********************************');
1060 $c->res->headers->header( 'X-Catalyst' => $Catalyst::VERSION );
1061 }
1062
1063 $c->prepare_request(@arguments);
1064 $c->prepare_connection;
1065 $c->prepare_query_parameters;
1066 $c->prepare_headers;
1067 $c->prepare_cookies;
1068 $c->prepare_path;
1069
1070 # On-demand parsing
1071 $c->prepare_body unless $c->config->{parse_on_demand};
1072
1073 $c->prepare_action;
1074 my $method = $c->req->method || '';
1075 my $path = $c->req->path || '';
1076 my $address = $c->req->address || '';
1077
1078 $c->log->debug(qq/"$method" request for "$path" from $address/)
1079 if $c->debug;
1080
1081 return $c;
1082}
1083
1084=item $c->prepare_action
1085
1086Prepare action.
1087
1088=cut
1089
1090sub prepare_action { my $c = shift; $c->dispatcher->prepare_action( $c, @_ ) }
1091
1092=item $c->prepare_body
1093
1094Prepare message body.
1095
1096=cut
1097
1098sub prepare_body {
1099 my $c = shift;
1100
1101 # Do we run for the first time?
1102 return if defined $c->request->{_body};
1103
1104 # Initialize on-demand data
1105 $c->engine->prepare_body( $c, @_ );
1106 $c->prepare_parameters;
1107 $c->prepare_uploads;
1108
1109 if ( $c->debug && keys %{ $c->req->body_parameters } ) {
1110 my $t = Text::ASCIITable->new;
1111 $t->setCols( 'Key', 'Value' );
1112 $t->setColWidth( 'Key', 37, 1 );
1113 $t->setColWidth( 'Value', 36, 1 );
1114 $t->alignCol( 'Value', 'right' );
1115 for my $key ( sort keys %{ $c->req->body_parameters } ) {
1116 my $param = $c->req->body_parameters->{$key};
1117 my $value = defined($param) ? $param : '';
1118 $t->addRow( $key,
1119 ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
1120 }
1121 $c->log->debug( "Body Parameters are:\n" . $t->draw );
1122 }
1123}
1124
4bd82c41 1125=item $c->prepare_body_chunk( $chunk )
1126
1127Prepare a chunk of data before sending it to HTTP::Body.
1128
1129=cut
1130
4f5ebacd 1131sub prepare_body_chunk {
1132 my $c = shift;
4bd82c41 1133 $c->engine->prepare_body_chunk( $c, @_ );
1134}
1135
fbcc39ad 1136=item $c->prepare_body_parameters
1137
1138Prepare body parameters.
1139
1140=cut
1141
1142sub prepare_body_parameters {
1143 my $c = shift;
1144 $c->engine->prepare_body_parameters( $c, @_ );
1145}
1146
1147=item $c->prepare_connection
1148
1149Prepare connection.
1150
1151=cut
1152
1153sub prepare_connection {
1154 my $c = shift;
1155 $c->engine->prepare_connection( $c, @_ );
1156}
1157
1158=item $c->prepare_cookies
1159
1160Prepare cookies.
1161
1162=cut
1163
1164sub prepare_cookies { my $c = shift; $c->engine->prepare_cookies( $c, @_ ) }
1165
1166=item $c->prepare_headers
1167
1168Prepare headers.
1169
1170=cut
1171
1172sub prepare_headers { my $c = shift; $c->engine->prepare_headers( $c, @_ ) }
1173
1174=item $c->prepare_parameters
1175
1176Prepare parameters.
1177
1178=cut
1179
1180sub prepare_parameters {
1181 my $c = shift;
1182 $c->prepare_body_parameters;
1183 $c->engine->prepare_parameters( $c, @_ );
1184}
1185
1186=item $c->prepare_path
1187
1188Prepare path and base.
1189
1190=cut
1191
1192sub prepare_path { my $c = shift; $c->engine->prepare_path( $c, @_ ) }
1193
1194=item $c->prepare_query_parameters
1195
1196Prepare query parameters.
1197
1198=cut
1199
1200sub prepare_query_parameters {
1201 my $c = shift;
1202
1203 $c->engine->prepare_query_parameters( $c, @_ );
1204
1205 if ( $c->debug && keys %{ $c->request->query_parameters } ) {
1206 my $t = Text::ASCIITable->new;
1207 $t->setCols( 'Key', 'Value' );
1208 $t->setColWidth( 'Key', 37, 1 );
1209 $t->setColWidth( 'Value', 36, 1 );
1210 $t->alignCol( 'Value', 'right' );
1211 for my $key ( sort keys %{ $c->req->query_parameters } ) {
1212 my $param = $c->req->query_parameters->{$key};
1213 my $value = defined($param) ? $param : '';
1214 $t->addRow( $key,
1215 ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
1216 }
1217 $c->log->debug( "Query Parameters are:\n" . $t->draw );
1218 }
1219}
1220
1221=item $c->prepare_read
1222
1223Prepare the input for reading.
1224
1225=cut
1226
1227sub prepare_read { my $c = shift; $c->engine->prepare_read( $c, @_ ) }
1228
1229=item $c->prepare_request
1230
1231Prepare the engine request.
1232
1233=cut
1234
1235sub prepare_request { my $c = shift; $c->engine->prepare_request( $c, @_ ) }
1236
1237=item $c->prepare_uploads
1238
1239Prepare uploads.
1240
1241=cut
1242
1243sub prepare_uploads {
1244 my $c = shift;
1245
1246 $c->engine->prepare_uploads( $c, @_ );
1247
1248 if ( $c->debug && keys %{ $c->request->uploads } ) {
1249 my $t = Text::ASCIITable->new;
bc2beef5 1250 $t->setCols( 'Key', 'Filename', 'Type', 'Size' );
1251 $t->setColWidth( 'Key', 12, 1 );
1252 $t->setColWidth( 'Filename', 28, 1 );
1253 $t->setColWidth( 'Type', 18, 1 );
fbcc39ad 1254 $t->setColWidth( 'Size', 9, 1 );
1255 $t->alignCol( 'Size', 'left' );
1256 for my $key ( sort keys %{ $c->request->uploads } ) {
1257 my $upload = $c->request->uploads->{$key};
1258 for my $u ( ref $upload eq 'ARRAY' ? @{$upload} : ($upload) ) {
bc2beef5 1259 $t->addRow( $key, $u->filename, $u->type, $u->size );
fbcc39ad 1260 }
1261 }
1262 $c->log->debug( "File Uploads are:\n" . $t->draw );
1263 }
1264}
1265
1266=item $c->prepare_write
1267
1268Prepare the output for writing.
1269
1270=cut
1271
1272sub prepare_write { my $c = shift; $c->engine->prepare_write( $c, @_ ) }
1273
1274=item $c->read( [$maxlength] )
1275
1276Read a chunk of data from the request body. This method is designed to be
1277used in a while loop, reading $maxlength bytes on every call. $maxlength
1278defaults to the size of the request if not specified.
1279
1280You have to set MyApp->config->{parse_on_demand} to use this directly.
1281
1282=cut
1283
1284sub read { my $c = shift; return $c->engine->read( $c, @_ ) }
1285
1286=item $c->run
1287
1288Starts the engine.
1289
1290=cut
1291
1292sub run { my $c = shift; return $c->engine->run( $c, @_ ) }
1293
1294=item $c->set_action( $action, $code, $namespace, $attrs )
1295
1296Set an action in a given namespace.
1297
1298=cut
1299
1300sub set_action { my $c = shift; $c->dispatcher->set_action( $c, @_ ) }
1301
1302=item $c->setup_actions($component)
1303
1304Setup actions for a component.
1305
1306=cut
1307
1308sub setup_actions { my $c = shift; $c->dispatcher->setup_actions( $c, @_ ) }
1309
1310=item $c->setup_components
1311
1312Setup components.
1313
1314=cut
1315
1316sub setup_components {
1317 my $class = shift;
1318
1319 my $callback = sub {
1320 my ( $component, $context ) = @_;
1321
1322 unless ( $component->isa('Catalyst::Base') ) {
1323 return $component;
1324 }
1325
1326 my $suffix = Catalyst::Utils::class2classsuffix($component);
1327 my $config = $class->config->{$suffix} || {};
1328
1329 my $instance;
1330
1331 eval { $instance = $component->new( $context, $config ); };
1332
1333 if ( my $error = $@ ) {
1334
1335 chomp $error;
1336
1337 Catalyst::Exception->throw( message =>
1338 qq/Couldn't instantiate component "$component", "$error"/ );
1339 }
1340
1341 Catalyst::Exception->throw( message =>
1342qq/Couldn't instantiate component "$component", "new() didn't return a object"/
1343 )
1344 unless ref $instance;
1345 return $instance;
1346 };
1347
1348 eval {
1349 Module::Pluggable::Fast->import(
1350 name => '_catalyst_components',
1351 search => [
1352 "$class\::Controller", "$class\::C",
1353 "$class\::Model", "$class\::M",
1354 "$class\::View", "$class\::V"
1355 ],
1356 callback => $callback
1357 );
1358 };
1359
1360 if ( my $error = $@ ) {
1361
1362 chomp $error;
1363
1364 Catalyst::Exception->throw(
1365 message => qq/Couldn't load components "$error"/ );
1366 }
1367
1368 for my $component ( $class->_catalyst_components($class) ) {
1369 $class->components->{ ref $component || $component } = $component;
1370 }
1371}
1372
1373=item $c->setup_dispatcher
1374
1375=cut
1376
1377sub setup_dispatcher {
1378 my ( $class, $dispatcher ) = @_;
1379
1380 if ($dispatcher) {
1381 $dispatcher = 'Catalyst::Dispatcher::' . $dispatcher;
1382 }
1383
1384 if ( $ENV{CATALYST_DISPATCHER} ) {
1385 $dispatcher = 'Catalyst::Dispatcher::' . $ENV{CATALYST_DISPATCHER};
1386 }
1387
1388 if ( $ENV{ uc($class) . '_DISPATCHER' } ) {
1389 $dispatcher =
1390 'Catalyst::Dispatcher::' . $ENV{ uc($class) . '_DISPATCHER' };
1391 }
1392
1393 unless ($dispatcher) {
1394 $dispatcher = 'Catalyst::Dispatcher';
1395 }
1396
1397 $dispatcher->require;
1398
1399 if ($@) {
1400 Catalyst::Exception->throw(
1401 message => qq/Couldn't load dispatcher "$dispatcher", "$@"/ );
1402 }
1403
1404 # dispatcher instance
1405 $class->dispatcher( $dispatcher->new );
1406}
1407
1408=item $c->setup_engine
1409
1410=cut
1411
1412sub setup_engine {
1413 my ( $class, $engine ) = @_;
1414
1415 if ($engine) {
1416 $engine = 'Catalyst::Engine::' . $engine;
1417 }
1418
1419 if ( $ENV{CATALYST_ENGINE} ) {
1420 $engine = 'Catalyst::Engine::' . $ENV{CATALYST_ENGINE};
1421 }
1422
1423 if ( $ENV{ uc($class) . '_ENGINE' } ) {
1424 $engine = 'Catalyst::Engine::' . $ENV{ uc($class) . '_ENGINE' };
1425 }
1426
1427 if ( !$engine && $ENV{MOD_PERL} ) {
1428
1429 # create the apache method
1430 {
1431 no strict 'refs';
1432 *{"$class\::apache"} = sub { shift->engine->apache };
1433 }
1434
1435 my ( $software, $version ) =
1436 $ENV{MOD_PERL} =~ /^(\S+)\/(\d+(?:[\.\_]\d+)+)/;
1437
1438 $version =~ s/_//g;
1439 $version =~ s/(\.[^.]+)\./$1/g;
1440
1441 if ( $software eq 'mod_perl' ) {
1442
1443 if ( $version >= 1.99922 ) {
1444 $engine = 'Catalyst::Engine::Apache2::MP20';
1445 }
1446
1447 elsif ( $version >= 1.9901 ) {
1448 $engine = 'Catalyst::Engine::Apache2::MP19';
1449 }
1450
1451 elsif ( $version >= 1.24 ) {
1452 $engine = 'Catalyst::Engine::Apache::MP13';
1453 }
1454
1455 else {
1456 Catalyst::Exception->throw( message =>
1457 qq/Unsupported mod_perl version: $ENV{MOD_PERL}/ );
1458 }
1459
1460 # install the correct mod_perl handler
1461 if ( $version >= 1.9901 ) {
1462 *handler = sub : method {
1463 shift->handle_request(@_);
1464 };
1465 }
1466 else {
1467 *handler = sub ($$) { shift->handle_request(@_) };
1468 }
1469
1470 }
1471
1472 elsif ( $software eq 'Zeus-Perl' ) {
1473 $engine = 'Catalyst::Engine::Zeus';
1474 }
1475
1476 else {
1477 Catalyst::Exception->throw(
1478 message => qq/Unsupported mod_perl: $ENV{MOD_PERL}/ );
1479 }
1480 }
1481
1482 unless ($engine) {
1483 $engine = 'Catalyst::Engine::CGI';
1484 }
1485
1486 $engine->require;
1487
1488 if ($@) {
1489 Catalyst::Exception->throw( message =>
1490qq/Couldn't load engine "$engine" (maybe you forgot to install it?), "$@"/
1491 );
1492 }
0e7f5826 1493
d54484bf 1494 # check for old engines that are no longer compatible
1495 my $old_engine;
0e7f5826 1496 if ( $engine->isa('Catalyst::Engine::Apache')
1497 && !Catalyst::Engine::Apache->VERSION )
d54484bf 1498 {
1499 $old_engine = 1;
1500 }
0e7f5826 1501
d54484bf 1502 elsif ( $engine->isa('Catalyst::Engine::Server::Base')
0e7f5826 1503 && Catalyst::Engine::Server->VERSION le '0.02' )
d54484bf 1504 {
1505 $old_engine = 1;
1506 }
0e7f5826 1507
1508 elsif ($engine->isa('Catalyst::Engine::HTTP::POE')
1509 && $engine->VERSION eq '0.01' )
d54484bf 1510 {
1511 $old_engine = 1;
1512 }
0e7f5826 1513
1514 elsif ($engine->isa('Catalyst::Engine::Zeus')
1515 && $engine->VERSION eq '0.01' )
d54484bf 1516 {
1517 $old_engine = 1;
1518 }
fbcc39ad 1519
d54484bf 1520 if ($old_engine) {
1521 Catalyst::Exception->throw( message =>
0e7f5826 1522 qq/Engine "$engine" is not supported by this version of Catalyst/
d54484bf 1523 );
1524 }
0e7f5826 1525
fbcc39ad 1526 # engine instance
1527 $class->engine( $engine->new );
1528}
1529
1530=item $c->setup_home
1531
1532=cut
1533
1534sub setup_home {
1535 my ( $class, $home ) = @_;
1536
1537 if ( $ENV{CATALYST_HOME} ) {
1538 $home = $ENV{CATALYST_HOME};
1539 }
1540
1541 if ( $ENV{ uc($class) . '_HOME' } ) {
1542 $home = $ENV{ uc($class) . '_HOME' };
1543 }
1544
1545 unless ($home) {
1546 $home = Catalyst::Utils::home($class);
1547 }
1548
1549 if ($home) {
1550 $class->config->{home} ||= $home;
1551 $class->config->{root} ||= dir($home)->subdir('root');
1552 }
1553}
1554
1555=item $c->setup_log
1556
1557=cut
1558
1559sub setup_log {
1560 my ( $class, $debug ) = @_;
1561
1562 unless ( $class->log ) {
1563 $class->log( Catalyst::Log->new );
1564 }
1565
f996b297 1566 if ( $ENV{CATALYST_DEBUG} || $ENV{ uc($class) . '_DEBUG' } || $debug ) {
fbcc39ad 1567 no strict 'refs';
1568 *{"$class\::debug"} = sub { 1 };
1569 $class->log->debug('Debug messages enabled');
1570 }
1571}
1572
1573=item $c->setup_plugins
1574
1575=cut
1576
1577sub setup_plugins {
1578 my ( $class, $plugins ) = @_;
1579
1580 $plugins ||= [];
1581 for my $plugin ( reverse @$plugins ) {
1582
1583 $plugin = "Catalyst::Plugin::$plugin";
1584
1585 $plugin->require;
1586
1587 if ($@) {
1588 Catalyst::Exception->throw(
1589 message => qq/Couldn't load plugin "$plugin", "$@"/ );
1590 }
1591
1592 {
1593 no strict 'refs';
1594 unshift @{"$class\::ISA"}, $plugin;
1595 }
1596 }
1597}
1598
1599=item $c->write( $data )
1600
1601Writes $data to the output stream. When using this method directly, you will
1602need to manually set the Content-Length header to the length of your output
1603data, if known.
1604
1605=cut
1606
4f5ebacd 1607sub write {
1608 my $c = shift;
1609
1610 # Finalize headers if someone manually writes output
1611 $c->finalize_headers;
1612
1613 return $c->engine->write( $c, @_ );
1614}
fbcc39ad 1615
bf88a181 1616=item version
1617
1618Returns the Catalyst version number. mostly useful for powered by messages
1619in template systems.
1620
1621=cut
1622
1623sub version { return $Catalyst::VERSION }
1624
23f9d934 1625=back
1626
b0bb11ec 1627=head1 INTERNAL ACTIONS
1628
1629Catalyst uses internal actions like C<_DISPATCH>, C<_BEGIN>, C<_AUTO>
1630C<_ACTION> and C<_END>, these are by default not shown in the private
1631action table.
1632
1633But you can deactivate this with a config parameter.
1634
1635 MyApp->config->{show_internal_actions} = 1;
1636
d2ee9760 1637=head1 CASE SENSITIVITY
1638
1639By default Catalyst is not case sensitive, so C<MyApp::C::FOO::Bar> becomes
1640C</foo/bar>.
1641
1642But you can activate case sensitivity with a config parameter.
1643
1644 MyApp->config->{case_sensitive} = 1;
1645
fbcc39ad 1646So C<MyApp::C::Foo::Bar> becomes C</Foo/Bar>.
1647
1648=head1 ON-DEMAND PARSER
1649
1650The request body is usually parsed at the beginning of a request,
1651but if you want to handle input yourself or speed things up a bit
1652you can enable on-demand parsing with a config parameter.
1653
1654 MyApp->config->{parse_on_demand} = 1;
1655
1656=head1 PROXY SUPPORT
1657
1658Many production servers operate using the common double-server approach, with
1659a lightweight frontend web server passing requests to a larger backend
1660server. An application running on the backend server must deal with two
1661problems: the remote user always appears to be '127.0.0.1' and the server's
1662hostname will appear to be 'localhost' regardless of the virtual host the
1663user connected through.
1664
1665Catalyst will automatically detect this situation when you are running both
1666the frontend and backend servers on the same machine. The following changes
1667are made to the request.
1668
1669 $c->req->address is set to the user's real IP address, as read from the
1670 HTTP_X_FORWARDED_FOR header.
1671
1672 The host value for $c->req->base and $c->req->uri is set to the real host,
1673 as read from the HTTP_X_FORWARDED_HOST header.
1674
1675Obviously, your web server must support these 2 headers for this to work.
1676
1677In a more complex server farm environment where you may have your frontend
1678proxy server(s) on different machines, you will need to set a configuration
1679option to tell Catalyst to read the proxied data from the headers.
1680
1681 MyApp->config->{using_frontend_proxy} = 1;
1682
1683If you do not wish to use the proxy support at all, you may set:
d1a31ac6 1684
fbcc39ad 1685 MyApp->config->{ignore_frontend_proxy} = 1;
1686
1687=head1 THREAD SAFETY
1688
1689Catalyst has been tested under Apache 2's threading mpm_worker, mpm_winnt,
1690and the standalone forking HTTP server on Windows. We believe the Catalyst
1691core to be thread-safe.
1692
1693If you plan to operate in a threaded environment, remember that all other
1694modules you are using must also be thread-safe. Some modules, most notably
1695DBD::SQLite, are not thread-safe.
d1a31ac6 1696
3cb1db8c 1697=head1 SUPPORT
1698
1699IRC:
1700
1701 Join #catalyst on irc.perl.org.
1702
1703Mailing-Lists:
1704
1705 http://lists.rawmode.org/mailman/listinfo/catalyst
1706 http://lists.rawmode.org/mailman/listinfo/catalyst-dev
1985c30b 1707
432d507d 1708Web:
1709
1710 http://catalyst.perl.org
1711
fc7ec1d9 1712=head1 SEE ALSO
1713
61b1e958 1714=over 4
1715
1716=item L<Catalyst::Manual> - The Catalyst Manual
1717
1718=item L<Catalyst::Engine> - Core Engine
1719
1720=item L<Catalyst::Log> - The Log Class.
1721
1722=item L<Catalyst::Request> - The Request Object
1723
1724=item L<Catalyst::Response> - The Response Object
1725
1726=item L<Catalyst::Test> - The test suite.
1727
1728=back
fc7ec1d9 1729
15f0b5b7 1730=head1 CREDITS
fc7ec1d9 1731
15f0b5b7 1732Andy Grundman
1733
fbcc39ad 1734Andy Wardley
1735
33108eaf 1736Andreas Marienborg
1737
f4a57de4 1738Andrew Bramble
1739
15f0b5b7 1740Andrew Ford
1741
1742Andrew Ruthven
1743
fbcc39ad 1744Arthur Bergman
1745
15f0b5b7 1746Autrijus Tang
1747
1748Christian Hansen
1749
1750Christopher Hicks
1751
1752Dan Sully
1753
1754Danijel Milicevic
1755
1756David Naughton
1757
1758Gary Ashton Jones
1759
1760Geoff Richards
1761
1762Jesse Sheidlower
1763
fbcc39ad 1764Jesse Vincent
1765
15f0b5b7 1766Jody Belka
1767
1768Johan Lindstrom
1769
1770Juan Camacho
1771
1772Leon Brocard
1773
1774Marcus Ramberg
1775
1776Matt S Trout
1777
71c3bcc3 1778Robert Sedlacek
1779
a727119f 1780Sam Vilain
1781
15f0b5b7 1782Tatsuhiko Miyagawa
fc7ec1d9 1783
51f0308d 1784Ulf Edvinsson
1785
bdcb95ef 1786Yuval Kogman
1787
51f0308d 1788=head1 AUTHOR
1789
1790Sebastian Riedel, C<sri@oook.de>
1791
fc7ec1d9 1792=head1 LICENSE
1793
9ce5ab63 1794This library is free software, you can redistribute it and/or modify it under
41ca9ba7 1795the same terms as Perl itself.
fc7ec1d9 1796
1797=cut
1798
17991;