X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Runtime.git;a=blobdiff_plain;f=lib%2FCatalyst.pm;h=6c32146480bd53b6217ab840c59c160c0c48785c;hp=8f91b31381a95b798aa231ef055b3cbb43a6904d;hb=00341d8110ed6405a08e717cc38d5931b414e2a0;hpb=7c7954572cfa0a81c6cea8c15c6110386b851418 diff --git a/lib/Catalyst.pm b/lib/Catalyst.pm index 8f91b31..6c32146 100644 --- a/lib/Catalyst.pm +++ b/lib/Catalyst.pm @@ -75,7 +75,7 @@ our $GO = Catalyst::Exception::Go->new; __PACKAGE__->mk_classdata($_) for qw/components arguments dispatcher engine log dispatcher_class engine_loader context_class request_class response_class stats_class - setup_finished _psgi_app loading_psgi_file/; + setup_finished _psgi_app loading_psgi_file run_options/; __PACKAGE__->dispatcher_class('Catalyst::Dispatcher'); __PACKAGE__->request_class('Catalyst::Request'); @@ -84,7 +84,7 @@ __PACKAGE__->stats_class('Catalyst::Stats'); # Remember to update this in Catalyst::Runtime as well! -our $VERSION = '5.89003'; +our $VERSION = '5.90007'; sub import { my ( $class, @arguments ) = @_; @@ -148,7 +148,7 @@ documentation and tutorials. use Catalyst qw/-Debug/; # include plugins here as well ### In lib/MyApp/Controller/Root.pm (autocreated) - sub foo : Global { # called for /foo, /foo/1, /foo/1/2, etc. + sub foo : Chained('/') Args() { # called for /foo, /foo/1, /foo/1/2, etc. my ( $self, $c, @args ) = @_; # args are qw/1 2/ for /foo/1/2 $c->stash->{template} = 'foo.tt'; # set the template # lookup something from db -- stash vars are passed to TT @@ -166,50 +166,16 @@ documentation and tutorials. [% END %] # called for /bar/of/soap, /bar/of/soap/10, etc. - sub bar : Path('/bar/of/soap') { ... } - - # called for all actions, from the top-most controller downwards - sub auto : Private { - my ( $self, $c ) = @_; - if ( !$c->user_exists ) { # Catalyst::Plugin::Authentication - $c->res->redirect( '/login' ); # require login - return 0; # abort request and go immediately to end() - } - return 1; # success; carry on to next action - } + sub bar : Chained('/') PathPart('/bar/of/soap') Args() { ... } # called after all actions are finished - sub end : Private { + sub end : Action { my ( $self, $c ) = @_; if ( scalar @{ $c->error } ) { ... } # handle errors return if $c->res->body; # already have a response $c->forward( 'MyApp::View::TT' ); # render template } - ### in MyApp/Controller/Foo.pm - # called for /foo/bar - sub bar : Local { ... } - - # called for /blargle - sub blargle : Global { ... } - - # an index action matches /foo, but not /foo/1, etc. - sub index : Private { ... } - - ### in MyApp/Controller/Foo/Bar.pm - # called for /foo/bar/baz - sub baz : Local { ... } - - # first Root auto is called, then Foo auto, then this - sub auto : Private { ... } - - # powerful regular expression paths are also possible - sub details : Regex('^product/(\w+)/details$') { - my ( $self, $c ) = @_; - # extract the (\w+) from the URI - my $product = $c->req->captures->[0]; - } - See L for additional information. =head1 DESCRIPTION @@ -236,7 +202,7 @@ fully qualify the name by using a unary plus: +Fully::Qualified::Plugin::Name /; -Special flags like C<-Debug> and C<-Engine> can also be specified as +Special flags like C<-Debug> can also be specified as arguments when Catalyst is loaded: use Catalyst qw/-Debug My::Module/; @@ -256,13 +222,6 @@ priority. This sets the log level to 'debug' and enables full debug output on the error screen. If you only want the latter, see L<< $c->debug >>. -=head2 -Engine - -Forces Catalyst to use a specific engine. Omit the -C prefix of the engine name, i.e.: - - use Catalyst qw/-Engine=CGI/; - =head2 -Home Forces Catalyst to use a specific home directory, e.g.: @@ -1036,26 +995,11 @@ sub path_to { else { return Path::Class::File->new( $c->config->{home}, @path ) } } -=head2 $c->plugin( $name, $class, @args ) - -Helper method for plugins. It creates a class data accessor/mutator and -loads and instantiates the given class. - - MyApp->plugin( 'prototype', 'HTML::Prototype' ); - - $c->prototype->define_javascript_functions; - -B This method of adding plugins is deprecated. The ability -to add plugins like this B in a Catalyst 5.81. -Please do not use this functionality in new code. - -=cut - sub plugin { my ( $class, $name, $plugin, @args ) = @_; # See block comment in t/unit_core_plugin.t - $class->log->warn(qq/Adding plugin using the ->plugin method is deprecated, and will be removed in Catalyst 5.81/); + $class->log->warn(qq/Adding plugin using the ->plugin method is deprecated, and will be removed in a future release/); $class->_register_plugin( $plugin, 1 ); @@ -1084,6 +1028,9 @@ Catalyst> line. MyApp->setup; MyApp->setup( qw/-Debug/ ); +B You B wrap this method with method modifiers +or bad things will happen - wrap the C method instead. + =cut sub setup { @@ -1348,7 +1295,15 @@ sub uri_for { } my $action = $path; - $path = $c->dispatcher->uri_for_action($action, $captures); + # ->uri_for( $action, \@captures_and_args, \%query_values? ) + if( !@args && $action->number_of_args ) { + my $expanded_action = $c->dispatcher->expand_action( $action ); + + my $num_captures = $expanded_action->number_of_captures; + unshift @args, splice @$captures, $num_captures; + } + + $path = $c->dispatcher->uri_for_action($action, $captures); if (not defined $path) { $c->log->debug(qq/Can't find uri_for action '$action' @$captures/) if $c->debug; @@ -1399,9 +1354,9 @@ sub uri_for { $res; } -=head2 $c->uri_for_action( $path, \@captures?, @args?, \%query_values? ) +=head2 $c->uri_for_action( $path, \@captures_and_args?, @args?, \%query_values? ) -=head2 $c->uri_for_action( $action, \@captures?, @args?, \%query_values? ) +=head2 $c->uri_for_action( $action, \@captures_and_args?, @args?, \%query_values? ) =over @@ -1430,6 +1385,31 @@ You can use: and it will create the URI /users/the-list. +=item \@captures_and_args? + +Optional array reference of Captures (i.e. C<req->captures>) +and arguments to the request. Usually used with L +to interpolate all the parameters in the URI. + +=item @args? + +Optional list of extra arguments - can be supplied in the +C<< \@captures_and_args? >> array ref, or here - whichever is easier for your +code. + +Your action can have zero, a fixed or a variable number of args (e.g. +C<< Args(1) >> for a fixed number or C<< Args() >> for a variable number).. + +=item \%query_values? + +Optional array reference of query parameters to append. E.g. + + { foo => 'bar' } + +will generate + + /rest/of/your/uri?foo=bar + =back =cut @@ -1593,6 +1573,16 @@ sub welcome_message { EOF } +=head2 run_options + +Contains a hash of options passed from the application script, including +the original ARGV the script received, the processed values from that +ARGV and any extra arguments to the script which were not processed. + +This can be used to add custom options to your application's scripts +and setup your application differently depending on the values of these +options. + =head1 INTERNAL METHODS These methods are not meant to be used by end users. @@ -1864,9 +1854,18 @@ sub finalize_headers { if ( !$response->has_body ) { # Add a default body if none is already present - $response->body( - qq{

