Merge remote-tracking branch 'upstream/master'
Ferruccio Zamuner [Wed, 25 Oct 2017 10:00:31 +0000 (12:00 +0200)]
30 files changed:
.travis.yml
Changes
Makefile.PL
README.mkdn
lib/Catalyst.pm
lib/Catalyst/Component/ContextClosure.pm
lib/Catalyst/Controller.pm
lib/Catalyst/DispatchType/Chained.pm
lib/Catalyst/Dispatcher.pm
lib/Catalyst/Exception/Basic.pm
lib/Catalyst/Exception/Interface.pm
lib/Catalyst/Runtime.pm
lib/Catalyst/ScriptRole.pm
lib/Catalyst/Utils.pm
t/abort-chain-1.t [new file with mode: 0644]
t/abort-chain-2.t [new file with mode: 0644]
t/abort-chain-3.t [new file with mode: 0644]
t/aggregate/live_component_controller_actionroles.t
t/aggregate/unit_core_log_autoflush.t
t/aggregate/unit_core_script_server-without_modules.t
t/aggregate/unit_core_uri_for.t
t/author/spelling.t
t/class_traits.t
t/class_traits_CAR_bug.t
t/evil_stash.t [new file with mode: 0644]
t/lib/Catalyst/ActionRole/Guff.pm [moved from t/lib/Catalyst/ActionRole/Moo.pm with 80% similarity]
t/lib/Guff.pm [moved from t/lib/Moo.pm with 91% similarity]
t/lib/TestApp/ActionRole/Guff.pm [moved from t/lib/TestApp/ActionRole/Moo.pm with 77% similarity]
t/lib/TestApp/Controller/ActionRoles.pm
t/relative_root_action_for_bug.t [new file with mode: 0644]

index b3bb3b9..9a04424 100644 (file)
@@ -1,6 +1,8 @@
 language: perl
 sudo: false
 perl:
+   - "5.24"
+   - "5.22"
    - "5.20"
    - "5.18"
    - "5.16"
@@ -51,7 +53,7 @@ script:
    - cpanm --test-only --metacpan Catalyst::Component::InstancePerContext
    - cpanm --test-only --metacpan Catalyst::Plugin::Session
    - cpanm --test-only --metacpan Catalyst::Plugin::Session::State::Cookie
-   - cpanm --test-only --metacpan Catalyst::Plugin::Static::Simple
+   - cpanm --test-only --verbose --metacpan Catalyst::Plugin::Static::Simple
    - cpanm --test-only --metacpan Catalyst::Plugin::ConfigLoader
    - cpanm --test-only --metacpan Catalyst::Plugin::ConfigLoader
    - cpanm --test-only --metacpan Catalyst::Authentication::Credential::HTTP
diff --git a/Changes b/Changes
index 5a70eef..0fc85ac 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,30 @@
 # This file documents the revision history for Perl extension Catalyst.
 
+5.90115 - 2017-05-01
+  - fixes for silent bad behavior in Catalyst::ScriptRole and 'ensure_class_loaded'
+    (hobbs++)
+  - do not require MXRWO if Moose is new enough to have cored it (ether++)
+  - documentation improvements (ether++)
+  - Encoding documentation improvements (colinnewell++)
+  - Improve documentation and test cases for 'abort_chain_on_error_fix' configuration
+    option (melmothx++)
+  - Better debug output when using Hash::MultiValue (tremor69++)
+  - Fixes for detecting debug terminal size (simonamor++)
+
+5.90114 - 2016-12-19
+  - Fixed regression introduced in the last version (5.90113) which caused 
+    application to hang when the action private name contained a string
+    like 'foo/bar..html'.  If you are running 5.90113 you should consider this
+    a required update.
+  - Tweaked travis CI script.
+
+5.90113 - 2016-12-15
+  - Fixed issue with $controller->action_for when targeting an action in
+    a namespace nested inside the current controller and the current controller
+    is a 'root' controller.
+  - Enhanced $controller->action_for so that you can reference the 'parent'
+    controller via relative path (eg ->action_for('../foo')).
+  - Backcompat fix for people that made the mistake of doing $c->{stash}
   - Sort controllers in setup_actions so cross-controller precedence is
     consistent.
 
index 68dd556..f1d8607 100644 (file)
@@ -26,7 +26,7 @@ author 'Sebastian Riedel <sri@cpan.org>';
 authority('cpan:MSTROUT');
 all_from 'lib/Catalyst/Runtime.pm';
 
-requires 'List::MoreUtils';
+requires 'List::Util' => '1.45';
 requires 'namespace::autoclean' => '0.28';
 requires 'namespace::clean' => '0.23';
 requires 'MooseX::Emulate::Class::Accessor::Fast' => '0.00903';
@@ -34,7 +34,7 @@ requires 'Class::Load' => '0.12';
 requires 'Data::OptList';
 requires 'Moose' => '1.03';
 requires 'MooseX::MethodAttributes::Role::AttrContainer::Inheritable' => '0.24';
-requires 'MooseX::Role::WithOverloading' => '0.09';
+requires 'MooseX::Role::WithOverloading' => '0.09' unless can_use('Moose', '2.1300');
 requires 'Carp' => '1.25';
 requires 'Class::C3::Adopt::NEXT' => '0.07';
 requires 'CGI::Simple::Cookie' => '1.109';
