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