Add in the REDIRECT_URL handling from trunk and the values for the tests
[catagits/Catalyst-Runtime.git] / lib / Catalyst.pm
index 58c0505..4714ed6 100644 (file)
@@ -14,6 +14,7 @@ use Catalyst::Request::Upload;
 use Catalyst::Response;
 use Catalyst::Utils;
 use Catalyst::Controller;
+use Data::OptList;
 use Devel::InnerPackage ();
 use File::stat;
 use Module::Pluggable::Object ();
@@ -30,6 +31,7 @@ use List::MoreUtils qw/uniq/;
 use attributes;
 use utf8;
 use Carp qw/croak carp shortmess/;
+use Try::Tiny;
 
 BEGIN { require 5.008004; }
 
@@ -68,20 +70,17 @@ our $GO        = Catalyst::Exception::Go->new;
 __PACKAGE__->mk_classdata($_)
   for qw/components arguments dispatcher engine log dispatcher_class
   engine_class context_class request_class response_class stats_class
-  setup_finished/;
+  setup_finished psgi_app/;
 
 __PACKAGE__->dispatcher_class('Catalyst::Dispatcher');
-__PACKAGE__->engine_class('Catalyst::Engine::CGI');
+__PACKAGE__->engine_class('Catalyst::Engine');
 __PACKAGE__->request_class('Catalyst::Request');
 __PACKAGE__->response_class('Catalyst::Response');
 __PACKAGE__->stats_class('Catalyst::Stats');
 
 # Remember to update this in Catalyst::Runtime as well!
 
