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