From: John Napiorkowski Date: Fri, 3 Oct 2014 21:29:05 +0000 (-0500) Subject: merged master changes X-Git-Tag: 5.90079_001~25 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Runtime.git;a=commitdiff_plain;h=2287c16e467f91552f2042a2490bf73ecc1bd89e;hp=c027f9c423216faae741b04092306cae93aefa1e merged master changes --- diff --git a/Changes b/Changes index 3028b3f..f4326ce 100644 --- a/Changes +++ b/Changes @@ -1,5 +1,17 @@ # This file documents the revision history for Perl extension Catalyst. +5.90080_001 - TBD + - MyApp->to_app is now an alias for MyApp->psgi_app in order to better support + existing Plack conventions. + - Modify Catayst::Response->from_psgi_response to allow the first argument to + be an object that does ->as_psgi. + - Modified Catayst::Middleware::Stash to be a shallow copy in $env. Added some + docs. Added a test case to make sure stash keys added in a child application + don't bubble back up to the main application. + - We no longer use Encode::is_utf8 since it doesn't work the way we think it + does... This required some UTF-8 changes. If your application is UTF-8 aware + I highly suggest you test this release. + 5.90074 - 2014-10-01 - Specify Carp minimum version to avoid pointless test fails (valy++) diff --git a/lib/Catalyst.pm b/lib/Catalyst.pm index 9abd86b..dbab5af 100644 --- a/lib/Catalyst.pm +++ b/lib/Catalyst.pm @@ -127,7 +127,7 @@ __PACKAGE__->stats_class('Catalyst::Stats'); __PACKAGE__->_encode_check(Encode::FB_CROAK | Encode::LEAVE_SRC); # Remember to update this in Catalyst::Runtime as well! -our $VERSION = '5.90074'; +our $VERSION = '5.90080_001'; sub import { my ( $class, @arguments ) = @_; @@ -495,6 +495,18 @@ Catalyst). # stash is automatically passed to the view for use in a template $c->forward( 'MyApp::View::TT' ); +The stash hash is currently stored in the PSGI C<$env> and is managed by +L. Since it's part of the C<$env> items in +the stash can be accessed in sub applications mounted under your main +L application. For example if you delegate the response of an +action to another L application, that sub application will have +access to all the stash keys of the main one, and if can of course add +more keys of its own. However those new keys will not 'bubble' back up +to the main application. + +For more information the best thing to do is to review the test case: +t/middleware-stash.t in the distribution /t directory. + =cut sub stash { @@ -2993,7 +3005,9 @@ sub apply_default_middlewares { return $psgi_app; } -=head2 $c->psgi_app +=head2 App->psgi_app + +=head2 App->to_app Returns a PSGI application code reference for the catalyst application C<$c>. This is the bare application without any middlewares @@ -3004,6 +3018,8 @@ reference of your Catalyst application for use in F<.psgi> files. =cut +*to_app = \&psgi_app; + sub psgi_app { my ($app) = @_; my $psgi = $app->engine->build_psgi_app($app); @@ -3095,9 +3111,7 @@ sub _handle_param_unicode_decoding { my $enc = $self->encoding; return try { - Encode::is_utf8( $value ) ? - $value - : $enc->decode( $value, $self->_encode_check ); + $enc->decode( $value, $self->_encode_check ); } catch { $self->handle_unicode_encoding_exception({ diff --git a/lib/Catalyst/Middleware/Stash.pm b/lib/Catalyst/Middleware/Stash.pm index 170fa11..e99285c 100644 --- a/lib/Catalyst/Middleware/Stash.pm +++ b/lib/Catalyst/Middleware/Stash.pm @@ -9,12 +9,12 @@ use Carp 'croak'; our @EXPORT_OK = qw(stash get_stash); -sub PSGI_KEY { 'Catalyst.Stash.v1' }; +sub PSGI_KEY () { 'Catalyst.Stash.v1' } sub get_stash { my $env = shift; - return $env->{&PSGI_KEY} || - _init_stash_in($env); + return $env->{+PSGI_KEY} || + croak "You requested a stash, but one does not exist."; } sub stash { @@ -38,16 +38,13 @@ sub _create_stash { }; } -sub _init_stash_in { - my ($env) = @_; - return $env->{&PSGI_KEY} ||= - _create_stash; -} - sub call { my ($self, $env) = @_; - _init_stash_in($env); - return $self->app->($env); + my $new_env = +{ %$env }; + my %stash = %{ ($env->{+PSGI_KEY} || sub {})->() || +{} }; + + $new_env->{+PSGI_KEY} = _create_stash( \%stash ); + return $self->app->($new_env); } =head1 TITLE @@ -63,6 +60,15 @@ alone distribution We store a coderef under the C which can be dereferenced with key values or nothing to access the underly hashref. +The stash middleware is designed so that you can 'nest' applications that +use it. If for example you have a L application that is called +by a controller under a parent L application, the child application +will inherit the full stash of the parent BUT any new keys added by the child +will NOT bubble back up to the parent. However, children of children will. + +For more information the current test case t/middleware-stash.t is the best +documentation. + =head1 SUBROUTINES This class defines the following subroutines. @@ -104,7 +110,7 @@ clients. Stash key / value are stored in memory. ["I found $stashed in the stash!"]]; }; -If the stash does not yet exist, we initialize one and return that. +If the stash does not yet exist, an exception is thrown. =head1 METHODS diff --git a/lib/Catalyst/Response.pm b/lib/Catalyst/Response.pm index f049ebf..709f0ad 100644 --- a/lib/Catalyst/Response.pm +++ b/lib/Catalyst/Response.pm @@ -4,6 +4,7 @@ use Moose; use HTTP::Headers; use Moose::Util::TypeConstraints; use namespace::autoclean; +use Scalar::Util 'blessed'; with 'MooseX::Emulate::Class::Accessor::Fast'; @@ -116,6 +117,9 @@ sub finalize_headers { sub from_psgi_response { my ($self, $psgi_res) = @_; + if(blessed($psgi_res) && $psgi_res->can('as_psgi')) { + $psgi_res = $psgi_res->as_psgi; + } if(ref $psgi_res eq 'ARRAY') { my ($status, $headers, $body) = @$psgi_res; $self->status($status); @@ -430,6 +434,8 @@ a $responder) set the response from it. Properly supports streaming and delayed response and / or async IO if running under an expected event loop. +If passed an object, will expect that object to do a method C. + Example: package MyApp::Web::Controller::Test; diff --git a/lib/Catalyst/Runtime.pm b/lib/Catalyst/Runtime.pm index fb39f90..a8da289 100644 --- a/lib/Catalyst/Runtime.pm +++ b/lib/Catalyst/Runtime.pm @@ -7,7 +7,7 @@ BEGIN { require 5.008003; } # Remember to update this in Catalyst as well! -our $VERSION = '5.90074'; +our $VERSION = '5.90080_001'; =head1 NAME diff --git a/t/aggregate/to_app.t b/t/aggregate/to_app.t new file mode 100644 index 0000000..7bcb497 --- /dev/null +++ b/t/aggregate/to_app.t @@ -0,0 +1,11 @@ +use strict; +use warnings; +use FindBin; +use lib "$FindBin::Bin/../lib"; +use TestApp; +use Test::More; + +ok(TestApp->can('to_app')); +is(ref(TestApp->to_app), 'CODE'); + +done_testing; diff --git a/t/middleware-stash.t b/t/middleware-stash.t new file mode 100644 index 0000000..e35e9ef --- /dev/null +++ b/t/middleware-stash.t @@ -0,0 +1,52 @@ +use warnings; +use strict; + +{ + + package MyAppChild::Controller::User; + $INC{'MyAppChild/Controller/User.pm'} = __FILE__; + + use base 'Catalyst::Controller'; + use Test::More; + + sub stash :Local { + my ($self, $c) = @_; + $c->stash->{inner} = "inner"; + $c->res->body( "inner: ${\$c->stash->{inner}}, outer: ${\$c->stash->{outer}}"); + + is_deeply [sort {$a cmp $b} keys($c->stash)], ['inner','outer'], 'both keys in stash'; + } + + package MyAppChild; + $INC{'MyAppChild.pm'} = __FILE__; + + use Catalyst; + MyAppChild->setup; + + package MyAppParent::Controller::User; + $INC{'MyAppParent/Controller/User.pm'} = __FILE__; + + use base 'Catalyst::Controller'; + use Test::More; + + sub stash :Local { + my ($self, $c) = @_; + $c->stash->{outer} = "outer"; + $c->res->from_psgi_response( MyAppChild->to_app->($c->req->env) ); + + is_deeply [keys($c->stash)], ['outer'], 'only one key in stash'; + } + + package MyAppParent; + use Catalyst; + MyAppParent->setup; + +} + +use Test::More; +use Catalyst::Test 'MyAppParent'; + +my $res = request '/user/stash'; +is $res->content, 'inner: inner, outer: outer', 'got expected response'; + +done_testing; diff --git a/t/psgi_utils.t b/t/psgi_utils.t index 078dd82..9c05559 100644 --- a/t/psgi_utils.t +++ b/t/psgi_utils.t @@ -9,6 +9,12 @@ my $psgi_app = sub { }; { + package MyApp::PSGIObject; + + sub as_psgi { + return [200, ['Content-Type' => 'text/plain'], ['as_psgi']]; + }; + package MyApp::Controller::Docs; $INC{'MyApp/Controller/Docs.pm'} = __FILE__; @@ -16,6 +22,12 @@ my $psgi_app = sub { use Plack::Request; use Catalyst::Utils; + sub as_psgi :Local { + my ($self, $c) = @_; + my $as_psgi = bless +{}, 'MyApp::PSGIObject'; + $c->res->from_psgi_response($as_psgi); + } + sub name :Local { my ($self, $c) = @_; my $env = $c->Catalyst::Utils::env_at_action; @@ -122,6 +134,11 @@ use Test::More; use Catalyst::Test 'MyApp'; { + my ($res, $c) = ctx_request('/docs/as_psgi'); + is $res->content, 'as_psgi'; +} + +{ my ($res, $c) = ctx_request('/user/mounted/111?path_prefix=1'); is $c->action, 'user/mounted'; is $res->content, 'http://localhost/user/user/local_example_args1/111'; @@ -367,32 +384,3 @@ use Catalyst::Test 'MyApp'; } done_testing(); - -__END__ - - -use Plack::App::URLMap; -use HTTP::Request::Common; -use HTTP::Message::PSGI; - -my $urlmap = Plack::App::URLMap->new; - -my $app1 = sub { - my $env = shift; - return [200, [], [ - "REQUEST_URI: $env->{REQUEST_URI}, FROM: $env->{MAP_TO}, PATH_INFO: $env->{PATH_INFO}, SCRIPT_NAME $env->{SCRIPT_NAME}"]]; -}; - -$urlmap->map("/" => sub { my $env = shift; $env->{MAP_TO} = '/'; $app1->($env)}); -$urlmap->map("/foo" => sub { my $env = shift; $env->{MAP_TO} = '/foo'; $app1->($env)}); -$urlmap->map("/bar/baz" => sub { my $env = shift; $env->{MAP_TO} = '/foo/bar'; $app1->($env)}); - -my $app = $urlmap->to_app; - -warn $app->(req_to_psgi(GET '/'))->[2]->[0]; -warn $app->(req_to_psgi(GET '/111'))->[2]->[0]; -warn $app->(req_to_psgi(GET '/foo'))->[2]->[0]; -warn $app->(req_to_psgi(GET '/foo/222'))->[2]->[0]; -warn $app->(req_to_psgi(GET '/bar/baz'))->[2]->[0]; -warn $app->(req_to_psgi(GET '/bar/baz/333'))->[2]->[0]; -