@@ -120,7 +120,6 @@ author_requires(
   @author_requires,
   map {; $_ => 0 } qw(
   File::Copy::Recursive
-  Test::Without::Module
   Starman
   MooseX::Daemonize
   Test::NoTabs
@@ -142,6 +141,7 @@ resources(
     'license',    => 'http://dev.perl.org/licenses/',
     'homepage',   => 'http://dev.catalyst.perl.org/',
     # r/w: catagits@git.shadowcat.co.uk:Catalyst-Runtime.git
+    # web: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits/Catalyst-Runtime.git;a=summary
     'repository', => 'git://git.shadowcat.co.uk/catagits/Catalyst-Runtime.git',
 );
 
index 7889225..203d418 100644 (file)
@@ -2,23 +2,11 @@
 
 Catalyst - The Elegant MVC Web Application Framework
 
-<div>
-
-    <a href="https://badge.fury.io/pl/Catalyst-Runtime"><img src="https://badge.fury.io/pl/Catalyst-Runtime.svg" alt="CPAN version" height="18"></a>
-    <a href="https://travis-ci.org/perl-catalyst/catalyst-runtime/"><img src="https://api.travis-ci.org/perl-catalyst/catalyst-runtime.png" alt="Catalyst></a>
-    <a href="http://cpants.cpanauthors.org/dist/Catalyst-Runtime"><img src="http://cpants.cpanauthors.org/dist/Catalyst-Runtime.png" alt='Kwalitee Score' /></a>
-</div>
-
 # SYNOPSIS
 
 See the [Catalyst::Manual](https://metacpan.org/pod/Catalyst::Manual) distribution for comprehensive
 documentation and tutorials.
 
-    # Building Catalyst for development
-    cpanm --local-lib=~/perl5 local::lib && eval $(perl -I ~/perl5/lib/perl5/ -Mlocal::lib)
-    cpanm --installdeps --with-develop .
-    perl Makefile.PL
-
     # Install Catalyst::Devel for helpers and other development tools
     # use the helper to create a new application
     catalyst.pl MyApp
@@ -296,7 +284,7 @@ the relationship between
 `$c->go` will perform a full dispatch on the specified action or method,
 with localized `$c->action` and `$c->namespace`. Like `detach`,
 `go` escapes the processing of the current request chain on completion, and
-does not return to its cunless blessed $cunless blessed $caller.
+does not return to its caller.
 
 @arguments are arguments to the final destination of $action. @captures are
 arguments to the intermediate steps, if any, on the way to the final sub of
@@ -362,6 +350,11 @@ Contains the return value of the last executed action.
 Note that << $c->state >> operates in a scalar context which means that all
 values it returns are scalar.
 
+Please note that if an action throws an exception, the value of state
+should no longer be considered the return if the last action.  It is generally
+going to be 0, which indicates an error state.  Examine $c->error for error
+details.
+
 ## $c->clear\_errors
 
 Clear errors.  You probably don't want to clear the errors unless you are
@@ -378,11 +371,17 @@ Returns true if you have errors
 ## $c->last\_error
 
 Returns the most recent error in the stack (the one most recently added...)
-or nothing if there are no errors.
+or nothing if there are no errors.  This does not modify the contents of the
+error stack.
 
 ## shift\_errors
 
-shifts the most recently added error off the error stack and returns if.  Returns
+shifts the most recently added error off the error stack and returns it.  Returns
+nothing if there are no more errors.
+
+## pop\_errors
+
+pops the most recently added error off the error stack and returns it.  Returns
 nothing if there are no more errors.
 
 ## COMPONENT ACCESSORS
@@ -637,11 +636,11 @@ Example:
         ## do stuff here..
     };
 
-## $c->uri\_for( $path?, @args?, \\%query\_values? )
+## $c->uri\_for( $path?, @args?, \\%query\_values?, \\$fragment? )
 
-## $c->uri\_for( $action, \\@captures?, @args?, \\%query\_values? )
+## $c->uri\_for( $action, \\@captures?, @args?, \\%query\_values?, \\$fragment? )
 
-## $c->uri\_for( $action, \[@captures, @args\], \\%query\_values? )
+## $c->uri\_for( $action, \[@captures, @args\], \\%query\_values?, \\$fragment? )
 
 Constructs an absolute [URI](https://metacpan.org/pod/URI) object based on the application root, the
 provided path, and the additional arguments and query parameters provided.
@@ -659,6 +658,15 @@ relative to the application root (if it does). It is then merged with
 `$c->request->base`; any `@args` are appended as additional path
 components; and any `%query_values` are appended as `?foo=bar` parameters.
 
+**NOTE** If you are using this 'stringy' first argument, we skip encoding and
+allow you to declare something like:
+
+    $c->uri_for('/foo/bar#baz')
+
+Where 'baz' is a URI fragment.  We consider this first argument string to be
+'expert' mode where you are expected to create a valid URL and we for the most
+part just pass it through without a lot of internal effort to escape and encode.
+
 If the first argument is a [Catalyst::Action](https://metacpan.org/pod/Catalyst::Action) it represents an action which
 will have its path resolved using `$c->dispatcher->uri_for_action`. The
 optional `\@captures` argument (an arrayref) allows passing the captured
@@ -976,7 +984,26 @@ Returns or sets the request class. Defaults to [Catalyst::Request](https://metac
 
 ## $app->request\_class\_traits
 
-An arrayref of [Moose::Role](https://metacpan.org/pod/Moose::Role)s which are applied to the request class.  
+An arrayref of [Moose::Role](https://metacpan.org/pod/Moose::Role)s which are applied to the request class.  You can
+name the full namespace of the role, or a namespace suffix, which will then
+be tried against the following standard namespace prefixes.
+
+    $MyApp::TraitFor::Request::$trait_suffix
+    Catalyst::TraitFor::Request::$trait_suffix
+
+So for example if you set:
+
+    MyApp->request_class_traits(['Foo']);
+
+We try each possible role in turn (and throw an error if none load)
+
+    Foo
+    MyApp::TraitFor::Request::Foo
+    Catalyst::TraitFor::Request::Foo
+
+The namespace part 'TraitFor::Request' was chosen to assist in backwards
+compatibility with [CatalystX::RoleApplicator](https://metacpan.org/pod/CatalystX::RoleApplicator) which previously provided
+these features in a stand alone package.
 
 ## $app->composed\_request\_class
 
@@ -988,7 +1015,26 @@ Returns or sets the response class. Defaults to [Catalyst::Response](https://met
 
 ## $app->response\_class\_traits
 
-An arrayref of [Moose::Role](https://metacpan.org/pod/Moose::Role)s which are applied to the response class.
+An arrayref of [Moose::Role](https://metacpan.org/pod/Moose::Role)s which are applied to the response class.  You can
+name the full namespace of the role, or a namespace suffix, which will then
+be tried against the following standard namespace prefixes.
+
+    $MyApp::TraitFor::Response::$trait_suffix
+    Catalyst::TraitFor::Response::$trait_suffix
+
+So for example if you set:
+
+    MyApp->response_class_traits(['Foo']);
+
+We try each possible role in turn (and throw an error if none load)
+
+    Foo
+    MyApp::TraitFor::Response::Foo
+    Catalyst::TraitFor::Responset::Foo
+
+The namespace part 'TraitFor::Response' was chosen to assist in backwards
+compatibility with [CatalystX::RoleApplicator](https://metacpan.org/pod/CatalystX::RoleApplicator) which previously provided
+these features in a stand alone package.
 
 ## $app->composed\_response\_class
 
@@ -1191,15 +1237,44 @@ Sets up the input/output encoding. See [ENCODING](https://metacpan.org/pod/ENCOD
 
 ## handle\_unicode\_encoding\_exception
 
-Hook to let you customize how encoding errors are handled.  By default
-we just throw an exception.  Receives a hashref of debug information.
-Example:
+Hook to let you customize how encoding errors are handled. By default
+we just throw an exception and the default error page will pick it up.
+Receives a hashref of debug information. Example of call (from the
+Catalyst internals):
 
-    $c->handle_unicode_encoding_exception({
-        param_value => $value,
-        error_msg => $_,
-            encoding_step => 'params',
-        });
+    my $decoded_after_fail = $c->handle_unicode_encoding_exception({
+          param_value => $value,
+          error_msg => $_,
+          encoding_step => 'params',
+     });
+
+The calling code expects to receive a decoded string or an exception.
+
+You can override this for custom handling of unicode errors. By
+default we just die. If you want a custom response here, one approach
+is to throw an HTTP style exception, instead of returning a decoded
+string or throwing a generic exception.
+
+    sub handle_unicode_encoding_exception {
+      my ($c, $params) = @_;
+      HTTP::Exception::BAD_REQUEST->throw(status_message=>$params->{error_msg});
+    }
+
+Alternatively you can 'catch' the error, stash it and write handling code later
+in your application:
+
+    sub handle_unicode_encoding_exception {
+      my ($c, $params) = @_;
+      $c->stash(BAD_UNICODE_DATA=>$params);
+      # return a dummy string.
+      return 1;
+    }
+
+<B>NOTE:&lt;/b> Please keep in mind that once an error like this occurs,
+the request setup is still ongoing, which means the state of `$c` and
+related context parts like the request and response may not be setup
+up correctly (since we haven't finished the setup yet). If you throw
+an exception the setup is aborted.
 
 ## $c->setup\_log
 
@@ -1365,7 +1440,26 @@ A arrayref of [Moose::Role](https://metacpan.org/pod/Moose::Role)s that are appl
 
 ## $app->composed\_stats\_class
 
-this is the stats\_class composed with any 'stats\_class\_traits'.
+this is the stats\_class composed with any 'stats\_class\_traits'.  You can
+name the full namespace of the role, or a namespace suffix, which will then
+be tried against the following standard namespace prefixes.
+
+    $MyApp::TraitFor::Stats::$trait_suffix
+    Catalyst::TraitFor::Stats::$trait_suffix
+
+So for example if you set:
+
+    MyApp->stats_class_traits(['Foo']);
+
+We try each possible role in turn (and throw an error if none load)
+
+    Foo
+    MyApp::TraitFor::Stats::Foo
+    Catalyst::TraitFor::Stats::Foo
+
+The namespace part 'TraitFor::Stats' was chosen to assist in backwards
+compatibility with [CatalystX::RoleApplicator](https://metacpan.org/pod/CatalystX::RoleApplicator) which previously provided
+these features in a stand alone package.
 
 ## $c->use\_stats
 
@@ -1445,7 +1539,7 @@ variable should be used for determining the request path.
         decoded, this means that applications using this mode can correctly handle URIs including the %2F character
         (i.e. with `AllowEncodedSlashes` set to `On` in Apache).
 
-        Given that this method of path resolution is probably more correct, it is recommended that you use
+        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.
 
@@ -1459,7 +1553,7 @@ variable should be used for determining the request path.
 - `using_frontend_proxy_path` - Enabled [Plack::Middleware::ReverseProxyPath](https://metacpan.org/pod/Plack::Middleware::ReverseProxyPath) on your application (if
 installed, otherwise log an error).  This is useful if your application is not running on the
 'root' (or /) of your host server.  **NOTE** if you use this feature you should add the required
-middleware to your project dependency list since it's not automatically a dependency of [Catalyst](https://metacpan.org/pod/Catalyst).
+middleware to your project dependency list since its not automatically a dependency of [Catalyst](https://metacpan.org/pod/Catalyst).
 This has been done since not all people need this feature and we wish to restrict the growth of
 [Catalyst](https://metacpan.org/pod/Catalyst) dependencies.
 - `encoding` - See ["ENCODING"](#encoding)
@@ -1469,18 +1563,19 @@ This has been done since not all people need this feature and we wish to restric
 
 - `abort_chain_on_error_fix`
 
-    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
-    can lead to running actions when the application is in an unexpected state.  If
-    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.)
+    Defaults to true.
 
-    use like:
+    When there is an error in an action chain, the default behavior is to
+    abort the processing of the remaining actions to avoid running them
+    when the application is in an unexpected state.
 
-        __PACKAGE__->config(abort_chain_on_error_fix => 1);
+    Before version 5.90070, the default used to be false. To keep the old
+    behaviour, you can explicitly set the value to false. E.g.
 
-    In the future this might become the default behavior.
+        __PACKAGE__->config(abort_chain_on_error_fix => 0);
+
+    If this setting is set to false, then the remaining actions are
+    performed and the error is caught at the end of the chain.
 
 - `use_hash_multivalue_in_request`
 
@@ -1527,8 +1622,14 @@ This has been done since not all people need this feature and we wish to restric
     in Catalyst version 5.90080+ break some of your query code, you may disable 
     the UTF-8 decoding globally using this configuration.
 
-    This setting takes precedence over `default_query_encoding` and
-    `decode_query_using_global_encoding`
+    This setting takes precedence over `default_query_encoding`
+
+- `do_not_check_query_encoding`
+
+    Catalyst versions 5.90080 - 5.90106 would decode query parts of an incoming
+    request but would not raise an exception when the decoding failed due to
+    incorrect unicode.  It now does, but if this change is giving you trouble
+    you may disable it by setting this configuration to true.
 
 - `default_query_encoding`
 
@@ -1537,13 +1638,6 @@ This has been done since not all people need this feature and we wish to restric
     specify a fixed value for how to decode your query.  You might need this if
     you are doing a lot of custom encoding of your URLs and not using UTF-8.
 
-    This setting take precedence over `decode_query_using_global_encoding`.
-
-- `decode_query_using_global_encoding`
-
-    Setting this to true will default your query decoding to whatever your
-    general global encoding is (the default is UTF-8).
-
 - `use_chained_args_0_special_case`
 
     In older versions of Catalyst, when more than one action matched the same path
@@ -1904,6 +1998,11 @@ the encoding configuration to undef.
 
 This is recommended for temporary backwards compatibility only.
 
+To turn it off for a single request use the [clear\_encoding](https://metacpan.org/pod/clear_encoding)
+method to turn off encoding for this request.  This can be useful
+when you are setting the body to be an arbitrary block of bytes,
+especially if that block happens to be a block of UTF8 text.
+
 Encoding is automatically applied when the content-type is set to
 a type that can be encoded.  Currently we encode when the content type
 matches the following regular expression:
@@ -2032,7 +2131,7 @@ Caelum: Rafael Kitover <rkitover@io.com>
 
 chansen: Christian Hansen
 
-Chase Venters <chase.venters@gmail.com>
+Chase Venters `chase.venters@gmail.com`
 
 chicks: Christopher Hicks
 
@@ -2096,8 +2195,6 @@ konobi: Scott McWhirter <konobi@cpan.org>
 
 marcus: Marcus Ramberg <mramberg@cpan.org>
 
-Mischa Spiegelmock <revmischa@cpan.org>
-
 miyagawa: Tatsuhiko Miyagawa <miyagawa@bulknews.net>
 
 mgrimes: Mark Grimes <mgrimes@cpan.org>
index e68e18c..8c598ba 100644 (file)
@@ -27,7 +27,6 @@ use HTML::Entities;
 use Tree::Simple qw/use_weak_refs/;
 use Tree::Simple::Visitor::FindByUID;
 use Class::C3::Adopt::NEXT;
-use List::MoreUtils qw/uniq/;
 use attributes;
 use String::RewritePrefix;
 use Catalyst::EngineLoader;
@@ -51,6 +50,7 @@ use Catalyst::Middleware::Stash;
 use Plack::Util;
 use Class::Load 'load_class';
 use Encode 2.21 'decode_utf8', 'encode_utf8';
+use Scalar::Util;
 
 BEGIN { require 5.008003; }
 
@@ -204,7 +204,7 @@ sub composed_stats_class {
 __PACKAGE__->_encode_check(Encode::FB_CROAK | Encode::LEAVE_SRC);
 
 # Remember to update this in Catalyst::Runtime as well!
-our $VERSION = '5.90112';
+our $VERSION = '5.90115';
 $VERSION = eval $VERSION if $VERSION =~ /_/; # numify for warning-free dev releases
 
 sub import {
@@ -246,11 +246,6 @@ sub _application { $_[0] }
 
 Catalyst - The Elegant MVC Web Application Framework
 
-=for html
-<a href="https://badge.fury.io/pl/Catalyst-Runtime"><img src="https://badge.fury.io/pl/Catalyst-Runtime.svg" alt="CPAN version" height="18"></a>
-<a href="https://travis-ci.org/perl-catalyst/catalyst-runtime/"><img src="https://api.travis-ci.org/perl-catalyst/catalyst-runtime.png" alt="Catalyst></a>
-<a href="http://cpants.cpanauthors.org/dist/Catalyst-Runtime"><img src="http://cpants.cpanauthors.org/dist/Catalyst-Runtime.png" alt='Kwalitee Score' /></a>
-
 =head1 SYNOPSIS
 
 See the L<Catalyst::Manual> distribution for comprehensive
@@ -474,7 +469,7 @@ or stash it like so:
 
 and access it from the stash.
 
-Keep in mind that the C<end> method used is that of the caller action. So a C<$c-E<gt>detach> inside a forwarded action would run the C<end> method from the original action requested.
+Keep in mind that the C<end> method used is that of the caller action. So a C<< $c->detach >> inside a forwarded action would run the C<end> method from the original action requested.
 
 =cut
 
@@ -1705,23 +1700,20 @@ sub uri_for {
       # somewhat lifted from URI::_query's query_form
       $query = '?'.join('&', map {
           my $val = $params->{$_};
-          #s/([;\/?:@&=+,\$\[\]%])/$URI::Escape::escapes{$1}/go; ## Commented out because seems to lead to double encoding - JNAP
-          s/ /+/g;
-          my $key = $_;
+          my $key = encode_utf8($_);
+          # using the URI::Escape pattern here so utf8 chars survive
+          $key =~ s/([^A-Za-z0-9\-_.!~*'() ])/$URI::Escape::escapes{$1}/go;
+          $key =~ s/ /+/g;
+
           $val = '' unless defined $val;
           (map {
-              my $param = "$_";
-              $param = encode_utf8($param);
+              my $param = encode_utf8($_);
               # using the URI::Escape pattern here so utf8 chars survive
               $param =~ s/([^A-Za-z0-9\-_.!~*'() ])/$URI::Escape::escapes{$1}/go;
               $param =~ s/ /+/g;
 
-              $key = encode_utf8($key);
-              # using the URI::Escape pattern here so utf8 chars survive
-              $key =~ s/([^A-Za-z0-9\-_.!~*'() ])/$URI::Escape::escapes{$1}/go;
-              $key =~ s/ /+/g;
-
-              "${key}=$param"; } ( ref $val eq 'ARRAY' ? @$val : $val ));
+              "${key}=$param";
+          } ( ref $val eq 'ARRAY' ? @$val : $val ));
       } @keys);
     }
 
@@ -2321,6 +2313,10 @@ sub finalize_encoding {
       (defined($res->body)) and
       (ref(\$res->body) eq 'SCALAR')
     ) {
+        # if you are finding yourself here and your body is already encoded correctly
+        # and you want to turn this off, use $c->clear_encoding to prevent encoding
+        # at this step, or set encoding to undef in the config to do so for the whole
+        # application.  See the ENCODING documentaiton for better notes.
         $c->res->body( $c->encoding->encode( $c->res->body, $c->_encode_check ) );
 
         # Set the charset if necessary.  This might be a bit bonkers since encodable response
@@ -2490,6 +2486,8 @@ sub prepare {
     };
 
     $c->log_request;
+    $c->{stash} = $c->stash;
+    Scalar::Util::weaken($c->{stash});
 
     return $c;
 }
@@ -2738,9 +2736,16 @@ sub log_request_parameters {
         next if ! keys %$params;
         my $t = Text::SimpleTable->new( [ 35, 'Parameter' ], [ $column_width, 'Value' ] );
         for my $key ( sort keys %$params ) {
-            my $param = $params->{$key};
-            my $value = defined($param) ? $param : '';
-            $t->row( $key, ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
+            my @values = ();
+            if(ref $params eq 'Hash::MultiValue') {
+                @values = $params->get_all($key);
+            } else {
+                my $param = $params->{$key};
+                if( defined($param) ) {
+                    @values = ref $param eq 'ARRAY' ? @$param : $param;
+                }
+            }
+            $t->row( $key.( scalar @values > 1 ? ' [multiple]' : ''), join(', ', @values) );
         }
         $c->log->debug( ucfirst($type) . " Parameters are:\n" . $t->draw );
     }
@@ -4297,18 +4302,20 @@ value to undef.
 
 C<abort_chain_on_error_fix>
 
-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
-can lead to running actions when the application is in an unexpected state.  If
-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.)
+Defaults to true.
+
+When there is an error in an action chain, the default behavior is to
+abort the processing of the remaining actions to avoid running them
+when the application is in an unexpected state.
+
+Before version 5.90070, the default used to be false. To keep the old
+behaviour, you can explicitly set the value to false. E.g.
 
-use like:
+    __PACKAGE__->config(abort_chain_on_error_fix => 0);
 
-    __PACKAGE__->config(abort_chain_on_error_fix => 1);
+If this setting is set to false, then the remaining actions are
+performed and the error is caught at the end of the chain.
 
-In the future this might become the default behavior.
 
 =item *
 
@@ -4762,6 +4769,11 @@ the encoding configuration to undef.
 
 This is recommended for temporary backwards compatibility only.
 
+To turn it off for a single request use the L<clear_encoding>
+method to turn off encoding for this request.  This can be useful
+when you are setting the body to be an arbitrary block of bytes,
+especially if that block happens to be a block of UTF8 text.
+
 Encoding is automatically applied when the content-type is set to
 a type that can be encoded.  Currently we encode when the content type
 matches the following regular expression:
@@ -4884,7 +4896,7 @@ andrewalker: André Walker <andre@cpan.org>
 
 Andrew Bramble
 
-Andrew Ford E<lt>A.Ford@ford-mason.co.ukE<gt>
+Andrew Ford <A.Ford@ford-mason.co.uk>
 
 Andrew Ruthven
 
@@ -4898,19 +4910,19 @@ Caelum: Rafael Kitover <rkitover@io.com>
 
 chansen: Christian Hansen
 
-Chase Venters C<chase.venters@gmail.com>
+Chase Venters <chase.venters@gmail.com>
 
 chicks: Christopher Hicks
 
-Chisel Wright C<pause@herlpacker.co.uk>
+Chisel Wright <pause@herlpacker.co.uk>
 
-Danijel Milicevic C<me@danijel.de>
+Danijel Milicevic <me@danijel.de>
 
 davewood: David Schmidt <davewood@cpan.org>
 
-David Kamholz E<lt>dkamholz@cpan.orgE<gt>
+David Kamholz <dkamholz@cpan.org>
 
-David Naughton, C<naughton@umn.edu>
+David Naughton <naughton@umn.edu>
 
 David E. Wheeler
 
@@ -4932,7 +4944,7 @@ gabb: Danijel Milicevic
 
 Gary Ashton Jones
 
-Gavin Henry C<ghenry@perl.me.uk>
+Gavin Henry <ghenry@perl.me.uk>
 
 Geoff Richards
 
@@ -4944,7 +4956,7 @@ ilmari: Dagfinn Ilmari MannsÃ¥ker <ilmari@ilmari.org>
 
 jcamacho: Juan Camacho
 
-jester: Jesse Sheidlower C<jester@panix.com>
+jester: Jesse Sheidlower <jester@panix.com>
 
 jhannah: Jay Hannah <jay@jays.net>
 
@@ -4954,9 +4966,9 @@ Johan Lindstrom
 
 jon: Jon Schutz <jjschutz@cpan.org>
 
-Jonathan Rockway C<< <jrockway@cpan.org> >>
+Jonathan Rockway <jrockway@cpan.org>
 
-Kieren Diment C<kd@totaldatasolution.com>
+Kieren Diment <kd@totaldatasolution.com>
 
 konobi: Scott McWhirter <konobi@cpan.org>
 
@@ -4992,7 +5004,9 @@ rafl: Florian Ragwitz <rafl@debian.org>
 
 random: Roland Lammel <lammel@cpan.org>
 
-Robert Sedlacek C<< <rs@474.at> >>
+revmischa: Mischa Spiegelmock <revmischa@cpan.org>
+
+Robert Sedlacek <rs@474.at>
 
 SpiceMan: Marcel Montes
 
@@ -5006,17 +5020,17 @@ Ulf Edvinsson
 
 vanstyn: Henry Van Styn <vanstyn@cpan.org>
 
-Viljo Marrandi C<vilts@yahoo.com>
+Viljo Marrandi <vilts@yahoo.com>
 
-Will Hawes C<info@whawes.co.uk>
+Will Hawes <info@whawes.co.uk>
 
 willert: Sebastian Willert <willert@cpan.org>
 
 wreis: Wallace Reis <wreis@cpan.org>
 
-Yuval Kogman, C<nothingmuch@woobling.org>
+Yuval Kogman <nothingmuch@woobling.org>
 
-rainboxx: Matthias Dietrich, C<perl@rainboxx.de>
+rainboxx: Matthias Dietrich <perl@rainboxx.de>
 
 dd070: Dhaval Dhanani <dhaval070@gmail.com>
 
index 9c3139a..b25cc46 100644 (file)
@@ -67,7 +67,7 @@ L<CatalystX::LeakChecker>
 
 =head1 AUTHOR
 
-Florian Ragwitz E<lt>rafl@debian.orgE<gt>
+Florian Ragwitz <rafl@debian.org>
 
 =end stopwords
 
index ad88a51..f65ee10 100644 (file)
@@ -5,8 +5,7 @@ use Class::MOP;
 use Class::Load ':all';
 use String::RewritePrefix;
 use Moose::Util qw/find_meta/;
-use List::Util qw/first/;
-use List::MoreUtils qw/uniq/;
+use List::Util qw/first uniq/;
 use namespace::clean -except => 'meta';
 
 BEGIN {
@@ -662,10 +661,25 @@ arguments, when it is instantiated:
 From L<Catalyst::Component::ApplicationAttribute>, stashes the application
 instance as $self->_application.
 
-=head2 $self->action_for('name')
+=head2 $self->action_for($action_name)
 
-Returns the Catalyst::Action object (if any) for a given method name
-in this component.
+Returns the Catalyst::Action object (if any) for a given action in this
+controller or relative to it.  You may refer to actions in controllers
+nested under the current controllers namespace, or in controllers 'up'
+from the current controller namespace.  For example:
+
+    package MyApp::Controller::One::Two;
+    use base 'Catalyst::Controller';
+
+    sub foo :Local {
+      my ($self, $c) = @_;
+      $self->action_for('foo'); # action 'foo' in Controller 'One::Two'
+      $self->action_for('three/bar'); # action 'bar' in Controller 'One::Two::Three'
+      $self->action_for('../boo'); # action 'boo' in Controller 'One'
+    }
+
+This returns 'undef' if there is no action matching the requested action
+name (after any path normalization) so you should check for this as needed.
 
 =head2 $self->action_namespace($c)
 
@@ -847,9 +861,9 @@ The following is exactly the same:
 
     package MyApp::Controller::Zoo;
 
-    sub foo  : Local Does('Moo')  { ... } # Catalyst::ActionRole::
-    sub bar  : Local Does('~Moo') { ... } # MyApp::ActionRole::Moo
-    sub baz  : Local Does('+MyApp::ActionRole::Moo') { ... }
+    sub foo  : Local Does('Buzz')  { ... } # Catalyst::ActionRole::
+    sub bar  : Local Does('~Buzz') { ... } # MyApp::ActionRole::Buzz
+    sub baz  : Local Does('+MyApp::ActionRole::Buzz') { ... }
 
 =head2 GET
 
index cb1dfed..138727c 100644 (file)
@@ -702,7 +702,7 @@ controller. For Example:
   # in MyApp::Controller::Foo
   sub bar : Chained CaptureArgs(1) { ... }
 
-  # in MyApp::Controller::Foo::Moo
+  # in MyApp::Controller::Foo::Bar
   sub bar : ChainedParent Args(1) { ... }
 
 This builds a chain like C</bar/*/bar/*>.
@@ -715,7 +715,7 @@ parts of the path (separated by C</>) this action wants to capture as
 its arguments. If it doesn't expect any, just specify
 C<:CaptureArgs(0)>.  The captures get passed to the action's C<@_> right
 after the context, but you can also find them as array references in
-C<$c-E<gt>request-E<gt>captures-E<gt>[$level]>. The C<$level> is the
+C<< $c->request->captures->[$level] >>. The C<$level> is the
 level of the action in the chain that captured the parts of the path.
 
 An action that is part of a chain (that is, one that has a C<:Chained>
@@ -764,7 +764,7 @@ of path parts after the endpoint.
 
 Just as with C<:CaptureArgs>, the arguments get passed to the action in
 C<@_> after the context object. They can also be reached through
-C<$c-E<gt>request-E<gt>arguments>.
+C<< $c->request->arguments >>.
 
 You should see 'Args' in L<Catalyst::Controller> for more details on using
 type constraints in your Args declarations.
index c51510e..cf98256 100644 (file)
@@ -407,9 +407,14 @@ sub prepare_action {
       if ( $c->debug && @args );
 }
 
-=head2 $self->get_action( $action, $namespace )
+=head2 $self->get_action( $action_name, $namespace )
 
-returns a named action from a given namespace.
+returns a named action from a given namespace.  C<$action_name>
+may be a relative path on that C<$namespace> such as
+
+    $self->get_action('../bar', 'foo/baz');
+
+In which case we look for the action at 'foo/bar'.
 
 =cut
 
@@ -419,17 +424,22 @@ sub get_action {
 
     $namespace = join( "/", grep { length } split '/', ( defined $namespace ? $namespace : "" ) );
 
-    return $self->_action_hash->{"${namespace}/${name}"};
+    return $self->get_action_by_path("${namespace}/${name}");
 }
 
 =head2 $self->get_action_by_path( $path );
 
 Returns the named action by its full private path.
 
+This method performs some normalization on C<$path> so that if
+it includes '..' it will do the right thing (for example if
+C<$path> is '/foo/../bar' that is normalized to '/bar'.
+
 =cut
 
 sub get_action_by_path {
     my ( $self, $path ) = @_;
+    $path =~s/[^\/]+\/\.\.\/// while $path=~m/[^\/]+\/\.\.\//;
     $path =~ s/^\///;
     $path = "/$path" unless $path =~ /\//;
     $self->_action_hash->{$path};
index 713bb5f..253b6a8 100644 (file)
@@ -1,6 +1,8 @@
 package Catalyst::Exception::Basic;
 
-use MooseX::Role::WithOverloading;
+use Moose::Role;
+use if !eval { require Moose; Moose->VERSION('2.1300') },
+    'MooseX::Role::WithOverloading';
 use Carp;
 use namespace::clean -except => 'meta';
 
index aae67f2..73e4cc0 100644 (file)
@@ -1,6 +1,8 @@
 package Catalyst::Exception::Interface;
 
-use MooseX::Role::WithOverloading;
+use Moose::Role;
+use if !eval { require Moose; Moose->VERSION('2.1300') },
+    'MooseX::Role::WithOverloading';
 use namespace::clean -except => 'meta';
 
 use overload
index 75796db..24ddcc7 100644 (file)
@@ -7,7 +7,7 @@ BEGIN { require 5.008003; }
 
 # Remember to update this in Catalyst as well!
 
-our $VERSION = '5.90112';
+our $VERSION = '5.90115';
 $VERSION = eval $VERSION if $VERSION =~ /_/; # numify for warning-free dev releases
 
 =head1 NAME
index f8a12da..845e891 100644 (file)
@@ -4,15 +4,14 @@ use Pod::Usage;
 use MooseX::Getopt;
 use Catalyst::EngineLoader;
 use Moose::Util::TypeConstraints;
-use Catalyst::Utils qw/ ensure_class_loaded /;
-use Class::Load 'load_class';
+use Catalyst::Utils;
 use namespace::autoclean;
 
 subtype 'Catalyst::ScriptRole::LoadableClass',
   as 'ClassName';
 coerce 'Catalyst::ScriptRole::LoadableClass',
   from 'Str',
-  via { ensure_class_loaded($_); 1 };
+  via { Catalyst::Utils::ensure_class_loaded($_); $_ };
 
 with 'MooseX::Getopt' => {
     -version => 0.48,
@@ -88,7 +87,7 @@ sub _plack_engine_name {}
 sub _run_application {
     my $self = shift;
     my $app = $self->application_name;
-    load_class($app);
+    Catalyst::Utils::ensure_class_loaded($app);
     my $server;
     if (my $e = $self->_plack_engine_name ) {
         $server = $self->load_engine($e, $self->_plack_loader_args);
index 9fb1e92..fc877d4 100644 (file)
@@ -413,11 +413,14 @@ sub term_width {
           } else {
             warn "There was an error trying to detect your terminal size: $@\n";
           }
+    };
+
+    unless ($width) {
         warn 'Trouble trying to detect your terminal size, looking at $ENV{COLUMNS}'."\n";
         $width = $ENV{COLUMNS}
             if exists($ENV{COLUMNS})
             && $ENV{COLUMNS} =~ m/^\d+$/;
-    };
+    }
 
     do {
       warn "Cannot determine desired terminal width, using default of 80 columns\n";
diff --git a/t/abort-chain-1.t b/t/abort-chain-1.t
new file mode 100644 (file)
index 0000000..dba8593
--- /dev/null
@@ -0,0 +1,50 @@
+#!perl
+
+use strict;
+use warnings;
+use Test::More tests => 1;
+use HTTP::Request::Common;
+
+BEGIN {
+    package TestApp::Controller::Root;
+    $INC{'TestApp/Controller/Root.pm'} = __FILE__;
+    use Moose;
+    use MooseX::MethodAttributes;
+    extends 'Catalyst::Controller';
+
+    has counter => (is => 'rw', isa => 'Int', default => sub { 0 });
+    sub increment {
+        my $self = shift;
+        $self->counter($self->counter + 1);
+    }
+    sub root :Chained('/') :PathPart('') :CaptureArgs(0) {
+        my ($self, $c, $arg) = @_;
+        die "Died in root";
+    }
+    sub main :Chained('root') :PathPart('') :Args(0) {
+        my ($self, $c, $arg) = @_;
+        $self->increment;
+        die "Died in main";
+    }
+    sub hits :Path('hits') :Args(0) {
+        my ($self, $c, $arg) = @_;
+        $c->response->body($self->counter);
+    }
+    __PACKAGE__->config(namespace => '');
+}
+{
+    package TestApp;
+    $INC{'TestApp.pm'} = __FILE__;
+    use Catalyst;
+    __PACKAGE__->setup;
+}
+
+use Catalyst::Test 'TestApp';
+
+{
+    my $res = request('/');
+}
+{
+    my $res = request('/hits');
+    is $res->content, 0, "main action not touched on crash with no explicit setting";
+}
diff --git a/t/abort-chain-2.t b/t/abort-chain-2.t
new file mode 100644 (file)
index 0000000..370868c
--- /dev/null
@@ -0,0 +1,51 @@
+#!perl
+
+use strict;
+use warnings;
+use Test::More tests => 1;
+use HTTP::Request::Common;
+
+BEGIN {
+    package TestApp::Controller::Root;
+    $INC{'TestApp/Controller/Root.pm'} = __FILE__;
+    use Moose;
+    use MooseX::MethodAttributes;
+    extends 'Catalyst::Controller';
+
+    has counter => (is => 'rw', isa => 'Int', default => sub { 0 });
+    sub increment {
+        my $self = shift;
+        $self->counter($self->counter + 1);
+    }
+    sub root :Chained('/') :PathPart('') :CaptureArgs(0) {
+        my ($self, $c, $arg) = @_;
+        die "Died in root";
+    }
+    sub main :Chained('root') :PathPart('') :Args(0) {
+        my ($self, $c, $arg) = @_;
+        $self->increment;
+        die "Died in main";
+    }
+    sub hits :Path('hits') :Args(0) {
+        my ($self, $c, $arg) = @_;
+        $c->response->body($self->counter);
+    }
+    __PACKAGE__->config(namespace => '');
+}
+{
+    package TestApp;
+    $INC{'TestApp.pm'} = __FILE__;
+    use Catalyst;
+    __PACKAGE__->config(abort_chain_on_error_fix => 1);
+    __PACKAGE__->setup;
+}
+
+use Catalyst::Test 'TestApp';
+
+{
+    my $res = request('/');
+}
+{
+    my $res = request('/hits');
+    is $res->content, 0, "main action not touched on crash with explicit setting to true";
+}
diff --git a/t/abort-chain-3.t b/t/abort-chain-3.t
new file mode 100644 (file)
index 0000000..1b0f928
--- /dev/null
@@ -0,0 +1,51 @@
+#!perl
+
+use strict;
+use warnings;
+use Test::More tests => 1;
+use HTTP::Request::Common;
+
+BEGIN {
+    package TestApp::Controller::Root;
+    $INC{'TestApp/Controller/Root.pm'} = __FILE__;
+    use Moose;
+    use MooseX::MethodAttributes;
+    extends 'Catalyst::Controller';
+
+    has counter => (is => 'rw', isa => 'Int', default => sub { 0 });
+    sub increment {
+        my $self = shift;
+        $self->counter($self->counter + 1);
+    }
+    sub root :Chained('/') :PathPart('') :CaptureArgs(0) {
+        my ($self, $c, $arg) = @_;
+        die "Died in root";
+    }
+    sub main :Chained('root') :PathPart('') :Args(0) {
+        my ($self, $c, $arg) = @_;
+        $self->increment;
+        die "Died in main";
+    }
+    sub hits :Path('hits') :Args(0) {
+        my ($self, $c, $arg) = @_;
+        $c->response->body($self->counter);
+    }
+    __PACKAGE__->config(namespace => '');
+}
+{
+    package TestApp;
+    $INC{'TestApp.pm'} = __FILE__;
+    use Catalyst;
+    __PACKAGE__->config(abort_chain_on_error_fix => 0);
+    __PACKAGE__->setup;
+}
+
+use Catalyst::Test 'TestApp';
+
+{
+    my $res = request('/');
+}
+{
+    my $res = request('/hits');
+    is $res->content, 1, "main action performed on crash with explicit setting to false";
+}
index 0bf1b0c..a78fbec 100644 (file)
@@ -8,9 +8,9 @@ use lib "$FindBin::Bin/../lib";
 use Catalyst::Test 'TestApp';
 
 my %roles = (
-    foo  => 'TestApp::ActionRole::Moo',
-    bar  => 'TestApp::ActionRole::Moo',
-    baz  => 'Moo',
+    foo  => 'TestApp::ActionRole::Guff',
+    bar  => 'TestApp::ActionRole::Guff',
+    baz  => 'Guff',
     quux => 'Catalyst::ActionRole::Zoo',
 );
 
@@ -24,9 +24,9 @@ while (my ($path, $role) = each %roles) {
 {
     my $resp = request("/actionroles/corge");
     ok($resp->is_success);
-    is($resp->content, 'TestApp::ActionRole::Moo');
+    is($resp->content, 'TestApp::ActionRole::Guff');
     is($resp->header('X-Affe'), 'Tiger');
-   is($resp->header('X-Action-After'), 'moo');
+    is($resp->header('X-Action-After'), 'moo');
 }
 {
     my $resp = request("/actionroles/frew");
index 530d475..bb2aae5 100755 (executable)
@@ -35,7 +35,7 @@ like $MESSAGES[0], qr/^\[info\] hello there!$/,
 
 {
 
-    package Catalyst::Log::Subclass;
+    package Catalyst::Log::SubclassAutoflush;
     use base qw/Catalyst::Log/;
 
     sub _send_to_log {
@@ -47,9 +47,9 @@ like $MESSAGES[0], qr/^\[info\] hello there!$/,
 
 @MESSAGES = (); # clear the message log
 
-my $SUBCLASS = 'Catalyst::Log::Subclass';
+my $SUBCLASS = 'Catalyst::Log::SubclassAutoflush';
 can_ok $SUBCLASS, 'new';
-ok $log = Catalyst::Log::Subclass->new,
+ok $log = $SUBCLASS->new,
     '... and the log subclass constructor should return a new object';
 isa_ok $log, $SUBCLASS, '... and the object it returns';
 isa_ok $log, $LOG,      '... and it also';
index 0fdaa87..d6bbcaf 100644 (file)
@@ -10,26 +10,19 @@ BEGIN { $ENV{PACKAGE_STASH_IMPLEMENTATION} = 'PP' if $] < '5.008007' }
 use Test::More;
 use Try::Tiny;
 
-plan skip_all => "Need Test::Without::Module for this test"
-    unless try { require Test::Without::Module; 1 };
-
-Test::Without::Module->import(qw(
+my %hidden = map { (my $m = "$_.pm") =~ s{::}{/}g; $m => 1 } qw(
     Starman::Server
     Plack::Handler::Starman
     MooseX::Daemonize
     MooseX::Daemonize::Pid::File
     MooseX::Daemonize::Core
-));
-
-require "$Bin/../aggregate/unit_core_script_server.t";
+);
+local @INC = (sub {
+  return unless exists $hidden{$_[1]};
+  die "Can't locate $_[1] in \@INC (hidden)\n";
+}, @INC);
 
-Test::Without::Module->unimport(qw(
-    Starman::Server
-    Plack::Handler::Starman
-    MooseX::Daemonize
-    MooseX::Daemonize::Pid::File
-    MooseX::Daemonize::Core
-));
+do "$Bin/../aggregate/unit_core_script_server.t"
+  or die $@ || 'test returned false';
 
 1;
-
index ab256f0..887aa4a 100644 (file)
@@ -60,6 +60,12 @@ is(
 );
 
 is(
+    Catalyst::uri_for( $context, '/bar', 'with space', { 'also with' => 'space here' })->as_string,
+    'http://127.0.0.1/foo/bar/with%20space?also+with=space+here',
+    'Spaces encoded correctly'
+);
+
+is(
     Catalyst::uri_for( $context, '/bar#fragment', { param1 => 'value1' } )->as_string,
     'http://127.0.0.1/foo/bar?param1=value1#fragment',
     'URI for path with fragment and query params 1'
@@ -127,6 +133,12 @@ is(
     'Plus is not encoded, called with only class name'
 );
 
+is(
+    Catalyst::uri_for( 'TestApp', '/bar', 'with space', { 'also with' => 'space here' })->as_string,
+    '/bar/with%20space?also+with=space+here',
+    'Spaces encoded correctly, called with only class name'
+);
+
 TODO: {
     local $TODO = 'broken by 5.7008';
     is(
index c90f51a..e5b01bc 100644 (file)
@@ -60,8 +60,10 @@ add_stopwords(qw(
     Marrandi
     McWhirter
     Milicevic
+    Mischa
     Miyagawa
     Montes
+    Napiorkowski
     Naughton
     Oleg
     Ragwitz
@@ -79,11 +81,13 @@ add_stopwords(qw(
     Sedlacek
     Sheidlower
     SpiceMan
+    Spiegelmock
     Styn
     Szilakszi
     Tatsuhiko
     Ulf
     Upasana
+    Venters
     Vilain
     Viljo
     Wardley
@@ -121,7 +125,6 @@ add_stopwords(qw(
     miyagawa
     mst
     multipart
-    Napiorkowski
     naughton
     ningu
     nothingmuch
@@ -130,6 +133,7 @@ add_stopwords(qw(
     phaylon
     rafl
     rainboxx
+    revmischa
     sri
     szbalint
     uploadtmp
index edccea2..d6a0c50 100644 (file)
@@ -4,6 +4,17 @@ use Test::More;
 use Class::MOP;
 
 BEGIN {
+  my %hidden = map { (my $m = "$_.pm") =~ s{::}{/}g; $m => 1 } qw(
+    Foo
+    Bar
+  );
+  unshift @INC, sub {
+    return unless exists $hidden{$_[1]};
+    die "Can't locate $_[1] in \@INC (hidden)\n";
+  };
+}
+
+BEGIN {
   package TestRole;
   $INC{'TestRole'} = __FILE__;
   use Moose::Role;
@@ -30,7 +41,7 @@ BEGIN {
 
   sub c { 'c' }
 
-  package TestApp::TraitFor::Response::Bar; 
+  package TestApp::TraitFor::Response::Bar;
   $INC{'TestApp/TraitFor/Response/Bar.pm'} = __FILE__;
 
   use Moose::Role;
index d160e73..cc57bc1 100644 (file)
@@ -11,6 +11,17 @@ BEGIN {
 }
 
 BEGIN {
+  my %hidden = map { (my $m = "$_.pm") =~ s{::}{/}g; $m => 1 } qw(
+    Foo
+    Bar
+  );
+  unshift @INC, sub {
+    return unless exists $hidden{$_[1]};
+    die "Can't locate $_[1] in \@INC (hidden)\n";
+  };
+}
+
+BEGIN {
   package TestRole;
   $INC{'TestRole'} = __FILE__;
   use Moose::Role;
diff --git a/t/evil_stash.t b/t/evil_stash.t
new file mode 100644 (file)
index 0000000..e97a131
--- /dev/null
@@ -0,0 +1,37 @@
+use warnings;
+use strict;
+use Test::More;
+
+{
+  package MyApp::Controller::Root;
+  $INC{'MyApp/Controller/Root.pm'} = __FILE__;
+
+  use base 'Catalyst::Controller';
+
+  sub root :Path('') Args(0) {
+    my ($self, $c) = @_;
+    $c->{stash}->{foo} = 'bar';
+    $c->stash(baz=>'boor');
+    $c->{stash}->{baz} = $c->stash->{baz} . 2;
+    
+    Test::More::is($c->stash->{foo}, 'bar');
+    Test::More::is($c->stash->{baz}, 'boor2');
+    Test::More::is($c->{stash}->{foo}, 'bar');
+    Test::More::is($c->{stash}->{baz}, 'boor2');
+
+    $c->res->body('return');
+  }
+
+  package MyApp;
+  use Catalyst;
+  MyApp->setup;
+}
+
+use HTTP::Request::Common;
+use Catalyst::Test 'MyApp';
+
+{
+   ok my $res = request POST 'root/';
+}
+
+done_testing();
similarity index 80%
rename from t/lib/Catalyst/ActionRole/Moo.pm
rename to t/lib/Catalyst/ActionRole/Guff.pm
index 3d4aa51..4f8e046 100644 (file)
@@ -1,4 +1,4 @@
-package Catalyst::ActionRole::Moo;
+package Catalyst::ActionRole::Guff;
 
 use Moose::Role;
 
similarity index 91%
rename from t/lib/Moo.pm
rename to t/lib/Guff.pm
index c28806a..16e558e 100644 (file)
@@ -1,4 +1,4 @@
-package Moo;
+package Guff;
 
 use Moose::Role;
 
similarity index 77%
rename from t/lib/TestApp/ActionRole/Moo.pm
rename to t/lib/TestApp/ActionRole/Guff.pm
index d0fd290..3e8fdd9 100644 (file)
@@ -1,4 +1,4 @@
-package TestApp::ActionRole::Moo;
+package TestApp::ActionRole::Guff;
 
 use Moose::Role;
 
index 37c24f9..69206bb 100644 (file)
@@ -11,12 +11,12 @@ __PACKAGE__->config(
     },
 );
 
-sub foo  : Local Does('Moo')  {}
-sub bar  : Local Does('~Moo') {}
-sub baz  : Local Does('+Moo') {}
+sub foo  : Local Does('Guff')  {}
+sub bar  : Local Does('~Guff') {}
+sub baz  : Local Does('+Guff') {}
 sub quux : Local Does('Zoo')  {}
 
-sub corge : Local Does('Moo') ActionClass('TestAfter') {
+sub corge : Local Does('Guff') ActionClass('TestAfter') {
     my ($self, $ctx) = @_;
     $ctx->stash(after_message => 'moo');
 }
diff --git a/t/relative_root_action_for_bug.t b/t/relative_root_action_for_bug.t
new file mode 100644 (file)
index 0000000..06cd0c2
--- /dev/null
@@ -0,0 +1,93 @@
+use warnings;
+use strict;
+use Test::More;
+
+{
+    package MyApp::Controller::Root;
+    $INC{'MyApp/Controller/Root.pm'} = __FILE__;
+
+    use Moose;
+    use MooseX::MethodAttributes;
+
+    extends 'Catalyst::Controller';
+
+    sub root :Chained(/) PathPart('') CaptureArgs(0) {
+      my ($self, $c) = @_;
+    }
+
+    sub top :Chained('root') Args(0) {
+      my ($self, $c) = @_;
+      Test::More::is $self->action_for('top'), 'top';
+      Test::More::is $self->action_for('story/story'), 'story/story';
+    }
+
+    sub default : Path {
+
+        my ($self, $c) = @_;
+        $c->response->body("Ok");
+    }
+
+    MyApp::Controller::Root->config(namespace=>'');
+
+    package MyApp::Controller::Story;
+    $INC{'MyApp/Controller/Story.pm'} = __FILE__;
+
+    use Moose;
+    use MooseX::MethodAttributes;
+
+    extends 'Catalyst::Controller';
+
+    sub root :Chained(/root) PathPart('') CaptureArgs(0) {
+      my ($self, $c) = @_;
+    }
+
+    sub story :Chained(root) Args(0) {
+      my ($self, $c) = @_;
+
+      Test::More::is $self->action_for('story'), 'story/story';
+      Test::More::is $self->action_for('author/author'), 'story/author/author';
+    }
+
+    __PACKAGE__->meta->make_immutable;
+
+    package MyApp::Controller::Story::Author;
+    $INC{'MyApp/Controller/Story/Author.pm'} = __FILE__;
+
+    use Moose;
+    use MooseX::MethodAttributes;
+
+    extends 'Catalyst::Controller';
+
+    sub root :Chained(/story/root) PathPart('') CaptureArgs(0) {
+      my ($self, $c) = @_;
+    }
+
+    sub author :Chained(root) Args(0) {
+      my ($self, $c, $id) = @_;
+      Test::More::is $self->action_for('author'), 'story/author/author';
+      Test::More::is $self->action_for('../story'), 'story/story';
+      Test::More::is $self->action_for('../../top'), 'top';
+    }
+
+    __PACKAGE__->meta->make_immutable;
+
+    package MyApp;
+    $INC{'MyApp.pm'} = __FILE__;
+
+    use Catalyst;
+
+    MyApp->setup;
+}
+
+use Catalyst::Test 'MyApp';
+
+ok request '/top';
+ok request '/story';
+ok request '/author';
+ok request '/double';
+ok request '/double/file.ext';
+ok request '/double/file..ext';
+
+
+done_testing(13);
+