fixed first block of startup debug messages missing when using a custom logger that...
[catagits/Catalyst-Runtime.git] / lib / Catalyst.pm
index 19b2dfa..5002763 100644 (file)
@@ -35,13 +35,14 @@ use utf8;
 use Carp qw/croak carp shortmess/;
 use Try::Tiny;
 use Safe::Isa;
+use Moose::Util 'find_meta';
 use Plack::Middleware::Conditional;
 use Plack::Middleware::ReverseProxy;
 use Plack::Middleware::IIS6ScriptNameFix;
 use Plack::Middleware::IIS7KeepAliveFix;
 use Plack::Middleware::LighttpdScriptNameFix;
 use Plack::Util;
-use JSON::MaybeXS qw(decode_json);
+use Class::Load 'load_class';
 
 BEGIN { require 5.008003; }
 
@@ -64,6 +65,8 @@ sub _build_request_constructor_args {
     my %p = ( _log => $self->log );
     $p{_uploadtmp} = $self->_uploadtmp if $self->_has_uploadtmp;
     $p{data_handlers} = {$self->registered_data_handlers};
+    $p{_use_hash_multivalue} = $self->config->{use_hash_multivalue_in_request}
+      if $self->config->{use_hash_multivalue_in_request};
     \%p;
 }
 
@@ -117,7 +120,7 @@ __PACKAGE__->stats_class('Catalyst::Stats');
 
 # Remember to update this in Catalyst::Runtime as well!
 
-our $VERSION = '5.90049_001';
+our $VERSION = '5.90051';
 
 sub import {
     my ( $class, @arguments ) = @_;
@@ -326,7 +329,18 @@ cookies, HTTP headers, etc.). See L<Catalyst::Request>.
 
 =head2 $c->forward( $class, $method, [, \@arguments ] )
 
-Forwards processing to another action, by its private name. If you give a
+This is one way of calling another action (method) in the same or
+a different controller. You can also use C<< $self->my_method($c, @args) >>
+in the same controller or C<< $c->controller('MyController')->my_method($c, @args) >>
+in a different controller.
+The main difference is that 'forward' uses some of the Catalyst request
+cycle overhead, including debugging, which may be useful to you. On the
+other hand, there are some complications to using 'forward', restrictions
+on values returned from 'forward', and it may not handle errors as you prefer.
+Whether you use 'forward' or not is up to you; it is not considered superior to
+the other ways to call a method.
+
+'forward' calls  another action, by its private name. If you give a
 class name but no method, C<process()> is called. You may also optionally
 pass arguments in an arrayref. The action will receive the arguments in
 C<@_> and C<< $c->req->args >>. Upon returning from the function,
@@ -1145,6 +1159,19 @@ You are running an old script!
 EOF
     }
 
