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