From: Tomas Doran Date: Wed, 28 Jul 2010 22:50:57 +0000 (+0000) Subject: Merge 'trunk' into 'fix_iis_cgi' X-Git-Tag: 5.80025~2 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Runtime.git;a=commitdiff_plain;h=fef8c827fc1a87b1c32c487e330b5e967874f581;hp=a49001028fac795b862b62c24092510c02b6ae9d Merge 'trunk' into 'fix_iis_cgi' r13096@spaceinvaders (orig r13060): rafl | 2010-03-22 10:30:30 +0000 It's --help, not -help. r13098@spaceinvaders (orig r13062): miyagawa | 2010-03-22 11:07:22 +0000 added cpanm :) r13099@spaceinvaders (orig r13063): ijw | 2010-03-22 11:30:38 +0000 Disabled name lookup for server hostname in favour of returning IP address. Annoyingly, I can't find any actual use of the name in this module (it's passed as SERVER_NAME in the env), but we believe it ends up in the 'Host' header. r13100@spaceinvaders (orig r13064): rafl | 2010-03-22 12:32:25 +0000 no tabs. kthx! r13118@spaceinvaders (orig r13082): rafl | 2010-03-26 16:00:54 +0000 Make sure to construct Upload objects properly, even if there are multiple Content-Type headers. Closes RT#55976. r13119@spaceinvaders (orig r13083): t0m | 2010-03-28 17:43:37 +0100 Back out 13063. This changes the CGI environment we construct to just be plain wrong, SERVER_NAME is meant to be a name after all. I have a feeling that all of this crap could be simplified, but I don't want to spend a lot of tuits changing it (and possibly breaking things for people with non-obvious behaviour changes like this one) - when those tuits could be spent on Plack stuff so that all this code dies anyway (and we do a major release so breakage can be more reasonable expected / extensively tested for). This is all possibly being _too_ paranoid here, but given the original commit didn't know what was happening / what changed _at all_, I think that's fair.. r13120@spaceinvaders (orig r13084): t0m | 2010-03-28 17:54:36 +0100 Cache the IP address => hostname lookups which could be performed multiple times to mitigate slow DNS servers. Poor man's replacement for 13063 r13121@spaceinvaders (orig r13085): t0m | 2010-03-28 18:24:18 +0100 Back out behaviour change in debug logging we don't want, keeping only the addition of 1 line of response info. More refactoring to give hooks to put the behaviour I just removed back from a plugin r13122@spaceinvaders (orig r13086): t0m | 2010-03-28 19:18:45 +0100 More splitting up of the response logging methods r13123@spaceinvaders (orig r13087): t0m | 2010-03-28 19:25:41 +0100 Make the tables from the log_headers method scale to term size nicely r13139@spaceinvaders (orig r13103): t0m | 2010-03-29 16:55:39 +0100 Fail, commit version bump r13142@spaceinvaders (orig r13106): rafl | 2010-03-29 17:09:05 +0100 Remove $VERSION hacks. For future dev releases we'll just use "-TRIAL" in the dist name. r13143@spaceinvaders (orig r13107): rafl | 2010-03-29 17:19:15 +0100 We always have a metaclass after setup, right? r13152@spaceinvaders (orig r13116): t0m | 2010-03-31 21:09:17 +0100 Adding ability to switch X-Catalyst on by config. For everyone who would like to let Netcraft know how awesome Catalyst is. (Sorry rafl...) r13157@spaceinvaders (orig r13121): rafl | 2010-04-02 00:30:06 +0100 Import croak, which the test already uses in various places. r13160@spaceinvaders (orig r13124): t0m | 2010-04-02 19:27:33 +0100 Require new CGI::Simple::Cookie, changelog r13187@spaceinvaders (orig r13151): t0m | 2010-04-12 20:16:39 +0100 Fix dsadinoff's mod_rewrite bug I hope r13188@spaceinvaders (orig r13152): t0m | 2010-04-12 20:18:04 +0100 Revert unintentional change in r13151 r13191@spaceinvaders (orig r13155): t0m | 2010-04-13 22:03:58 +0100 Fix spelling errors - RT#54335 r13192@spaceinvaders (orig r13156): t0m | 2010-04-13 23:06:19 +0100 Fix RT#41442 so that temporary files are always, always cleaned up. r13193@spaceinvaders (orig r13157): t0m | 2010-04-13 23:12:13 +0100 Document return of C::T::get is bytes not characters, RT#53678 r13194@spaceinvaders (orig r13158): t0m | 2010-04-13 23:18:19 +0100 Fix RT#49267 r13195@spaceinvaders (orig r13159): t0m | 2010-04-13 23:29:10 +0100 Trivial test feature, fixes RT#53653 r13202@spaceinvaders (orig r13166): t0m | 2010-04-19 00:03:56 +0100 Fix unquoted regex as per RT#24951 r13205@spaceinvaders (orig r13169): t0m | 2010-04-19 03:40:24 +0100 Document the action config here, as people don't seem to find it and this may help.. r13206@spaceinvaders (orig r13170): t0m | 2010-04-19 03:41:57 +0100 Bah, accidentally removed.. r13207@spaceinvaders (orig r13171): t0m | 2010-04-19 08:22:49 +0100 Go away useless warning r13213@spaceinvaders (orig r13177): ajgb | 2010-04-21 12:10:51 +0100 Fix not stripping backslashes in DispatchType::Regex::uri_for_action r15483@spaceinvaders (orig r13190): rafl | 2010-04-28 23:54:04 +0100 Make sure path_to returns an instance of the right Path::Class class. r15484@spaceinvaders (orig r13191): edenc | 2010-04-29 00:29:02 +0100 minor documentation fix for handle_request r15489@spaceinvaders (orig r13193): rafl | 2010-05-03 00:16:25 +0100 Allow parameterized roles to be applied as plugins. r15490@spaceinvaders (orig r13194): t0m | 2010-05-03 00:27:43 +0100 Back out crazy heuristics r15492@spaceinvaders (orig r13196): rafl | 2010-05-03 00:44:30 +0100 Unbreak tests by actually adding the module they're supposed to test. r15494@spaceinvaders (orig r13198): rafl | 2010-05-03 01:51:43 +0100 Remove useless conditional. r15515@spaceinvaders (orig r13219): wreis | 2010-05-06 13:34:10 +0100 make uri_for a bit cleaner r15516@spaceinvaders (orig r13220): wreis | 2010-05-06 14:30:19 +0100 minor fix for Changes file | add me as a contributor r15517@spaceinvaders (orig r13221): rafl | 2010-05-07 22:11:10 +0100 Pass along options to load_class for plugins. r15518@spaceinvaders (orig r13222): rafl | 2010-05-07 22:48:51 +0100 Changelogging. r15519@spaceinvaders (orig r13223): rafl | 2010-05-07 23:06:26 +0100 Version 5.80023. r15535@spaceinvaders (orig r13239): ribasushi | 2010-05-12 12:48:40 +0100 Better stats API explanation (SpiceMan) r15559@spaceinvaders (orig r13263): t0m | 2010-05-15 10:42:58 +0100 r13208@spaceinvaders (orig r13172): t0m | 2010-04-19 09:54:56 +0200 Branch to try and fix the request uri stuff. r13209@spaceinvaders (orig r13173): t0m | 2010-04-19 09:58:37 +0200 Just add comments to tests, no functional changes r13210@spaceinvaders (orig r13174): t0m | 2010-04-19 09:59:14 +0200 Get it mostly working, except uri_for is still buggered r15488@spaceinvaders (orig r13192): t0m | 2010-05-03 00:26:22 +0200 Revert to old behaviour, allow config for new behaviour. Config option name is rubbish, needs fixing r15532@spaceinvaders (orig r13236): t0m | 2010-05-09 01:09:01 +0200 I hate this name less. Others may feel differently r15556@spaceinvaders (orig r13260): t0m | 2010-05-15 10:52:16 +0200 Simplify madness some more, back to how it looked in the original fix_path_info_decoding branch so that we aren't using dodgy heuristics to determine the path. Alter the prepare_path tests so that they're testing the appropriate config option so that we now have tests for both code paths r15557@spaceinvaders (orig r13261): t0m | 2010-05-15 11:20:16 +0200 Add a pile of docs for the new use_request_uri_for_path setting r15558@spaceinvaders (orig r13262): t0m | 2010-05-15 11:38:06 +0200 Add recommendation r15560@spaceinvaders (orig r13264): t0m | 2010-05-15 10:55:07 +0100 Changelog, bump versions, add new contributor :) r15567@spaceinvaders (orig r13271): jhannah | 2010-05-19 23:36:21 +0100 We appear to have a bug where if lazy => 1 isn't set an exception occurs. r15575@spaceinvaders (orig r13279): jhannah | 2010-05-20 20:46:31 +0100 Oops. I should have TODO'd this one. rafl++ r15577@spaceinvaders (orig r13281): t0m | 2010-05-20 21:31:39 +0100 r13203@t0mlaptop (orig r13167): t0m | 2010-04-19 02:57:27 +0100 Branch for doys upcoming metaclass compat fixes. r13204@t0mlaptop (orig r13168): t0m | 2010-04-19 03:02:36 +0100 Remove the fugly hack to avoid metaclass compat issues now that Moose is fixed r15584@spaceinvaders (orig r13282): t0m | 2010-05-20 21:34:34 +0100 Changelog and dep bump for more_metaclass_compat branch merge r15585@spaceinvaders (orig r13283): t0m | 2010-05-20 21:35:20 +0100 Back out hunks I accidentally committed r15598@spaceinvaders (orig r13296): t0m | 2010-05-22 11:17:18 +0100 Changelog, bump Moose dep r15599@spaceinvaders (orig r13297): t0m | 2010-05-22 11:18:14 +0100 r15581@spaceinvaders: t0m | 2010-05-21 18:11:33 +0100 Mech tests branch r15582@spaceinvaders: t0m | 2010-05-21 18:20:01 +0100 Fix --mech as reportedon list as 'create controller 'option' -mechanize fails' r15583@spaceinvaders: t0m | 2010-05-21 18:21:18 +0100 Fix missing - in option, options must be -- r15645@spaceinvaders (orig r13336): t0m | 2010-06-10 10:54:11 +0100 Fix $self vs $class, davewood++ r15660@spaceinvaders (orig r13351): rainboxx | 2010-06-15 12:53:05 +0100 Applied doc patches that might help to figure out how to receive component's config values. r15672@spaceinvaders (orig r13363): jester | 2010-06-22 21:47:45 +0100 Fixed typo r15674@spaceinvaders (orig r13365): bricas | 2010-06-24 13:52:20 +0100 add typo fix to Changes r15689@spaceinvaders (orig r13380): arcanez | 2010-07-03 01:04:07 +0100 remove extra ' r15716@spaceinvaders (orig r13407): hobbs | 2010-07-09 10:40:44 +0100 Try harder to make finalize_error encoding-safe. r15750@spaceinvaders (orig r13441): t0m | 2010-07-28 22:39:41 +0100 r15666@spaceinvaders (orig r13357): jnapiorkowski | 2010-06-16 20:12:46 +0100 new branch r15667@spaceinvaders (orig r13358): jnapiorkowski | 2010-06-16 20:55:08 +0100 broke out and documented action_class method r15749@spaceinvaders (orig r13440): t0m | 2010-07-28 22:39:32 +0100 Changelog r15755@spaceinvaders (orig r13446): t0m | 2010-07-28 23:00:14 +0100 r15662@spaceinvaders (orig r13353): t0m | 2010-06-15 22:15:31 +0100 Branch for rt58057 r15663@spaceinvaders (orig r13354): t0m | 2010-06-15 22:17:32 +0100 Commit standalone test, test which makes the TestApp blow up and the nasty workaround r15726@spaceinvaders (orig r13417): t0m | 2010-07-23 13:29:21 +0100 I know hobbs has a patch to add a load of these, but we should at least add (es) r15753@spaceinvaders (orig r13444): t0m | 2010-07-28 22:56:25 +0100 Entirely the wrong branch, idiot. Remove r13417 r15754@spaceinvaders (orig r13445): t0m | 2010-07-28 23:00:05 +0100 Document the horrible, changelog. I'm going to merge this and fix a real issue as I don't have time to fuck around inside Moose right now and it's been waiting way too long already r15756@spaceinvaders (orig r13447): t0m | 2010-07-28 23:26:21 +0100 Fix TODO tests. r15757@spaceinvaders (orig r13448): t0m | 2010-07-28 23:26:31 +0100 Whitespace --- diff --git a/Changes b/Changes index ead2c17..0eee575 100644 --- a/Changes +++ b/Changes @@ -1,25 +1,105 @@ # This file documents the revision history for Perl extension Catalyst. + New features: + - An 'action_class' method has been added to Catalyst::Controller to + allow controller base classes, roles or traits + (e.g. Catalyst::Controller::ActionRole) to more easily override + the default action creation. + + Bug fixes: + - Fix the --mech and --mechanize options to the myapp_create.pl script + to operate correctly by fixing the options passed down into the script. + - Fix controllers with no method attributes (where the action definitions + are entirely contained in config). RT#58057 + + Documentation: + - Fix missing - in the docs when describing the --mechanize option at one + point. + - Explained the common practice how to access the component's config + values. + - Fixed typo in Catalyst/Script/Server.pm (RT #58474) + +5.80024 2010-05-15 11:55:44 + + Bug fixes: + - Revert the path resolution behaviour to how it used to work before + Catalyst 5.80014_02, so that application paths are (by default) + resolved from $ENV{PATH_INFO} and $ENV{SCRIPT_NAME}. This fixes backward + compatibility breakage seen by a number of people since that release + with mod_rewrite and SSI. + + New features: + - Add a use_request_uri_for_path config setting to optionally + use the (more correct) $ENV{REQUEST_URI} path resolution behaviour. + + Documentation: + - Clarify the documentation for the Catalyst::Stats interface. + - Copious documentation about the use_request_uri_for_path feature + and the implications of setting this to true/false in + Catalyst::Engine::CGI + +5.80023 2010-05-07 23:50:27 + + Bug fixes: + - Ensure to always cleanup temporary uploaded files in all cases, even + when exceptions occur during request processing, using HTTP::Body's + ->cleanup feature. (RT#41442) + - Ensure that Catalyst::Engine::HTTP's options hash is defined before + dereferencing it. (RT#49267) + - Fix regex special characters in REDIRECT_URL variable breaking + the request base. (2nd part of RT#24951) + - Fix not stripping backslashes in DispatchType::Regex::uri_for_action + + New features: + - Setting __PACKAGE__->config(enable_catalyst_header => 1); in your MyApp.pm + now enables the X-Catalyst header being printed when not in debug mode. + - Require CGI::Simple::Cookie version 1.109 to ensure support for the + HttpOnly flag + - Allow the myapp_test.pl script to be given a list of paths which it + will retrieve all of. (RT#53653) + - Allow parameterized roles to be applied as plugins. + - Allow requiring minimum versions of plugins when loading them. + + Documentation: + - The Catalyst::Test::get method is documented as returning the raw + response bytes without any character decoding (RT#53678) + + Cleanups: + - Removal of $Catalyst::PRETTY_VERSION. Future releases will always have the + full and unmangled version number, including trailing zeroes, in + $Catalyst::VERSION. + +5.80022 2010-03-28 19:43:01 + New features: - Log an extra line in debug mode with the response status code, the content type and content length if available. Refactoring / optimizations: - Display of the end of hit debug messages has been factored out into - log_headers, log_request and log_response methods which all call + log_headers, log_request, log_request_headers, log_response, + log_response_status_line and log_response_headers methods so that + plugins which customise how much information is shown on the debug + screen as easy to write. + - Make all logging of request and response state get the information from $c->dump_these so that there is a unified point from which to hook in parameter filtering (for example). - $c->model/view/controller have become a lot faster for non-regexp names by using direct hash lookup instead of looping. + - IP address => hostname mapping for the server is only done once and cached + by Catalyst::Engine::HTTP to somewhat mitigate the problem of people + developing on machines pointed at slow DNS servers. - Bug fixed: + Bugs fixed: - DispatchType::Index's uri_for_action only returns for actions registered with it (prevents 'index :Path' or similar resolving to the wrong URI) + - Make sure to construct Upload objects properly, even if there are + multiple Content-Type headers (Closes RT#55976). 5.80021 2010-03-03 23:02:01 Bug fixed: - - $c->uri_for will now escape unsafe characterss in captures + - $c->uri_for will now escape unsafe characters in captures ($c->request->captures) and correctly encode utf8 charracters. 5.80020 2010-02-04 06:51:18 diff --git a/Makefile.PL b/Makefile.PL index 568ea2c..3b2305c 100644 --- a/Makefile.PL +++ b/Makefile.PL @@ -19,15 +19,16 @@ requires 'namespace::clean' => '0.13'; requires 'B::Hooks::EndOfScope' => '0.08'; requires 'MooseX::Emulate::Class::Accessor::Fast' => '0.00903'; requires 'Class::MOP' => '0.95'; -requires 'Moose' => '0.93'; +requires 'Moose' => '1.03'; requires 'MooseX::MethodAttributes::Inheritable' => '0.19'; requires 'MooseX::Role::WithOverloading' => '0.05'; requires 'Carp'; requires 'Class::C3::Adopt::NEXT' => '0.07'; -requires 'CGI::Simple::Cookie'; +requires 'CGI::Simple::Cookie' => '1.109'; requires 'Data::Dump'; +requires 'Data::OptList'; requires 'HTML::Entities'; -requires 'HTTP::Body' => '1.04'; # makes uploadtmp work +requires 'HTTP::Body' => '1.06'; # ->cleanup(1) requires 'HTTP::Headers' => '1.64'; requires 'HTTP::Request' => '5.814'; requires 'HTTP::Response' => '5.813'; @@ -101,7 +102,8 @@ print <<"EOF"; you also install the development tools package Catalyst::Devel. perl -MCPANPLUS -e 'install Catalyst::Devel' # or - perl -MCPAN -e 'install Catalyst::Devel' + perl -MCPAN -e 'install Catalyst::Devel' # or + cpanm Catalyst::Devel To get some commonly used plugins, as well as the TT view and DBIC model, install Task::Catalyst in the same way. diff --git a/lib/Catalyst.pm b/lib/Catalyst.pm index 3a43f54..1013d21 100644 --- a/lib/Catalyst.pm +++ b/lib/Catalyst.pm @@ -14,6 +14,7 @@ use Catalyst::Request::Upload; use Catalyst::Response; use Catalyst::Utils; use Catalyst::Controller; +use Data::OptList; use Devel::InnerPackage (); use File::stat; use Module::Pluggable::Object (); @@ -78,10 +79,7 @@ __PACKAGE__->stats_class('Catalyst::Stats'); # Remember to update this in Catalyst::Runtime as well! -our $VERSION = '5.80021'; -our $PRETTY_VERSION = $VERSION; - -$VERSION = eval $VERSION; +our $VERSION = '5.80024'; sub import { my ( $class, @arguments ) = @_; @@ -283,14 +281,15 @@ Specifies a comma-delimited list of log levels. =head2 -Stats -Enables statistics collection and reporting. You can also force this setting -from the system environment with CATALYST_STATS or _STATS. The -environment settings override the application, with _STATS having the -highest priority. +Enables statistics collection and reporting. + + use Catalyst qw/-Stats=1/; -e.g. +You can also force this setting from the system environment with CATALYST_STATS +or _STATS. The environment settings override the application, with +_STATS having the highest priority. - use Catalyst qw/-Stats=1/ +Stats are also enabled if L<< debugging |/"-Debug" >> is enabled. =head1 METHODS @@ -893,7 +892,7 @@ component is constructed. For example: MyApp->config({ 'Model::Foo' => { bar => 'baz', overrides => 'me' } }); - MyApp::Model::Foo->config({ quux => 'frob', 'overrides => 'this' }); + MyApp::Model::Foo->config({ quux => 'frob', overrides => 'this' }); will mean that C receives the following data when constructed: @@ -904,6 +903,21 @@ constructed: overrides => 'me', }); +It's common practice to use a Moose attribute +on the receiving component to access the config value. + + package MyApp::Model::Foo; + + use Moose; + + # this attr will receive 'baz' at construction time + has 'bar' => ( + is => 'rw', + isa => 'Str', + ); + +You can then get the value 'baz' by calling $c->model('Foo')->bar + =cut around config => sub { @@ -1162,7 +1176,7 @@ EOF if ( $class->debug ) { my $name = $class->config->{name} || 'Application'; - $class->log->info("$name powered by Catalyst $Catalyst::PRETTY_VERSION"); + $class->log->info("$name powered by Catalyst $Catalyst::VERSION"); } # Make sure that the application class becomes immutable at this point, @@ -1282,13 +1296,11 @@ sub uri_for { carp "uri_for called with undef argument" if grep { ! defined $_ } @args; foreach my $arg (@args) { utf8::encode($arg) if utf8::is_utf8($arg); - } - s/([^$URI::uric])/$URI::Escape::escapes{$1}/go for @args; - if (blessed $path) { # Action object only. - s|/|%2F|g for @args; + $arg =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go; } if ( blessed($path) ) { # action object + s|/|%2F|g for @args; my $captures = [ map { s|/|%2F|g; $_; } ( scalar @args && ref $args[0] eq 'ARRAY' ? @{ shift(@args) } @@ -1309,8 +1321,6 @@ sub uri_for { $path = '/' if $path eq ''; } - undef($path) if (defined $path && $path eq ''); - unshift(@args, $path); unless (defined $path && $path =~ s!^/!!) { # in-place strip @@ -1516,7 +1526,7 @@ sub welcome_message { models, and views; they can save you a lot of work.

-
script/${prefix}_create.pl -help
+
script/${prefix}_create.pl --help

Also, be sure to check out the vast and growing collection of plugins for Catalyst on CPAN; you are likely to find what you need there. @@ -1893,7 +1903,7 @@ namespaces. sub get_actions { my $c = shift; $c->dispatcher->get_actions( $c, @_ ) } -=head2 $c->handle_request( $class, @arguments ) +=head2 $app->handle_request( @arguments ) Called to handle each HTTP request. @@ -1953,7 +1963,7 @@ sub prepare { #surely this is not the most efficient way to do things... $c->stats($class->stats_class->new)->enable($c->use_stats); - if ( $c->debug ) { + if ( $c->debug || $c->config->{enable_catalyst_header} ) { $c->res->headers->header( 'X-Catalyst' => $Catalyst::VERSION ); } @@ -2108,8 +2118,6 @@ Writes information about the request to the debug logs. This includes: =item * Request method, path, and remote IP address -=item * Request headers (see L) - =item * Query keywords (see L) =item * Request parameters @@ -2134,7 +2142,7 @@ sub log_request { $address ||= ''; $c->log->debug(qq/"$method" request for "$path" from "$address"/); - $c->log_headers('request', $request->headers); + $c->log_request_headers($request->headers); if ( my $keywords = $request->query_keywords ) { $c->log->debug("Query keywords are: $keywords"); @@ -2147,35 +2155,60 @@ sub log_request { =head2 $c->log_response -Writes information about the response to the debug logs. This includes: +Writes information about the response to the debug logs by calling +C<< $c->log_response_status_line >> and C<< $c->log_response_headers >>. + +=cut + +sub log_response { + my $c = shift; + + return unless $c->debug; + + my($dump) = grep {$_->[0] eq 'Response' } $c->dump_these; + my $response = $dump->[1]; + + $c->log_response_status_line($response); + $c->log_response_headers($response->headers); +} + +=head2 $c->log_response_status_line($response) + +Writes one line of information about the response to the debug logs. This includes: =over 4 =item * Response status code -=item * Response headers (see L) +=item * Content-Type header (if present) + +=item * Content-Length header (if present) =back =cut -sub log_response { - my $c = shift; +sub log_response_status_line { + my ($c, $response) = @_; - return unless $c->debug; + $c->log->debug( + sprintf( + 'Response Code: %s; Content-Type: %s; Content-Length: %s', + $response->status || 'unknown', + $response->headers->header('Content-Type') || 'unknown', + $response->headers->header('Content-Length') || 'unknown' + ) + ); +} - my($dump) = grep {$_->[0] eq 'Response' } $c->dump_these; - my $response = $dump->[1]; +=head2 $c->log_response_headers($headers); - $c->log->debug( - sprintf( - 'Response Code: %s; Content-Type: %s; Content-Length: %s', - $response->status || 'unknown', - $response->headers->header('Content-Type') || 'unknown', - $response->headers->header('Content-Length') || 'unknown' - ) - ); -} +Hook method which can be wrapped by plugins to log the responseheaders. +No-op in the default implementation. + +=cut + +sub log_response_headers {} =head2 $c->log_request_parameters( query => {}, body => {} ) @@ -2233,6 +2266,15 @@ sub log_request_uploads { } } +=head2 $c->log_request_headers($headers); + +Hook method which can be wrapped by plugins to log the request headers. +No-op in the default implementation. + +=cut + +sub log_request_headers {} + =head2 $c->log_headers($type => $headers) Logs L (either request or response) to the debug logs. @@ -2246,7 +2288,8 @@ sub log_headers { return unless $c->debug; - my $t = Text::SimpleTable->new( [ 35, 'Header Name' ], [ 40, 'Value' ] ); + my $column_width = Catalyst::Utils::term_width() - 28; + my $t = Text::SimpleTable->new( [ 15, 'Header Name' ], [ $column_width, 'Value' ] ); $headers->scan( sub { my ( $name, $value ) = @_; @@ -2378,10 +2421,6 @@ sub setup_components { # we know M::P::O found a file on disk so this is safe Catalyst::Utils::ensure_class_loaded( $component, { ignore_loaded => 1 } ); - - # Needs to be done as soon as the component is loaded, as loading a sub-component - # (next time round the loop) can cause us to get the wrong metaclass.. - $class->_controller_init_base_classes($component); } for my $component (@comps) { @@ -2391,7 +2430,6 @@ sub setup_components { : $class->expand_component_module( $component, $config ); for my $component (@expanded_components) { next if $comps{$component}; - $class->_controller_init_base_classes($component); # Also cover inner packages $class->components->{ $component } = $class->setup_component($component); } } @@ -2444,19 +2482,6 @@ sub expand_component_module { =cut -# FIXME - Ugly, ugly hack to ensure the we force initialize non-moose base classes -# nearest to Catalyst::Controller first, no matter what order stuff happens -# to be loaded. There are TODO tests in Moose for this, see -# f2391d17574eff81d911b97be15ea51080500003 -sub _controller_init_base_classes { - my ($app_class, $component) = @_; - return unless $component->isa('Catalyst::Controller'); - foreach my $class ( reverse @{ mro::get_linear_isa($component) } ) { - Moose::Meta::Class->initialize( $class ) - unless find_meta($class); - } -} - sub setup_component { my( $class, $component ) = @_; @@ -2757,13 +2782,8 @@ the plugin name does not begin with C. if $plugin->isa( 'Catalyst::Component' ); $proto->_plugins->{$plugin} = 1; unless ($instant) { - no strict 'refs'; - if ( my $meta = Class::MOP::get_metaclass_by_name($class) ) { - my @superclasses = ($plugin, $meta->superclasses ); - $meta->superclasses(@superclasses); - } else { - unshift @{"$class\::ISA"}, $plugin; - } + my $meta = Class::MOP::get_metaclass_by_name($class); + $meta->superclasses($plugin, $meta->superclasses); } return $class; } @@ -2772,22 +2792,29 @@ the plugin name does not begin with C. my ( $class, $plugins ) = @_; $class->_plugins( {} ) unless $class->_plugins; - $plugins ||= []; + $plugins = Data::OptList::mkopt($plugins || []); - my @plugins = Catalyst::Utils::resolve_namespace($class . '::Plugin', 'Catalyst::Plugin', @$plugins); + my @plugins = map { + [ Catalyst::Utils::resolve_namespace( + $class . '::Plugin', + 'Catalyst::Plugin', $_->[0] + ), + $_->[1], + ] + } @{ $plugins }; for my $plugin ( reverse @plugins ) { - Class::MOP::load_class($plugin); - my $meta = find_meta($plugin); + Class::MOP::load_class($plugin->[0], $plugin->[1]); + my $meta = find_meta($plugin->[0]); next if $meta && $meta->isa('Moose::Meta::Role'); - $class->_register_plugin($plugin); + $class->_register_plugin($plugin->[0]); } my @roles = - map { $_->name } - grep { $_ && blessed($_) && $_->isa('Moose::Meta::Role') } - map { find_meta($_) } + map { $_->[0]->name, $_->[1] } + grep { blessed($_->[0]) && $_->[0]->isa('Moose::Meta::Role') } + map { [find_meta($_->[0]), $_->[1]] } @plugins; Moose::Util::apply_all_roles( @@ -2801,15 +2828,24 @@ the plugin name does not begin with C. Returns an arrayref of the internal execution stack (actions that are currently executing). +=head2 $c->stats + +Returns the current timing statistics object. By default Catalyst uses +L, but can be set otherwise with +L<< stats_class|/"$c->stats_class" >>. + +Even if L<< -Stats|/"-Stats" >> is not enabled, the stats object is still +available. By enabling it with C< $c->stats->enabled(1) >, it can be used to +profile explicitly, although MyApp.pm still won't profile nor output anything +by itself. + =head2 $c->stats_class -Returns or sets the stats (timing statistics) class. +Returns or sets the stats (timing statistics) class. L is used by default. =head2 $c->use_stats -Returns 1 when stats collection is enabled. Stats collection is enabled -when the -Stats options is set, debug is on or when the _STATS -environment variable is set. +Returns 1 when L<< stats collection|/"-Stats" >> is enabled. Note that this is a static method, not an accessor and should be overridden by declaring C in your MyApp.pm, not by calling C<< $c->use_stats(1) >>. @@ -2907,6 +2943,12 @@ to be shown in hit debug tables in the test server. =item * +C - Controlls if the C or C environment +variable should be used for determining the request path. See L +for more information. + +=item * + C - See L. =back @@ -3136,6 +3178,8 @@ random: Roland Lammel Robert Sedlacek C<< >> +SpiceMan: Marcel Montes + sky: Arthur Bergman szbalint: Balint Szilakszi @@ -3150,8 +3194,12 @@ Will Hawes C willert: Sebastian Willert +wreis: Wallace Reis + Yuval Kogman, C +rainboxx: Matthias Dietrich, C + =head1 LICENSE This library is free software. You can redistribute it and/or modify it under diff --git a/lib/Catalyst/Component.pm b/lib/Catalyst/Component.pm index 1b2d462..28da155 100644 --- a/lib/Catalyst/Component.pm +++ b/lib/Catalyst/Component.pm @@ -170,7 +170,7 @@ to be stored in the application's component hash. C<< my $component_instance = $component->COMPONENT($app, $arguments); >> -If this method is present (as it is on all Catalyst::Component subclasses, +If this method is present (as it is on all Catalyst::Component subclasses), it is called by Catalyst during setup_components with the application class as $app and any config entry on the application for this component (for example, in the case of MyApp::Controller::Foo this would be @@ -185,7 +185,7 @@ something like this: sub COMPONENT { my ($class, $app, $args) = @_; - $args = $self->merge_config_hashes($self->config, $args); + $args = $class->merge_config_hashes($class->config, $args); return $class->new($app, $args); } @@ -200,6 +200,12 @@ key value pair, or you can specify a hashref. In either case the keys will be merged with any existing config settings. Each component in a Catalyst application has its own config hash. +The component's config hash is merged with any config entry on the +application for this component and passed to C (as mentioned +above at L). The common practice to access the merged +config is to use a Moose attribute for each config entry on the +receiving component. + =head2 $c->process() This is the default method called on a Catalyst component in the dispatcher. diff --git a/lib/Catalyst/Controller.pm b/lib/Catalyst/Controller.pm index 9f52b3f..b1c4c94 100644 --- a/lib/Catalyst/Controller.pm +++ b/lib/Catalyst/Controller.pm @@ -225,7 +225,9 @@ sub register_action_methods { foreach my $method (@methods) { my $name = $method->name; - my $attributes = $method->attributes; + # Horrible hack! All method metaclasses should have an attributes + # method, core Moose bug - see r13354. + my $attributes = $method->can('attributes') ? $method->attributes : []; my $attrs = $self->_parse_attrs( $c, $name, @{ $attributes } ); if ( $attrs->{Private} && ( keys %$attrs > 1 ) ) { $c->log->debug( 'Bad action definition "' @@ -248,16 +250,25 @@ sub register_action_methods { } } -sub create_action { +sub action_class { my $self = shift; my %args = @_; my $class = (exists $args{attributes}{ActionClass} - ? $args{attributes}{ActionClass}[0] - : $self->_action_class); + ? $args{attributes}{ActionClass}[0] + : $self->_action_class); + Class::MOP::load_class($class); + return $class; +} +sub create_action { + my $self = shift; + my %args = @_; + + my $class = $self->action_class(%args); my $action_args = $self->config->{action_args}; + my %extra_args = ( %{ $action_args->{'*'} || {} }, %{ $action_args->{ $args{name} } || {} }, @@ -446,11 +457,31 @@ of setting namespace to '' (the null string). Sets 'path_prefix', as described below. +=head2 action + +Allows you to set the attributes that the dispatcher creates actions out of. +This allows you to do 'rails style routes', or override some of the +attribute defintions of actions composed from Roles. +You can set arguments globally (for all actions of the controller) and +specifically (for a single action). + + __PACKAGE__->config( + action => { + '*' => { Chained => 'base', Args => 0 }, + base => { Chained => '/', PathPart => '', CaptureArgs => 0 }, + }, + ); + +In the case above every sub in the package would be made into a Chain +endpoint with a URI the same as the sub name for each sub, chained +to the sub named C. Ergo dispatch to C would call the +C method, then the C method. + =head2 action_args Allows you to set constructor arguments on your actions. You can set arguments -globally (for all actions of the controller) and specifically (for a single -action). This is particularly useful when using Cs +globally and specifically (as above). +This is particularly useful when using Cs (L) and custom Ces. __PACKAGE__->config( @@ -509,6 +540,11 @@ action methods for this package. Creates action objects for a set of action methods using C< create_action >, and registers them with the dispatcher. +=head2 $self->action_class(%args) + +Used when a controller is creating an action to determine the correct base +action class to use. + =head2 $self->create_action(%args) Called with a hash of data to be use for construction of a new diff --git a/lib/Catalyst/DispatchType/Regex.pm b/lib/Catalyst/DispatchType/Regex.pm index 0d6da04..4b1beae 100644 --- a/lib/Catalyst/DispatchType/Regex.pm +++ b/lib/Catalyst/DispatchType/Regex.pm @@ -151,6 +151,7 @@ sub uri_for_action { my $re = "$orig"; $re =~ s/^\^//; $re =~ s/\$$//; + $re =~ s/\\([^\\])/$1/g; my $final = '/'; my @captures = @$captures; while (my ($front, $rest) = split(/\(/, $re, 2)) { diff --git a/lib/Catalyst/Engine.pm b/lib/Catalyst/Engine.pm index 7ba4167..e770f32 100644 --- a/lib/Catalyst/Engine.pm +++ b/lib/Catalyst/Engine.pm @@ -10,6 +10,8 @@ use HTML::Entities; use HTTP::Body; use HTTP::Headers; use URI::QueryParam; +use Encode (); +use utf8; use namespace::clean -except => 'meta'; @@ -131,6 +133,14 @@ sub finalize_error { $c->res->content_type('text/html; charset=utf-8'); my $name = ref($c)->config->{name} || join(' ', split('::', ref $c)); + + # Prevent Catalyst::Plugin::Unicode::Encoding from running. + # This is a little nasty, but it's the best way to be clean whether or + # not the user has an encoding plugin. + + if ($c->can('encoding')) { + $c->{encoding} = ''; + } my ( $title, $error, $infos ); if ( $c->debug ) { @@ -279,11 +289,12 @@ sub finalize_error { - # Trick IE. Old versions of IE would display their own error page instead # of ours if we'd give it less than 512 bytes. $c->res->{body} .= ( ' ' x 512 ); + $c->res->{body} = Encode::encode("UTF-8", $c->res->{body}); + # Return 500 $c->res->status(500); } @@ -311,6 +322,8 @@ Clean up after uploads, deleting temp files. sub finalize_uploads { my ( $self, $c ) = @_; + # N.B. This code is theoretically entirely unneeded due to ->cleanup(1) + # on the HTTP::Body object. my $request = $c->request; foreach my $key (keys %{ $request->uploads }) { my $upload = $request->uploads->{$key}; @@ -335,6 +348,7 @@ sub prepare_body { unless ( $request->_body ) { my $type = $request->header('Content-Type'); $request->_body(HTTP::Body->new( $type, $length )); + $request->_body->cleanup(1); # Make extra sure! $request->_body->tmpdir( $appclass->config->{uploadtmp} ) if exists $appclass->config->{uploadtmp}; } @@ -543,7 +557,7 @@ sub prepare_uploads { my $u = Catalyst::Request::Upload->new ( size => $upload->{size}, - type => $headers->content_type, + type => scalar $headers->content_type, headers => $headers, tempname => $upload->{tempname}, filename => $upload->{filename}, @@ -705,13 +719,13 @@ sub unescape_uri { =head2 $self->env -Hash containing enviroment variables including many special variables inserted +Hash containing environment variables including many special variables inserted by WWW server - like SERVER_*, REMOTE_*, HTTP_* ... -Before accesing enviroment variables consider whether the same information is +Before accessing environment variables consider whether the same information is not directly available via Catalyst objects $c->request, $c->engine ... -BEWARE: If you really need to access some enviroment variable from your Catalyst +BEWARE: If you really need to access some environment variable from your Catalyst application you should use $c->engine->env->{VARNAME} instead of $ENV{VARNAME}, as in some enviroments the %ENV hash does not contain what you would expect. diff --git a/lib/Catalyst/Engine/CGI.pm b/lib/Catalyst/Engine/CGI.pm index a8d64a5..076f5b0 100644 --- a/lib/Catalyst/Engine/CGI.pm +++ b/lib/Catalyst/Engine/CGI.pm @@ -28,6 +28,43 @@ appropriate engine module. This is the Catalyst engine specialized for the CGI environment. +=head1 PATH DECODING + +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). + +=head2 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 synthesised 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). + +=head2 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. + =head1 OVERLOADED METHODS This class overloads some methods from C. @@ -129,7 +166,7 @@ sub prepare_path { my $base_path; if ( exists $ENV{REDIRECT_URL} ) { $base_path = $ENV{REDIRECT_URL}; - $base_path =~ s/$ENV{PATH_INFO}$//; + $base_path =~ s/\Q$ENV{PATH_INFO}\E$//; } else { $base_path = $script_name || '/'; @@ -154,30 +191,19 @@ sub prepare_path { } } - # RFC 3875: "Unlike a URI path, the PATH_INFO is not URL-encoded, - # and cannot contain path-segment parameters." This means PATH_INFO - # is always decoded, and the script can't distinguish / vs %2F. - # See https://issues.apache.org/bugzilla/show_bug.cgi?id=35256 - # Here we try to resurrect the original encoded URI from REQUEST_URI. my $path_info = $ENV{PATH_INFO}; - if (my $req_uri = $ENV{REQUEST_URI}) { - $req_uri =~ s/^\Q$base_path\E//; - $req_uri =~ s/\?.*$//; - if ($req_uri) { - # Note that if REQUEST_URI doesn't start with a /, then the user - # is probably using mod_rewrite or something to rewrite requests - # into a sub-path of their application.. - # This means that REQUEST_URI needs information from PATH_INFO - # prepending to it to be useful, otherwise the sub path which is - # being redirected to becomes the app base address which is - # incorrect. - if (substr($req_uri, 0, 1) ne '/') { - my ($match) = $req_uri =~ m|^([^/]+)|; - my ($path_info_part) = $path_info =~ m|^(.*?\Q$match\E)|; - substr($req_uri, 0, length($match), $path_info_part) - if $path_info_part; + if ($c->config->{use_request_uri_for_path}) { + # RFC 3875: "Unlike a URI path, the PATH_INFO is not URL-encoded, + # and cannot contain path-segment parameters." This means PATH_INFO + # is always decoded, and the script can't distinguish / vs %2F. + # See https://issues.apache.org/bugzilla/show_bug.cgi?id=35256 + # Here we try to resurrect the original encoded URI from REQUEST_URI. + if (my $req_uri = $ENV{REQUEST_URI}) { + if (defined $script_name) { + $req_uri =~ s/^\Q$script_name\E//; } - $path_info = $req_uri; + $req_uri =~ s/\?.*$//; + $path_info = $req_uri if $req_uri; } } diff --git a/lib/Catalyst/Engine/HTTP.pm b/lib/Catalyst/Engine/HTTP.pm index 7f01795..97cd8d6 100644 --- a/lib/Catalyst/Engine/HTTP.pm +++ b/lib/Catalyst/Engine/HTTP.pm @@ -68,7 +68,8 @@ sub finalize_headers { # Should we keep the connection open? my $connection = $c->request->header('Connection'); - if ( $self->options->{keepalive} + if ( $self->options + && $self->options->{keepalive} && $connection && $connection =~ /^keep-alive$/i ) { @@ -534,13 +535,21 @@ sub _socket_data { peeraddr => $iaddr ? ( inet_ntoa($iaddr) || '127.0.0.1' ) : '127.0.0.1', - localname => gethostbyaddr( $localiaddr, AF_INET ) || 'localhost', + localname => _gethostbyaddr( $localiaddr ), localaddr => inet_ntoa($localiaddr) || '127.0.0.1', }; return $data; } +{ # If you have a crappy DNS server then these can be slow, so cache 'em + my %hostname_cache; + sub _gethostbyaddr { + my $ip = shift; + $hostname_cache{$ip} ||= gethostbyaddr( $ip, AF_INET ) || 'localhost'; + } +} + sub _inet_addr { unpack "N*", inet_aton( $_[0] ) } =head2 options diff --git a/lib/Catalyst/Runtime.pm b/lib/Catalyst/Runtime.pm index 935ce79..0c37ca2 100644 --- a/lib/Catalyst/Runtime.pm +++ b/lib/Catalyst/Runtime.pm @@ -7,9 +7,7 @@ BEGIN { require 5.008004; } # Remember to update this in Catalyst as well! -our $VERSION='5.80021'; - -$VERSION = eval $VERSION; +our $VERSION = '5.80024'; =head1 NAME diff --git a/lib/Catalyst/Script/Create.pm b/lib/Catalyst/Script/Create.pm index 05b4a66..95c0710 100644 --- a/lib/Catalyst/Script/Create.pm +++ b/lib/Catalyst/Script/Create.pm @@ -46,7 +46,7 @@ sub run { Class::MOP::load_class($helper_class); my $helper = $helper_class->new( { '.newfiles' => !$self->force, mech => $self->mechanize } ); - $self->_getopt_full_usage unless $helper->mk_component( $self->application_name, @ARGV ); + $self->_getopt_full_usage unless $helper->mk_component( $self->application_name, @{$self->extra_argv} ); } @@ -68,7 +68,7 @@ Catalyst::Script::Create - Create a new Catalyst Component Examples: myapp_create.pl controller My::Controller myapp_create.pl controller My::Controller BindLex - myapp_create.pl -mechanize controller My::Controller + myapp_create.pl --mechanize controller My::Controller myapp_create.pl view My::View myapp_create.pl view MyView TT myapp_create.pl view TT TT diff --git a/lib/Catalyst/Script/Server.pm b/lib/Catalyst/Script/Server.pm index e1f1049..45156e3 100644 --- a/lib/Catalyst/Script/Server.pm +++ b/lib/Catalyst/Script/Server.pm @@ -231,7 +231,7 @@ Catalyst::Script::Server - Catalyst test server a restart when modified (defaults to '\.yml$|\.yaml$|\.conf|\.pm$') --rdir --restart_directory the directory to search for - modified files, can be set mulitple times + modified files, can be set multiple times (defaults to '[SCRIPT_DIR]/..') --sym --follow_symlinks follow symlinks in search directories (defaults to false. this is a no-op on Win32) diff --git a/lib/Catalyst/Script/Test.pm b/lib/Catalyst/Script/Test.pm index 53473a4..36934b2 100644 --- a/lib/Catalyst/Script/Test.pm +++ b/lib/Catalyst/Script/Test.pm @@ -10,7 +10,9 @@ sub run { Catalyst::Test->import($self->application_name); - print request($self->ARGV->[0])->content . "\n"; + foreach my $arg (@{ $self->ARGV }) { + print request($arg)->content . "\n"; + } } diff --git a/lib/Catalyst/Test.pm b/lib/Catalyst/Test.pm index f987172..e940be0 100644 --- a/lib/Catalyst/Test.pm +++ b/lib/Catalyst/Test.pm @@ -202,6 +202,9 @@ method and the L method below: is ( $uri->path , '/y'); my $content = get($uri->path); +Note also that the content is returned as raw bytes, without any attempt +to decode it into characters. + =head2 $res = request( ... ); Returns an L object. Accepts an optional hashref for request diff --git a/script/catalyst.pl b/script/catalyst.pl index ef9771c..a0c2324 100755 --- a/script/catalyst.pl +++ b/script/catalyst.pl @@ -142,7 +142,7 @@ runs the generated application as a FastCGI script =item C -runs an action of the generated application from the comand line. +runs an action of the generated application from the command line. =back diff --git a/t/aggregate/live_component_controller_args.t b/t/aggregate/live_component_controller_args.t index 29d26a1..93b757c 100644 --- a/t/aggregate/live_component_controller_args.t +++ b/t/aggregate/live_component_controller_args.t @@ -73,15 +73,15 @@ sub run_test_for { my $response; - ok( $response = request("http://localhost/args/args/$path"), "Requested args for path $path"); + ok( $response = request("http://localhost/args/args/$path"), "Requested /args/args/$path"); is( $response->content, $test, "$test as args" ); undef $response; - ok( $response = request("http://localhost/args/params/$path"), "Requested params for path $path"); + ok( $response = request("http://localhost/args/params/$path"), "Requested /args/params/$path"); - is( $response->content, $test, "$test as params" ); + is( $response->content, $test, "response content $test as params" ); undef $response; diff --git a/t/aggregate/unit_core_ctx_attr.t b/t/aggregate/unit_core_ctx_attr.t new file mode 100644 index 0000000..219600f --- /dev/null +++ b/t/aggregate/unit_core_ctx_attr.t @@ -0,0 +1,30 @@ +use strict; +use warnings; +use FindBin qw/$Bin/; +use lib "$FindBin::Bin/../lib"; +use Test::More; +use URI; + +use_ok('TestApp'); + +my $request = Catalyst::Request->new( { + base => URI->new('http://127.0.0.1/foo') + } ); +my $dispatcher = TestApp->dispatcher; +my $context = TestApp->new( { + request => $request, + namespace => 'yada', + } ); + +is( $context->hello_lazy, 'hello there', '$context->hello_lazy'); +eval { is( $context->hello_notlazy, 'hello there', '$context->hello_notlazy') }; +TODO: { + local $TODO = 'we appear to have a lazy bug'; + if ($@) { + fail('$context->hello_notlazy'); + warn $@; + } +} + +done_testing; + diff --git a/t/aggregate/unit_core_engine_cgi-prepare_path.t b/t/aggregate/unit_core_engine_cgi-prepare_path.t index 9e297a1..2f30ce9 100644 --- a/t/aggregate/unit_core_engine_cgi-prepare_path.t +++ b/t/aggregate/unit_core_engine_cgi-prepare_path.t @@ -8,18 +8,18 @@ use Catalyst::Engine::CGI; # mod_rewrite to app root for non / based app { - my $r = get_req ( + my $r = get_req (0, REDIRECT_URL => '/comics/', SCRIPT_NAME => '/comics/dispatch.cgi', REQUEST_URI => '/comics/', ); - is ''.$r->uri, 'http://www.foo.com/comics/'; - is ''.$r->base, 'http://www.foo.com/comics/'; + is ''.$r->uri, 'http://www.foo.com/comics/', 'uri is correct'; + is ''.$r->base, 'http://www.foo.com/comics/', 'base is correct'; } # mod_rewrite to sub path under app root for non / based app { - my $r = get_req ( + my $r = get_req (0, PATH_INFO => '/foo/bar.gif', REDIRECT_URL => '/comics/foo/bar.gif', SCRIPT_NAME => '/comics/dispatch.cgi', @@ -31,7 +31,7 @@ use Catalyst::Engine::CGI; # Standard CGI hit for non / based app { - my $r = get_req ( + my $r = get_req (0, PATH_INFO => '/static/css/blueprint/screen.css', SCRIPT_NAME => '/~bobtfish/Gitalist/script/gitalist.cgi', REQUEST_URI => '/~bobtfish/Gitalist/script/gitalist.cgi/static/css/blueprint/screen.css', @@ -41,19 +41,19 @@ use Catalyst::Engine::CGI; } # / %2F %252F escaping case. { - my $r = get_req ( + my $r = get_req (1, PATH_INFO => '/%2F/%2F', SCRIPT_NAME => '/~bobtfish/Gitalist/script/gitalist.cgi', REQUEST_URI => '/~bobtfish/Gitalist/script/gitalist.cgi/%252F/%252F', ); - is ''.$r->uri, 'http://www.foo.com/~bobtfish/Gitalist/script/gitalist.cgi/%252F/%252F'; - is ''.$r->base, 'http://www.foo.com/~bobtfish/Gitalist/script/gitalist.cgi/'; + is ''.$r->uri, 'http://www.foo.com/~bobtfish/Gitalist/script/gitalist.cgi/%252F/%252F', 'uri correct'; + is ''.$r->base, 'http://www.foo.com/~bobtfish/Gitalist/script/gitalist.cgi/', 'base correct'; } # Using rewrite rules to ask for a sub-path in your app. # E.g. RewriteRule ^(.*)$ /path/to/fastcgi/domainprofi.fcgi/iframeredirect$1 [L,NS] { - my $r = get_req ( + my $r = get_req (0, PATH_INFO => '/iframeredirect/info', SCRIPT_NAME => '', REQUEST_URI => '/info', @@ -64,7 +64,7 @@ use Catalyst::Engine::CGI; # nginx example from espent with path /"foo" { - my $r = get_req ( + my $r = get_req (0, PATH_INFO => '"foo"', SCRIPT_NAME => '/', REQUEST_URI => '/%22foo%22', @@ -76,19 +76,19 @@ use Catalyst::Engine::CGI; # nginx example from espent with path /"foo" and the app based at /oslobilder { - my $r = get_req ( + my $r = get_req (1, PATH_INFO => 'oslobilder/"foo"', SCRIPT_NAME => '/oslobilder/', REQUEST_URI => '/oslobilder/%22foo%22', ); - is ''.$r->path, '%22foo%22'; - is ''.$r->uri, 'http://www.foo.com/oslobilder/%22foo%22'; - is ''.$r->base, 'http://www.foo.com/oslobilder/'; + is ''.$r->path, '%22foo%22', 'path correct'; + is ''.$r->uri, 'http://www.foo.com/oslobilder/%22foo%22', 'uri correct'; + is ''.$r->base, 'http://www.foo.com/oslobilder/', 'base correct'; } # CGI hit on IIS for non / based app { - my $r = get_req ( + my $r = get_req(0, SERVER_SOFTWARE => 'Microsoft-IIS/6.0', PATH_INFO => '/bobtfish/Gitalist/script/gitalist.cgi/static/css/blueprint/screen.css', SCRIPT_NAME => '/bobtfish/Gitalist/script/gitalist.cgi', @@ -99,6 +99,31 @@ use Catalyst::Engine::CGI; is ''.$r->base, 'http://www.foo.com/bobtfish/Gitalist/script/gitalist.cgi/'; } +{ + my $r = get_req (0, + PATH_INFO => '/auth/login', + SCRIPT_NAME => '/tx', + REQUEST_URI => '/login', + ); + is ''.$r->path, 'auth/login', 'path correct'; + is ''.$r->uri, 'http://www.foo.com/tx/auth/login', 'uri correct'; + is ''.$r->base, 'http://www.foo.com/tx/', 'base correct'; +} + +# test req->base and c->uri_for work correctly after an internally redirected request +# (i.e. REDIRECT_URL set) when the PATH_INFO contains a regex +{ + my $path = '/engine/request/uri/Rx(here)'; + my $r = get_req (0, + SCRIPT_NAME => '/', + PATH_INFO => $path, + REQUEST_URI => $path, + REDIRECT_URL => $path, + ); + + is $r->path, 'engine/request/uri/Rx(here)', 'URI contains correct path'; + is $r->base, 'http://www.foo.com/', 'Base is correct'; +} # FIXME - Test proxy logic # - Test query string @@ -107,6 +132,8 @@ use Catalyst::Engine::CGI; # - Test scheme (secure request on port 80) sub get_req { + my $use_request_uri_for_path = shift; + my %template = ( HTTP_HOST => 'www.foo.com', PATH_INFO => '/', @@ -115,6 +142,9 @@ sub get_req { local %ENV = (%template, @_); my $i = TestApp->new; + $i->setup_finished(0); + $i->config(use_request_uri_for_path => $use_request_uri_for_path); + $i->setup_finished(1); $i->engine(Catalyst::Engine::CGI->new); $i->engine->prepare_path($i); return $i->req; diff --git a/t/aggregate/unit_core_path_to.t b/t/aggregate/unit_core_path_to.t index a89135c..f4fe89f 100644 --- a/t/aggregate/unit_core_path_to.t +++ b/t/aggregate/unit_core_path_to.t @@ -2,6 +2,9 @@ use strict; use warnings; use Test::More; +use FindBin; +use Path::Class; +use File::Basename; my %non_unix = ( MacOS => 1, @@ -16,17 +19,20 @@ my %non_unix = ( my $os = $non_unix{$^O} ? $^O : 'Unix'; -if( $os ne 'Unix' ) { +if ( $os ne 'Unix' ) { plan skip_all => 'tests require Unix'; } -else { - plan tests => 3; -} use_ok('Catalyst'); my $context = 'Catalyst'; +$context->setup_home; +my $base = dir($FindBin::Bin)->relative->stringify; + +isa_ok( Catalyst::path_to( $context, $base ), 'Path::Class::Dir' ); +isa_ok( Catalyst::path_to( $context, $base, basename $0 ), 'Path::Class::File' ); + my $config = Catalyst->config; $config->{home} = '/home/sri/my-app/'; @@ -37,3 +43,5 @@ $config->{home} = '/Users/sri/myapp/'; is( Catalyst::path_to( $context, 'foo', 'bar' ), '/Users/sri/myapp/foo/bar', 'deep Unix path' ); + +done_testing; diff --git a/t/aggregate/unit_core_plugin.t b/t/aggregate/unit_core_plugin.t index 11cef84..16a5e24 100644 --- a/t/aggregate/unit_core_plugin.t +++ b/t/aggregate/unit_core_plugin.t @@ -3,7 +3,7 @@ use strict; use warnings; -use Test::More tests => 24; +use Test::More; use lib 't/lib'; @@ -61,3 +61,4 @@ my @expected = qw( is_deeply [ TestApp->registered_plugins ], \@expected, 'registered_plugins() should only report the plugins for the current class'; +done_testing; diff --git a/t/aggregate/unit_core_script_test.t b/t/aggregate/unit_core_script_test.t index 5f56681..71d0e4b 100644 --- a/t/aggregate/unit_core_script_test.t +++ b/t/aggregate/unit_core_script_test.t @@ -1,6 +1,7 @@ use strict; use warnings; +use Carp qw(croak); use FindBin qw/$Bin/; use lib "$Bin/../lib"; diff --git a/t/aggregate/unit_core_uri_for_action.t b/t/aggregate/unit_core_uri_for_action.t index 89079f9..ccc5910 100644 --- a/t/aggregate/unit_core_uri_for_action.t +++ b/t/aggregate/unit_core_uri_for_action.t @@ -8,7 +8,7 @@ use lib "$FindBin::Bin/../lib"; use Test::More; -plan tests => 30; +plan tests => 33; use_ok('TestApp'); @@ -21,8 +21,6 @@ my $private_action = $dispatcher->get_action_by_path( '/class_forward_test_method' ); -warn $dispatcher->uri_for_action($private_action); - ok(!defined($dispatcher->uri_for_action($private_action)), "Private action returns undef for URI"); @@ -56,6 +54,21 @@ is($dispatcher->uri_for_action($regex_action, [ 'foo', 123 ]), "/action/regexp/foo/123", "Regex action interpolates captures correctly"); +my $regex_action_bs = $dispatcher->get_action_by_path( + '/action/regexp/one_backslashes' + ); + +ok(!defined($dispatcher->uri_for_action($regex_action_bs)), + "Regex action without captures returns undef"); + +ok(!defined($dispatcher->uri_for_action($regex_action_bs, [ 1, 2, 3 ])), + "Regex action with too many captures returns undef"); + +is($dispatcher->uri_for_action($regex_action_bs, [ 'foo', 123 ]), + "/action/regexp/foo/123.html", + "Regex action interpolates captures correctly"); + + # # Index Action # diff --git a/t/lib/PluginTestApp.pm b/t/lib/PluginTestApp.pm index 1031586..29a02cd 100644 --- a/t/lib/PluginTestApp.pm +++ b/t/lib/PluginTestApp.pm @@ -1,10 +1,13 @@ package PluginTestApp; use Test::More; -use Catalyst qw( - Test::Plugin - +TestApp::Plugin::FullyQualified - ); +use Catalyst ( + 'Test::Plugin', + '+TestApp::Plugin::FullyQualified', + (eval { require MooseX::Role::Parameterized; 1 } + ? ('+TestApp::Plugin::ParameterizedRole' => { method_name => 'affe' }) + : ()), +); sub _test_plugins { my $c = shift; diff --git a/t/lib/PluginTestApp/Controller/Root.pm b/t/lib/PluginTestApp/Controller/Root.pm index 5358074..7bec366 100644 --- a/t/lib/PluginTestApp/Controller/Root.pm +++ b/t/lib/PluginTestApp/Controller/Root.pm @@ -34,6 +34,13 @@ sub run_time_plugins : Local { ref($c)->plugin( faux => $faux_plugin ); isa_ok $c, 'Catalyst::Plugin::Test::Plugin'; + + # applied parameterized role + if (eval { require MooseX::Role::Parameterized; 1 }) { + can_ok $c, 'affe'; + is $c->affe, 'birne', 'right method created by parameterized role'; + } + isa_ok $c, 'TestApp::Plugin::FullyQualified'; ok !$c->isa($faux_plugin), '... and it should not inherit from the instant plugin'; diff --git a/t/lib/TestApp.pm b/t/lib/TestApp.pm index 1e4d5c4..3bd3763 100644 --- a/t/lib/TestApp.pm +++ b/t/lib/TestApp.pm @@ -16,9 +16,38 @@ use Catalyst::Utils; use Moose; use namespace::autoclean; +# ----------- +# t/aggregate/unit_core_ctx_attr.t pukes until lazy is true +package Greeting; +use Moose; +sub hello_notlazy { 'hello there' } +sub hello_lazy { 'hello there' } + +package TestApp; +has 'my_greeting_obj_notlazy' => ( + is => 'ro', + isa => 'Greeting', + default => sub { Greeting->new() }, + handles => [ qw( hello_notlazy ) ], + lazy => 0, +); +has 'my_greeting_obj_lazy' => ( + is => 'ro', + isa => 'Greeting', + default => sub { Greeting->new() }, + handles => [ qw( hello_lazy ) ], + lazy => 1, +); +# ----------- + our $VERSION = '0.01'; -TestApp->config( name => 'TestApp', root => '/some/dir' ); +TestApp->config( name => 'TestApp', root => '/some/dir', use_request_uri_for_path => 1 ); + +# Test bug found when re-adjusting the metaclass compat code in Moose +# in 292360. Test added to Moose in 4b760d6, but leave this attribute +# above ->setup so we have some generated methods to be double sure. +has an_attribute_before_we_change_base_classes => ( is => 'ro'); if ($::setup_leakchecker && eval { Class::MOP::load_class('CatalystX::LeakChecker'); 1 }) { with 'CatalystX::LeakChecker'; diff --git a/t/lib/TestApp/Controller/Action/Regexp.pm b/t/lib/TestApp/Controller/Action/Regexp.pm index 4b59d58..7966874 100644 --- a/t/lib/TestApp/Controller/Action/Regexp.pm +++ b/t/lib/TestApp/Controller/Action/Regexp.pm @@ -27,4 +27,9 @@ sub four : Action Regex('^action/regexp/redirect/(\w+)/universe/(\d+)/everything ); } +sub one_backslashes : Action Regex('^action/regexp/(\w+)/(\d+)\.html$') { + my ( $self, $c ) = @_; + $c->forward('TestApp::View::Dump::Request'); +} + 1; diff --git a/t/lib/TestApp/Controller/Moose/NoAttributes.pm b/t/lib/TestApp/Controller/Moose/NoAttributes.pm new file mode 100644 index 0000000..7770816 --- /dev/null +++ b/t/lib/TestApp/Controller/Moose/NoAttributes.pm @@ -0,0 +1,16 @@ +package TestApp::Controller::Moose::NoAttributes; +use Moose; +extends qw/Catalyst::Controller/; + +__PACKAGE__->config( + actions => { + test => { Local => undef } + } +); + +sub test { +} + +no Moose; +1; + diff --git a/t/lib/TestApp/Plugin/ParameterizedRole.pm b/t/lib/TestApp/Plugin/ParameterizedRole.pm new file mode 100644 index 0000000..f02d454 --- /dev/null +++ b/t/lib/TestApp/Plugin/ParameterizedRole.pm @@ -0,0 +1,18 @@ +package TestApp::Plugin::ParameterizedRole; + +use MooseX::Role::Parameterized; +use namespace::autoclean; + +parameter method_name => ( + isa => 'Str', + required => 1, +); + +role { + my $p = shift; + my $method_name = $p->method_name; + + method $method_name => sub { 'birne' }; +}; + +1; diff --git a/t/unit_core_methodattributes_method_metaclass_on_subclasses.t b/t/unit_core_methodattributes_method_metaclass_on_subclasses.t new file mode 100644 index 0000000..b6f5054 --- /dev/null +++ b/t/unit_core_methodattributes_method_metaclass_on_subclasses.t @@ -0,0 +1,30 @@ +use strict; +use Test::More; + +{ + package NoAttributes::CT; + use Moose; + BEGIN { extends qw/Catalyst::Controller/; }; + + sub test {} +} +{ + package NoAttributes::RT; + use Moose; + extends qw/Catalyst::Controller/; + + sub test {} +} + +foreach my $class (qw/ CT RT /) { + my $class_name = 'NoAttributes::' . $class; + my $meta = $class_name->meta; + my $meth = $meta->find_method_by_name('test'); + { + local $TODO = "Known MX::MethodAttributes issue"; + ok $meth->can('attributes'), 'method metaclass has ->attributes method for ' . $class;; + } +} + +done_testing; +