Merge branch 'master' of https://github.com/simonamor/catalyst-runtime into simonamor...
John Napiorkowski [Mon, 1 May 2017 15:41:49 +0000 (10:41 -0500)]
Changes
Makefile.PL
lib/Catalyst.pm
lib/Catalyst/Component/ContextClosure.pm
lib/Catalyst/DispatchType/Chained.pm
lib/Catalyst/Exception/Basic.pm
lib/Catalyst/Exception/Interface.pm
lib/Catalyst/ScriptRole.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]

diff --git a/Changes b/Changes
index 8014ea8..94fed2d 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,16 @@
 # 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++)
+
+
 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
index 68dd556..5a47d3e 100644 (file)
@@ -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';
@@ -142,6 +142,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 178d502..03b842e 100644 (file)
@@ -475,7 +475,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
 
@@ -2322,6 +2322,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
@@ -2741,9 +2745,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 );
     }
@@ -4300,18 +4311,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.
 
-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 explicitely set the value to false. E.g.
+
+    __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.
 
-In the future this might become the default behavior.
 
 =item *
 
@@ -4765,6 +4778,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:
@@ -4887,7 +4905,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
 
@@ -4911,7 +4929,7 @@ Danijel Milicevic C<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>
 
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 cb1dfed..2eb0bc1 100644 (file)
@@ -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 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 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);
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";
+}