+    # Call plugins setup, this is stupid and evil.
+    # Also screws C3 badly on 5.10, hack to avoid.
+    {
+        no warnings qw/redefine/;
+        local *setup = sub { };
+        $class->setup unless $Catalyst::__AM_RESTARTING;
+    }
+
+    # Initialize our data structure
+    $class->components( {} );
+
+    $class->setup_components;
+
     if ( $class->debug ) {
         my @plugins = map { "$_  " . ( $_->VERSION || '' ) } $class->registered_plugins;
 
@@ -1158,7 +1185,7 @@ EOF
         my @middleware = map {
           ref $_ eq 'CODE' ? 
             "Inline Coderef" : 
-              (ref($_) .'  '. ($_->can('VERSION') ? $_->VERSION : '') 
+              (ref($_) .'  '. ($_->can('VERSION') ? $_->VERSION || '' : '') 
                 || '')  } $class->registered_middlewares;
 
         if (@middleware) {
@@ -1168,10 +1195,8 @@ EOF
             $class->log->debug( "Loaded PSGI Middleware:\n" . $t->draw . "\n" );
         }
 
-        my %dh = $class->registered_data_handlers || ();
-        my @data_handlers = keys %dh;
-
-        if (@data_handlers) {
+        my %dh = $class->registered_data_handlers;
+        if (my @data_handlers = keys %dh) {
             my $column_width = Catalyst::Utils::term_width() - 6;
             my $t = Text::SimpleTable->new($column_width);
             $t->row($_) for @data_handlers;
@@ -1190,22 +1215,7 @@ EOF
           ? $class->log->debug(qq/Found home "$home"/)
           : $class->log->debug(qq/Home "$home" doesn't exist/)
           : $class->log->debug(q/Couldn't find home/);
-    }
 
-    # Call plugins setup, this is stupid and evil.
-    # Also screws C3 badly on 5.10, hack to avoid.
-    {
-        no warnings qw/redefine/;
-        local *setup = sub { };
-        $class->setup unless $Catalyst::__AM_RESTARTING;
-    }
-
-    # Initialize our data structure
-    $class->components( {} );
-
-    $class->setup_components;
-
-    if ( $class->debug ) {
         my $column_width = Catalyst::Utils::term_width() - 8 - 9;
         my $t = Text::SimpleTable->new( [ $column_width, 'Class' ], [ 8, 'Type' ] );
         for my $comp ( sort keys %{ $class->components } ) {
@@ -1825,7 +1835,7 @@ sub finalize {
     # Support skipping finalize for psgix.io style 'jailbreak'.  Used to support
     # stuff like cometd and websockets
     
-    if($c->request->has_io_fh) {
+    if($c->request->_has_io_fh) {
       $c->log_response;
       return;
     }
@@ -2490,7 +2500,7 @@ sub run {
 
 sub _make_immutable_if_needed {
     my $class = shift;
-    my $meta = Class::MOP::get_metaclass_by_name($class);
+    my $meta = find_meta($class);
     my $isa_ca = $class->isa('Class::Accessor::Fast') || $class->isa('Class::Accessor');
     if (
         $meta->is_immutable
@@ -2681,7 +2691,7 @@ sub setup_dispatcher {
         $dispatcher = $class->dispatcher_class;
     }
 
-    Class::MOP::load_class($dispatcher);
+    load_class($dispatcher);
 
     # dispatcher instance
     $class->dispatcher( $dispatcher->new );
@@ -2731,7 +2741,7 @@ sub setup_engine {
     # Don't really setup_engine -- see _setup_psgi_app for explanation.
     return if $class->loading_psgi_file;
 
-    Class::MOP::load_class($engine);
+    load_class($engine);
 
     if ($ENV{MOD_PERL}) {
         my $apache = $class->engine_loader->auto;
@@ -3007,7 +3017,7 @@ the plugin name does not begin with C<Catalyst::Plugin::>.
         my ( $proto, $plugin, $instant ) = @_;
         my $class = ref $proto || $proto;
 
-        Class::MOP::load_class( $plugin );
+        load_class( $plugin );
         $class->log->warn( "$plugin inherits from 'Catalyst::Component' - this is deprecated and will not work in 5.81" )
             if $plugin->isa( 'Catalyst::Component' );
         my $plugin_meta = Moose::Meta::Class->create($plugin);
@@ -3055,7 +3065,7 @@ the plugin name does not begin with C<Catalyst::Plugin::>.
          } @{ $plugins };
 
         for my $plugin ( reverse @plugins ) {
-            Class::MOP::load_class($plugin->[0], $plugin->[1]);
+            load_class($plugin->[0], $plugin->[1]);
             my $meta = find_meta($plugin->[0]);
             next if $meta && $meta->isa('Moose::Meta::Role');
 
@@ -3155,12 +3165,20 @@ you really don't need to invoke it.
 
 =head2 default_data_handlers
 
-Default Data Handler that come bundled with L<Catalyst>.  Currently there is
-only one default data handler, for 'application/json'.  This uses L<JSON::MaybeXS>
-which uses the dependency free L<JSON::PP> OR L<Cpanel::JSON::XS> if you have
-installed it.  If you don't mind the XS dependency, you should add the faster
-L<Cpanel::JSON::XS> to you dependency list (in your Makefile.PL or dist.ini, or
-cpanfile, etc.)
+Default Data Handlers that come bundled with L<Catalyst>.  Currently there are
+only two default data handlers, for 'application/json' and an alternative to
+'application/x-www-form-urlencoded' which supposed nested form parameters via
+L<CGI::Struct> or via L<CGI::Struct::XS> IF you've installed it.
+
+The 'application/json' data handler is used to parse incoming JSON into a Perl
+data structure.  It used either L<JSON::MaybeXS> or L<JSON>, depending on which
+is installed.  This allows you to fail back to L<JSON:PP>, which is a Pure Perl
+JSON decoder, and has the smallest dependency impact.
+
+Because we don't wish to add more dependencies to L<Catalyst>, if you wish to
+use this new feature we recommend installing L<JSON> or L<JSON::MaybeXS> in
+order to get the best performance.  You should add either to your dependency
+list (Makefile.PL, dist.ini, cpanfile, etc.)
 
 =cut
 
@@ -3186,7 +3204,16 @@ sub setup_data_handlers {
 sub default_data_handlers {
     my ($class) = @_;
     return +{
-      'application/json' => sub { local $/; decode_json $_->getline },
+      'application/x-www-form-urlencoded' => sub {
+          my ($fh, $req) = @_;
+          my $params = $req->_use_hash_multivalue ? $req->body_parameters->mixed : $req->body_parameters;
+          Class::Load::load_first_existing_class('CGI::Struct::XS', 'CGI::Struct')
+            ->can('build_cgi_struct')->($params);
+      },
+      'application/json' => sub {
+          Class::Load::load_first_existing_class('JSON::MaybeXS', 'JSON')
+            ->can('decode_json')->(do { local $/; $_->getline });
+      },
     };
 }
 
@@ -3379,6 +3406,26 @@ In the future this might become the default behavior.
 
 =item *
 
+C<use_hash_multivalue_in_request>
+
+In L<Catalyst::Request> the methods C<query_parameters>, C<body_parametes>
+and C<parameters> return a hashref where values might be scalar or an arrayref
+depending on the incoming data.  In many cases this can be undesirable as it
+leads one to writing defensive code like the following:
+
+    my ($val) = ref($c->req->parameters->{a}) ?
+      @{$c->req->parameters->{a}} :
+        $c->req->parameters->{a};
+
+Setting this configuration item to true will make L<Catalyst> populate the
+attributes underlying these methods with an instance of L<Hash::MultiValue>
+which is used by L<Plack::Request> and others to solve this very issue.  You
+may prefer this behavior to the default, if so enable this option (be warned
+if you enable it in a legacy application we are not sure if it is completely
+backwardly compatible).
+
+=item *
+
 C<psgi_middleware> - See L<PSGI MIDDLEWARE>.
 
 =item *