This item has moved here.

} - ); + $response->body(<<"EOF"); + + + + Moved + + +

This item has moved here.

+ + +EOF + $response->content_type('text/html; charset=utf-8'); } } @@ -1977,7 +1976,7 @@ sub handle_request { return $status; } -=head2 $c->prepare( @arguments ) +=head2 $class->prepare( @arguments ) Creates a Catalyst context from an engine-specific request (Apache, CGI, etc.). @@ -2026,6 +2025,7 @@ sub prepare { $c->prepare_body; } } + $c->prepare_action; } # VERY ugly and probably shouldn't rely on ->finalize actually working catch { @@ -2033,19 +2033,19 @@ sub prepare { $c->response->status(400); $c->response->content_type('text/plain'); $c->response->body('Bad Request'); + # Note we call finalize and then die here, which escapes + # finalize being called in the enclosing block.. + # It in fact couldn't be called, as we don't return $c.. + # This is a mess - but I'm unsure you can fix this without + # breaking compat for people doing crazy things (we should set + # the 400 and just return the ctx here IMO, letting finalize get called + # above... $c->finalize; die $_; }; - my $method = $c->req->method || ''; - my $path = $c->req->path; - $path = '/' unless length $path; - my $address = $c->req->address || ''; - $c->log_request; - $c->prepare_action; - return $c; } @@ -2604,25 +2604,43 @@ Sets up engine. =cut sub engine_class { - my $class = shift; - $class->engine_loader->catalyst_engine_class(@_); + my ($class, $requested_engine) = @_; + + if (!$class->engine_loader || $requested_engine) { + $class->engine_loader( + Catalyst::EngineLoader->new({ + application_name => $class, + (defined $requested_engine + ? (catalyst_engine_class => $requested_engine) : ()), + }), + ); + } + + $class->engine_loader->catalyst_engine_class; } sub setup_engine { my ($class, $requested_engine) = @_; - $class->engine_loader( - Catalyst::EngineLoader->new({ - application_name => $class, - (defined $requested_engine - ? (requested_engine => $requested_engine) : ()), - }), - ); + my $engine = do { + my $loader = $class->engine_loader; + + if (!$loader || $requested_engine) { + $loader = Catalyst::EngineLoader->new({ + application_name => $class, + (defined $requested_engine + ? (requested_engine => $requested_engine) : ()), + }), + + $class->engine_loader($loader); + } + + $loader->catalyst_engine_class; + }; # Don't really setup_engine -- see _setup_psgi_app for explanation. return if $class->loading_psgi_file; - my $engine = $class->engine_class; Class::MOP::load_class($engine); if ($ENV{MOD_PERL}) { @@ -2635,7 +2653,7 @@ sub setup_engine { $meta->add_method(handler => sub { my $r = shift; - my $psgi_app = $class->psgi_app; + my $psgi_app = $class->_finalized_psgi_app; $apache->call_app($r, $psgi_app); }); @@ -2724,31 +2742,16 @@ sub apply_default_middlewares { }, ); - my $server_matches = sub { - my ($re) = @_; - return sub { - my ($env) = @_; - my $server = $env->{SERVER_SOFTWARE}; - return unless $server; - return $server =~ $re ? 1 : 0; - }; - }; - # If we're running under Lighttpd, swap PATH_INFO and SCRIPT_NAME # http://lists.scsys.co.uk/pipermail/catalyst/2006-June/008361.html - $psgi_app = Plack::Middleware::LighttpdScriptNameFix->wrap($psgi_app); - $psgi_app = Plack::Middleware::Conditional->wrap( $psgi_app, - condition => $server_matches->(qr/^nginx/), - builder => sub { - my ($to_wrap) = @_; - return sub { - my ($env) = @_; - my $script_name = $env->{SCRIPT_NAME}; - $env->{PATH_INFO} =~ s/^$script_name//g; - return $to_wrap->($env); - }; + builder => sub { Plack::Middleware::LighttpdScriptNameFix->wrap($_[0]) }, + condition => sub { + my ($env) = @_; + return unless $env->{SERVER_SOFTWARE} && $env->{SERVER_SOFTWARE} =~ m!lighttpd[-/]1\.(\d+\.\d+)!; + return unless $1 < 4.23; + 1; }, ); @@ -2866,7 +2869,7 @@ sub setup_stats { =head2 $c->registered_plugins Returns a sorted list of the plugins which have either been stated in the -import list or which have been added via C<< MyApp->plugin(@args); >>. +import list. If passed a given plugin name, it will report a boolean value indicating whether or not that plugin is loaded. A fully qualified name is required if @@ -3059,8 +3062,46 @@ to be shown in hit debug tables in the test server. =item * C - Controls if the C or C environment -variable should be used for determining the request path. See L -for more information. +variable should be used for determining the request path. + +Most web server environments pass the requested path to the application using environment variables, +from which Catalyst has to reconstruct the request base (i.e. the top level path to / in the application, +exposed as C<< $c->request->base >>) and the request path below that base. + +There are two methods of doing this, both of which have advantages and disadvantages. Which method is used +is determined by the C<< $c->config(use_request_uri_for_path) >> setting (which can either be true or false). + +=over + +=item use_request_uri_for_path => 0 + +This is the default (and the) traditional method that Catalyst has used for determining the path information. +The path is generated from a combination of the C and C environment variables. +The allows the application to behave correctly when C is being used to redirect requests +into the application, as these variables are adjusted by mod_rewrite to take account for the redirect. + +However this method has the major disadvantage that it is impossible to correctly decode some elements +of the path, as RFC 3875 says: "C<< Unlike a URI path, the PATH_INFO is not URL-encoded, and cannot +contain path-segment parameters. >>" This means PATH_INFO is B decoded, and therefore Catalyst +can't distinguish / vs %2F in paths (in addition to other encoded values). + +=item use_request_uri_for_path => 1 + +This method uses the C and C environment variables. As C is never +decoded, this means that applications using this mode can correctly handle URIs including the %2F character +(i.e. with C set to C in Apache). + +Given that this method of path resolution is provably more correct, it is recommended that you use +this unless you have a specific need to deploy your application in a non-standard environment, and you are +aware of the implications of not being able to handle encoded URI paths correctly. + +However it also means that in a number of cases when the app isn't installed directly at a path, but instead +is having paths rewritten into it (e.g. as a .cgi/fcgi in a public_html directory, with mod_rewrite in a +.htaccess file, or when SSI is used to rewrite pages into the app, or when sub-paths of the app are exposed +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 =item * @@ -3126,7 +3167,26 @@ headers. If you do not wish to use the proxy support at all, you may set: - MyApp->config(ignore_frontend_proxy => 1); + MyApp->config(ignore_frontend_proxy => 0); + +=head2 Note about psgi files + +Note that if you supply your own .psgi file, calling +C<< MyApp->psgi_app(@_); >>, then B. + +You either need to apply L yourself +in your psgi, for example: + + builder { + enable "Plack::Middleware::ReverseProxy"; + MyApp->psgi_app + }; + +This will unconditionally add the ReverseProxy support, or you need to call +C<< $app = MyApp->apply_default_middlewares($app) >> (to conditionally +apply the support depending upon your config). + +See L for more information. =head1 THREAD SAFETY