-our $VERSION = '5.80021';
-our $PRETTY_VERSION = $VERSION;
-
-$VERSION = eval $VERSION;
+our $VERSION = '5.80024';
 
 sub import {
     my ( $class, @arguments ) = @_;
@@ -283,14 +282,15 @@ Specifies a comma-delimited list of log levels.
 
 =head2 -Stats
 
-Enables statistics collection and reporting. You can also force this setting
-from the system environment with CATALYST_STATS or <MYAPP>_STATS. The
-environment settings override the application, with <MYAPP>_STATS having the
-highest priority.
+Enables statistics collection and reporting.
+
+   use Catalyst qw/-Stats=1/;
 
-e.g.
+You can also force this setting from the system environment with CATALYST_STATS
+or <MYAPP>_STATS. The environment settings override the application, with
+<MYAPP>_STATS having the highest priority.
 
-   use Catalyst qw/-Stats=1/
+Stats are also enabled if L<< debugging |/"-Debug" >> is enabled.
 
 =head1 METHODS
 
@@ -1162,7 +1162,7 @@ EOF
 
     if ( $class->debug ) {
         my $name = $class->config->{name} || 'Application';
-        $class->log->info("$name powered by Catalyst $Catalyst::PRETTY_VERSION");
+        $class->log->info("$name powered by Catalyst $Catalyst::VERSION");
     }
 
     # Make sure that the application class becomes immutable at this point,
@@ -1282,13 +1282,11 @@ sub uri_for {
     carp "uri_for called with undef argument" if grep { ! defined $_ } @args;
     foreach my $arg (@args) {
         utf8::encode($arg) if utf8::is_utf8($arg);
-    }
-    s/([^$URI::uric])/$URI::Escape::escapes{$1}/go for @args;
-    if (blessed $path) { # Action object only.
-        s|/|%2F|g for @args;
+        $arg =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;
     }
 
     if ( blessed($path) ) { # action object
+        s|/|%2F|g for @args;
         my $captures = [ map { s|/|%2F|g; $_; }
                         ( scalar @args && ref $args[0] eq 'ARRAY'
                          ? @{ shift(@args) }
@@ -1309,8 +1307,6 @@ sub uri_for {
         $path = '/' if $path eq '';
     }
 
-    undef($path) if (defined $path && $path eq '');
-
     unshift(@args, $path);
 
     unless (defined $path && $path =~ s!^/!!) { # in-place strip
@@ -1893,7 +1889,7 @@ namespaces.
 
 sub get_actions { my $c = shift; $c->dispatcher->get_actions( $c, @_ ) }
 
-=head2 $c->handle_request( $class, @arguments )
+=head2 $app->handle_request( @arguments )
 
 Called to handle each HTTP request.
 
@@ -1904,7 +1900,7 @@ sub handle_request {
 
     # Always expect worst case!
     my $status = -1;
-    eval {
+    try {
         if ($class->debug) {
             my $secs = time - $START || 1;
             my $av = sprintf '%.3f', $COUNT / $secs;
@@ -1915,12 +1911,11 @@ sub handle_request {
         my $c = $class->prepare(@arguments);
         $c->dispatch;
         $status = $c->finalize;
-    };
-
-    if ( my $error = $@ ) {
-        chomp $error;
-        $class->log->error(qq/Caught exception in engine "$error"/);
     }
+    catch {
+        chomp(my $error = $_);
+        $class->log->error(qq/Caught exception in engine "$error"/);
+    };
 
     $COUNT++;
 
@@ -1953,32 +1948,42 @@ sub prepare {
 
     #surely this is not the most efficient way to do things...
     $c->stats($class->stats_class->new)->enable($c->use_stats);
-    if ( $c->debug ) {
+    if ( $c->debug || $c->config->{enable_catalyst_header} ) {
         $c->res->headers->header( 'X-Catalyst' => $Catalyst::VERSION );
     }
 
-    #XXX reuse coderef from can
-    # Allow engine to direct the prepare flow (for POE)
-    if ( $c->engine->can('prepare') ) {
-        $c->engine->prepare( $c, @arguments );
-    }
-    else {
-        $c->prepare_request(@arguments);
-        $c->prepare_connection;
-        $c->prepare_query_parameters;
-        $c->prepare_headers;
-        $c->prepare_cookies;
-        $c->prepare_path;
-
-        # Prepare the body for reading, either by prepare_body
-        # or the user, if they are using $c->read
-        $c->prepare_read;
-
-        # Parse the body unless the user wants it on-demand
-        unless ( ref($c)->config->{parse_on_demand} ) {
-            $c->prepare_body;
+    try {
+        # Allow engine to direct the prepare flow (for POE)
+        if ( my $prepare = $c->engine->can('prepare') ) {
+            $c->engine->$prepare( $c, @arguments );
+        }
+        else {
+            $c->prepare_request(@arguments);
+            $c->prepare_connection;
+            $c->prepare_query_parameters;
+            $c->prepare_headers;
+            $c->prepare_cookies;
+            $c->prepare_path;
+
+            # Prepare the body for reading, either by prepare_body
+            # or the user, if they are using $c->read
+            $c->prepare_read;
+
+            # Parse the body unless the user wants it on-demand
+            unless ( ref($c)->config->{parse_on_demand} ) {
+                $c->prepare_body;
+            }
         }
     }
+    # VERY ugly and probably shouldn't rely on ->finalize actually working
+    catch {
+        # failed prepare is always due to an invalid request, right?
+        $c->response->status(400);
+        $c->response->content_type('text/plain');
+        $c->response->body('Bad Request');
+        $c->finalize;
+        die $_;
+    };
 
     my $method  = $c->req->method  || '';
     my $path    = $c->req->path;
@@ -2145,7 +2150,26 @@ sub log_request {
 
 =head2 $c->log_response
 
-Writes information about the response to the debug logs.  This includes:
+Writes information about the response to the debug logs by calling
+C<< $c->log_response_status_line >> and C<< $c->log_response_headers >>.
+
+=cut
+
+sub log_response {
+    my $c = shift;
+
+    return unless $c->debug;
+
+    my($dump) = grep {$_->[0] eq 'Response' } $c->dump_these;
+    my $response = $dump->[1];
+
+    $c->log_response_status_line($response);
+    $c->log_response_headers($response->headers);
+}
+
+=head2 $c->log_response_status_line($response)
+
+Writes one line of information about the response to the debug logs.  This includes:
 
 =over 4
 
@@ -2159,13 +2183,8 @@ Writes information about the response to the debug logs.  This includes:
 
 =cut
 
-sub log_response {
-    my $c = shift;
-
-    return unless $c->debug;
-
-    my($dump) = grep {$_->[0] eq 'Response' } $c->dump_these;
-    my $response = $dump->[1];
+sub log_response_status_line {
+    my ($c, $response) = @_;
 
     $c->log->debug(
         sprintf(
@@ -2177,6 +2196,15 @@ sub log_response {
     );
 }
 
+=head2 $c->log_response_headers($headers);
+
+Hook method which can be wrapped by plugins to log the responseheaders.
+No-op in the default implementation.
+
+=cut
+
+sub log_response_headers {}
+
 =head2 $c->log_request_parameters( query => {}, body => {} )
 
 Logs request parameters to debug logs
@@ -2255,7 +2283,8 @@ sub log_headers {
 
     return unless $c->debug;
 
-    my $t = Text::SimpleTable->new( [ 35, 'Header Name' ], [ 40, 'Value' ] );
+    my $column_width = Catalyst::Utils::term_width() - 28;
+    my $t = Text::SimpleTable->new( [ 15, 'Header Name' ], [ $column_width, 'Value' ] );
     $headers->scan(
         sub {
             my ( $name, $value ) = @_;
@@ -2387,10 +2416,6 @@ sub setup_components {
         # we know M::P::O found a file on disk so this is safe
 
         Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } );
-
-        # Needs to be done as soon as the component is loaded, as loading a sub-component
-        # (next time round the loop) can cause us to get the wrong metaclass..
-        $class->_controller_init_base_classes($component);
     }
 
     for my $component (@comps) {
@@ -2400,7 +2425,6 @@ sub setup_components {
             : $class->expand_component_module( $component, $config );
         for my $component (@expanded_components) {
             next if $comps{$component};
-            $class->_controller_init_base_classes($component); # Also cover inner packages
             $class->components->{ $component } = $class->setup_component($component);
         }
     }
@@ -2453,19 +2477,6 @@ sub expand_component_module {
 
 =cut
 
-# FIXME - Ugly, ugly hack to ensure the we force initialize non-moose base classes
-#         nearest to Catalyst::Controller first, no matter what order stuff happens
-#         to be loaded. There are TODO tests in Moose for this, see
-#         f2391d17574eff81d911b97be15ea51080500003
-sub _controller_init_base_classes {
-    my ($app_class, $component) = @_;
-    return unless $component->isa('Catalyst::Controller');
-    foreach my $class ( reverse @{ mro::get_linear_isa($component) } ) {
-        Moose::Meta::Class->initialize( $class )
-            unless find_meta($class);
-    }
-}
-
 sub setup_component {
     my( $class, $component ) = @_;
 
@@ -2536,76 +2547,15 @@ Sets up engine.
 =cut
 
 sub setup_engine {
-    my ( $class, $engine ) = @_;
-
-    if ($engine) {
-        $engine = 'Catalyst::Engine::' . $engine;
-    }
-
-    if ( my $env = Catalyst::Utils::env_value( $class, 'ENGINE' ) ) {
-        $engine = 'Catalyst::Engine::' . $env;
-    }
-
-    if ( $ENV{MOD_PERL} ) {
-        my $meta = Class::MOP::get_metaclass_by_name($class);
-
-        # create the apache method
-        $meta->add_method('apache' => sub { shift->engine->apache });
-
-        my ( $software, $version ) =
-          $ENV{MOD_PERL} =~ /^(\S+)\/(\d+(?:[\.\_]\d+)+)/;
-
-        $version =~ s/_//g;
-        $version =~ s/(\.[^.]+)\./$1/g;
-
-        if ( $software eq 'mod_perl' ) {
-
-            if ( !$engine ) {
-
-                if ( $version >= 1.99922 ) {
-                    $engine = 'Catalyst::Engine::Apache2::MP20';
-                }
-
-                elsif ( $version >= 1.9901 ) {
-                    $engine = 'Catalyst::Engine::Apache2::MP19';
-                }
-
-                elsif ( $version >= 1.24 ) {
-                    $engine = 'Catalyst::Engine::Apache::MP13';
-                }
-
-                else {
-                    Catalyst::Exception->throw( message =>
-                          qq/Unsupported mod_perl version: $ENV{MOD_PERL}/ );
-                }
-
-            }
-
-            # install the correct mod_perl handler
-            if ( $version >= 1.9901 ) {
-                *handler = sub  : method {
-                    shift->handle_request(@_);
-                };
-            }
-            else {
-                *handler = sub ($$) { shift->handle_request(@_) };
-            }
-
-        }
-
-        elsif ( $software eq 'Zeus-Perl' ) {
-            $engine = 'Catalyst::Engine::Zeus';
-        }
-
-        else {
-            Catalyst::Exception->throw(
-                message => qq/Unsupported mod_perl: $ENV{MOD_PERL}/ );
-        }
-    }
+    my ($class, $engine) = @_;
 
     unless ($engine) {
         $engine = $class->engine_class;
     }
+    $engine = 'Catalyst::Engine::' . $engine
+        unless $engine =~ /^Catalyst::Engine/;
+
+    $engine = 'Catalyst::Engine' if $engine eq 'Catalyst::Engine::HTTP';
 
     Class::MOP::load_class($engine);
 
@@ -2641,8 +2591,8 @@ sub setup_engine {
         );
     }
 
-    # engine instance
     $class->engine( $engine->new );
+    $class->psgi_app( $class->engine->build_psgi_app($class) );
 }
 
 =head2 $c->setup_home
@@ -2766,13 +2716,8 @@ the plugin name does not begin with C<Catalyst::Plugin::>.
             if $plugin->isa( 'Catalyst::Component' );
         $proto->_plugins->{$plugin} = 1;
         unless ($instant) {
-            no strict 'refs';
-            if ( my $meta = Class::MOP::get_metaclass_by_name($class) ) {
-              my @superclasses = ($plugin, $meta->superclasses );
-              $meta->superclasses(@superclasses);
-            } else {
-              unshift @{"$class\::ISA"}, $plugin;
-            }
+            my $meta = Class::MOP::get_metaclass_by_name($class);
+            $meta->superclasses($plugin, $meta->superclasses);
         }
         return $class;
     }
@@ -2781,22 +2726,29 @@ the plugin name does not begin with C<Catalyst::Plugin::>.
         my ( $class, $plugins ) = @_;
 
         $class->_plugins( {} ) unless $class->_plugins;
-        $plugins ||= [];
+        $plugins = Data::OptList::mkopt($plugins || []);
 
-        my @plugins = Catalyst::Utils::resolve_namespace($class . '::Plugin', 'Catalyst::Plugin', @$plugins);
+        my @plugins = map {
+            [ Catalyst::Utils::resolve_namespace(
+                  $class . '::Plugin',
+                  'Catalyst::Plugin', $_->[0]
+              ),
+              $_->[1],
+            ]
+         } @{ $plugins };
 
         for my $plugin ( reverse @plugins ) {
-            Class::MOP::load_class($plugin);
-            my $meta = find_meta($plugin);
+            Class::MOP::load_class($plugin->[0], $plugin->[1]);
+            my $meta = find_meta($plugin->[0]);
             next if $meta && $meta->isa('Moose::Meta::Role');
 
-            $class->_register_plugin($plugin);
+            $class->_register_plugin($plugin->[0]);
         }
 
         my @roles =
-            map { $_->name }
-            grep { $_ && blessed($_) && $_->isa('Moose::Meta::Role') }
-            map { find_meta($_) }
+            map  { $_->[0]->name, $_->[1] }
+            grep { blessed($_->[0]) && $_->[0]->isa('Moose::Meta::Role') }
+            map  { [find_meta($_->[0]), $_->[1]] }
             @plugins;
 
         Moose::Util::apply_all_roles(
@@ -2810,15 +2762,24 @@ the plugin name does not begin with C<Catalyst::Plugin::>.
 Returns an arrayref of the internal execution stack (actions that are
 currently executing).
 
+=head2 $c->stats
+
+Returns the current timing statistics object. By default Catalyst uses
+L<Catalyst::Stats|Catalyst::Stats>, but can be set otherwise with
+L<< stats_class|/"$c->stats_class" >>.
+
+Even if L<< -Stats|/"-Stats" >> is not enabled, the stats object is still
+available. By enabling it with C< $c->stats->enabled(1) >, it can be used to
+profile explicitly, although MyApp.pm still won't profile nor output anything
+by itself.
+
 =head2 $c->stats_class
 
-Returns or sets the stats (timing statistics) class.
+Returns or sets the stats (timing statistics) class. L<Catalyst::Stats|Catalyst::Stats> is used by default.
 
 =head2 $c->use_stats
 
-Returns 1 when stats collection is enabled.  Stats collection is enabled
-when the -Stats options is set, debug is on or when the <MYAPP>_STATS
-environment variable is set.
+Returns 1 when L<< stats collection|/"-Stats" >> is enabled.
 
 Note that this is a static method, not an accessor and should be overridden
 by declaring C<sub use_stats { 1 }> in your MyApp.pm, not by calling C<< $c->use_stats(1) >>.
@@ -2916,6 +2877,12 @@ to be shown in hit debug tables in the test server.
 
 =item *
 
+C<use_request_uri_for_path> - Controlls if the C<REQUEST_URI> or C<PATH_INFO> environment
+variable should be used for determining the request path. See L<Catalyst::Engine::CGI/PATH DECODING>
+for more information.
+
+=item *
+
 C<using_frontend_proxy> - See L</PROXY SUPPORT>.
 
 =back
@@ -3145,6 +3112,8 @@ random: Roland Lammel <lammel@cpan.org>
 
 Robert Sedlacek C<< <rs@474.at> >>
 
+SpiceMan: Marcel Montes
+
 sky: Arthur Bergman
 
 szbalint: Balint Szilakszi <szbalint@cpan.org>
@@ -3159,6 +3128,8 @@ Will Hawes C<info@whawes.co.uk>
 
 willert: Sebastian Willert <willert@cpan.org>
 
+wreis: Wallace Reis <wallace@reis.org.br>
+
 Yuval Kogman, C<nothingmuch@woobling.org>
 
 =head1 LICENSE