From: John Napiorkowski Date: Fri, 18 Oct 2013 21:56:49 +0000 (-0500) Subject: let you use Hash::MultiValue everywhere if you like it X-Git-Tag: 5.90050~1^2~29 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Runtime.git;a=commitdiff_plain;h=88ba7793bb132b13ecea722fcc56313756a408b9 let you use Hash::MultiValue everywhere if you like it --- diff --git a/Changes b/Changes index beadd42..e53bedc 100644 --- a/Changes +++ b/Changes @@ -22,6 +22,12 @@ - NEW FEATURE: Catalyst::Response can now pull response from a PSGI specification response. This makes it easier to host external Plack applications under Catalyst. See Catalyst::Response->from_psgi_response + - NEW FEATURE: New configuration option 'use_hash_multivalue_in_request' + will populate $request methods 'parameters', 'body_parameters' and + 'query_parameters' with an instance of Hash::MultiValue instead of a + HashRef. This is used by Plack and is intended to reduce the need to + write defensive logic since you are never sure if an incoming parameter + is a scalar or arrayref. 5.90049_003 - 2013-09-20 - Documented the new body_data method added in the previous release diff --git a/lib/Catalyst.pm b/lib/Catalyst.pm index aa98926..2d3a391 100644 --- a/lib/Catalyst.pm +++ b/lib/Catalyst.pm @@ -65,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; } @@ -3397,6 +3399,26 @@ In the future this might become the default behavior. =item * +C + +In L the methods C, C +and C 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 populate the +attributes underlying these methods with an instance of L +which is used by L 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 - See L. =item * diff --git a/lib/Catalyst/Engine.pm b/lib/Catalyst/Engine.pm index 2a1ed87..3b4ceb0 100644 --- a/lib/Catalyst/Engine.pm +++ b/lib/Catalyst/Engine.pm @@ -492,7 +492,10 @@ sub prepare_query_parameters { my $env = $c->request->env; if(my $query_obj = $env->{'plack.request.query'}) { - $c->request->query_parameters($query_obj->as_hashref_mixed); + $c->request->query_parameters( + $c->request->_use_hash_multivalue ? + $query_obj->clone : + $query_obj->as_hashref_mixed); return; } @@ -540,7 +543,10 @@ sub prepare_query_parameters { } $env->{'plack.request.query'} ||= Hash::MultiValue->from_mixed(\%query); - $c->request->query_parameters( \%query ); + $c->request->query_parameters( + $c->request->_use_hash_multivalue ? + $env->{'plack.request.query'}->clone : + \%query); } =head2 $self->prepare_read($c) diff --git a/lib/Catalyst/Request.pm b/lib/Catalyst/Request.pm index 7c1ae96..87ce66a 100644 --- a/lib/Catalyst/Request.pm +++ b/lib/Catalyst/Request.pm @@ -131,6 +131,11 @@ sub _build_body_data { } } +has _use_hash_multivalue => ( + is=>'ro', + required=>1, + default=> sub {0}); + # Amount of data to read from input on each pass our $CHUNKSIZE = 64 * 1024; @@ -212,6 +217,10 @@ sub _build_parameters { Hash::MultiValue->new($query->flatten, $body->flatten); }; + if($self->_use_hash_multivalue) { + return $self->env->{'plack.request.merged'}->clone; # We want a copy, in case your App is evil + } + # We copy, no references foreach my $name (keys %$query_parameters) { my $param = $query_parameters->{$name}; @@ -325,7 +334,9 @@ sub prepare_body_parameters { $self->prepare_body if ! $self->_has_body; return {} unless $self->_body; - return $self->_body->param; + return $self->_use_hash_multivalue ? + $self->env->{'plack.request.body'}->clone : + $self->_body->param; } sub prepare_connection { diff --git a/t/lib/TestFromPSGI.pm b/t/lib/TestFromPSGI.pm index def2832..31f486d 100644 --- a/t/lib/TestFromPSGI.pm +++ b/t/lib/TestFromPSGI.pm @@ -5,6 +5,7 @@ use Catalyst; __PACKAGE__->config( 'Controller::Root', { namespace => '' }, + use_hash_multivalue_in_request => 1, ); __PACKAGE__->setup; diff --git a/t/more-psgi-compat.t b/t/more-psgi-compat.t index 71dc283..da2a292 100644 --- a/t/more-psgi-compat.t +++ b/t/more-psgi-compat.t @@ -34,4 +34,23 @@ use Catalyst::Test 'TestFromPSGI'; 'expected content body /from_psgi_code_itr'; } +{ + ok my($res, $c) = ctx_request(POST '/test_psgi_keys?a=1&b=2', [c=>3,d=>4]); + + ok $c->req->env->{"psgix.input.buffered"}, "input is buffered"; + ok $c->req->env->{"plack.request.http.body"}; + ok my $body = $c->req->env->{"plack.request.body"}; + ok my $query = $c->req->env->{"plack.request.query"}; + ok my $merged = $c->req->env->{"plack.request.merged"}; + + is $body->get('c'), 3; + is $body->get('d'), 4; + is $query->get('a'), 1; + is $query->get('b'), 2; + is $merged->get('c'), 3; + is $merged->get('d'), 4; + is $merged->get('a'), 1; + is $merged->get('b'), 2; +} + done_testing;