X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FCatalyst.pm;h=bf5a1ee572aa030497a5276c6d7ead1bdec5f450;hb=20a09634de458885c2551635251caec4a349956d;hp=17605d40bd5eb83df992d23a49cd62f168f0d335;hpb=409d48fb495802db8eb6e02a927dd2915d8643b4;p=catagits%2FCatalyst-Runtime.git diff --git a/lib/Catalyst.pm b/lib/Catalyst.pm index 17605d4..bf5a1ee 100644 --- a/lib/Catalyst.pm +++ b/lib/Catalyst.pm @@ -40,6 +40,8 @@ use Plack::Middleware::ReverseProxy; use Plack::Middleware::IIS6ScriptNameFix; use Plack::Middleware::IIS7KeepAliveFix; use Plack::Middleware::LighttpdScriptNameFix; +use Plack::Util; +use Class::Load; BEGIN { require 5.008003; } @@ -113,7 +115,7 @@ __PACKAGE__->stats_class('Catalyst::Stats'); # Remember to update this in Catalyst::Runtime as well! -our $VERSION = '5.90030'; +our $VERSION = '5.90042'; sub import { my ( $class, @arguments ) = @_; @@ -1828,7 +1830,7 @@ sub finalize { $c->log_response; if ($c->use_stats) { - my $elapsed = sprintf '%f', $c->stats->elapsed; + my $elapsed = $c->stats->elapsed; my $av = $elapsed == 0 ? '??' : sprintf '%.3f', 1 / $elapsed; $c->log->info( "Request took ${elapsed}s ($av/s)\n" . $c->stats->report . "\n" ); @@ -2726,6 +2728,11 @@ sub setup_engine { return; } +## This exists just to supply a prebuild psgi app for mod_perl and for the +## build in server support (back compat support for pre psgi port behavior). +## This is so that we don't build a new psgi app for each request when using +## the mod_perl handler or the built in servers (http and fcgi, etc). + sub _finalized_psgi_app { my ($app) = @_; @@ -2737,6 +2744,12 @@ sub _finalized_psgi_app { return $app->_psgi_app; } +## Look for a psgi file like 'myapp_web.psgi' (if the app is MyApp::Web) in the +## home directory and load that and return it (just assume it is doing the +## right thing :) ). If that does not exist, call $app->psgi_app, wrap that +## in default_middleware and return it ( this is for backward compatibility +## with pre psgi port behavior ). + sub _setup_psgi_app { my ($app) = @_; @@ -3001,7 +3014,7 @@ the plugin name does not begin with C. () } : $_ } @$plugins ]; - unshift @$plugins, $class->_default_plugins; + push @$plugins, $class->_default_plugins; $plugins = Data::OptList::mkopt($plugins || []); my @plugins = map { @@ -3033,6 +3046,135 @@ the plugin name does not begin with C. } } +has '_registered_middlewares' => ( + traits => ['Array'], + is => 'bare', + isa => 'ArrayRef[Object|CodeRef]', + default => sub { [] }, + handles => { + registered_middlewares => 'elements', + _register_middleware => 'push', + }); + + +=head2 setup_middleware + +Read configuration information stored in configuration key 'psgi_middleware' +and invoke L for each middleware prototype found. See +under L information regarding L and how to +use it to enable L + +This method is automatically called during 'setup' of your application, so +you really don't need to invoke it. + +When we read middleware definitions from configuration, we reverse the list +which sounds odd but is likely how you expect it to work if you have prior +experience with L or if you previously used the plugin +L (which is now considered deprecated) + +=head2 register_middleware (@args) + +Given @args that represent the definition of some L or +middleware with a compatible interface, register it with your L +application. + +This is called by L. the behaior of invoking it yourself +at runtime is currently undefined, and anything that works or doesn't work +as a result of doing so is considered a side effect subject to change. + +=head2 _register_middleware (@args) + +Internal details of how registered middleware is stored by the application. + +=head2 _build_middleware_from_args (@args) + +Internal application that converts a single middleware definition (see +L) into an actual instance of middleware. + +=head2 registered_middlewares + +Read only accessor that returns an array of all the middleware in the order +that they were added (which is the REVERSE of the order they will be applied). + +The values returned will be either instances of L or of a +compatible interface, or a coderef, which is assumed to be inlined middleware + +=head2 apply_registered_middleware ($psgi) + +Given a $psgi reference, wrap all the L around it and +return the wrapped version. + +=cut + +sub setup_middleware { + my ($self) = @_; + my @middleware_definitions = reverse + @{$self->config->{'psgi_middleware'}||[]}; + + while(my $next = shift(@middleware_definitions)) { + if(ref $next) { + if(Scalar::Util::blessed $next && $next->can('wrap')) { + $self->register_middleware($next); + } elsif(ref $next eq 'CODE') { + $self->register_middleware($next); + } elsif(ref $next eq 'HASH') { + my $namespace = shift @middleware_definitions; + $self->register_middleware($namespace, %$next); + } else { + $self->register_middleware($next); + } + } + } +} + +sub register_middleware { + my($self, @args) = @_; + my $middleware = $self->_build_middleware_from_args(@args); + $self->_register_middleware($middleware); + return $middleware; +} + +sub _build_middleware_from_args { + my ($self, $proto, @args) = @_; + + if(ref $proto) { ## Either an object or coderef + if(Scalar::Util::blessed $proto && $proto->can('wrap')) { ## Its already an instance of middleware + return $proto; + } elsif(ref $proto eq 'CODE') { ## Its inlined coderef + return $proto; + } + } else { ## We assume its a string aiming to load Plack Middleware + my $class = ref($self) || $self; + if( + $proto =~s/^\+// || + $proto =~/^Plack::Middleware/ || + $proto =~/^$class/ + ) { ## the string is a full namespace + require "$proto"; + return $proto->new(@args); + } else { ## the string is a partial namespace + if(Class::Load::try_load_class("Plack::Middleware::$proto")) { ## Act like Plack::Builder + return "Plack::Middleware::$proto"->new(@args); + } elsif(Class::Load::try_load_class("$class::$proto")) { ## Load Middleware from Project namespace + return "$class::$proto"->new(@args); + } + } + } + + die "I don't know how to build $proto into valid Plack Middleware"; +} + +sub apply_registered_middleware { + my ($self, $psgi) = @_; + my $new_psgi = $psgi; + foreach my $middleware ($self->registered_middlewares) { + $new_psgi = Scalar::Util::blessed $middleware ? + $middleware->wrap($new_psgi) : + $middleware->($new_psgi); + } + return $new_psgi; +} + =head2 $c->stack Returns an arrayref of the internal execution stack (actions that are @@ -3193,7 +3335,7 @@ is having paths rewritten into it (e.g. as a .cgi/fcgi in a public_html director at other URIs than that which the app is 'normally' based at with C), the resolution of C<< $c->request->base >> will be incorrect. -=back +=back =item * @@ -3203,9 +3345,9 @@ C - See L. C - See L -=back +=item * -=item abort_chain_on_error_fix => 1 +C When there is an error in an action chain, the default behavior is to continue processing the remaining actions and then catch the error upon chain end. This @@ -3214,8 +3356,14 @@ you have this issue, setting this config value to true will promptly exit a chain when there is an error raised in any action (thus terminating the chain early.) +use like: + + __PACKAGE__->config(abort_chain_on_error_fix => 1); + In the future this might become the default behavior. +=back + =head1 INTERNAL ACTIONS Catalyst uses internal actions like C<_DISPATCH>, C<_BEGIN>, C<_AUTO>,