Merge master into gsoc_breadboard
Tomas Doran [Wed, 27 Jun 2012 20:56:45 +0000 (21:56 +0100)]
30 files changed:
Changes
Makefile.PL
lib/Catalyst.pm
lib/Catalyst/Controller.pm
lib/Catalyst/DispatchType/Chained.pm
lib/Catalyst/Engine.pm
lib/Catalyst/Request.pm
lib/Catalyst/Response.pm
lib/Catalyst/Runtime.pm
lib/Catalyst/Utils.pm
t/aggregate/live_component_controller_action_action.t
t/aggregate/live_component_controller_action_streaming.t
t/aggregate/live_component_controller_actionroles.t [new file with mode: 0644]
t/author/http-server.t
t/lib/Catalyst/Action/TestAfter.pm
t/lib/Catalyst/ActionRole/Moo.pm [new file with mode: 0644]
t/lib/Catalyst/ActionRole/Zoo.pm [new file with mode: 0644]
t/lib/Moo.pm [new file with mode: 0644]
t/lib/TestApp.pm
t/lib/TestApp/Action/TestActionArgsFromConstructor.pm [new file with mode: 0644]
t/lib/TestApp/ActionRole/Boo.pm [new file with mode: 0644]
t/lib/TestApp/ActionRole/Kooh.pm [new file with mode: 0644]
t/lib/TestApp/ActionRole/Moo.pm [new file with mode: 0644]
t/lib/TestApp/Controller/Action/Action.pm
t/lib/TestApp/Controller/ActionRoles.pm [new file with mode: 0644]
t/lib/TestApp/Controller/BodyParams.pm
t/lib/TestAppBadlyImmutable.pm
t/live_catalyst_test.t
t/plugin_new_method_backcompat.t
t/unit_core_script_test.t [moved from t/aggregate/unit_core_script_test.t with 98% similarity]

diff --git a/Changes b/Changes
index c13291d..68a4626 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,14 +1,65 @@
 # This file documents the revision history for Perl extension Catalyst.
 
+5.90014 - 2012-06-26 10:00:00
+
+  - Fix calling finalize_headers before writing body when using $c->write /
+    $c->res->write (fixes RT#76179).
+
+5.90013 - 2012-06-21 10:40:00
+
+  - Release previous TRIAL as stable.
+  - We failed to note in the previous changelog that the Makefile.PL has been
+    improved to make it easier for authors to bootstrap a developer install
+    of Catalyst.
+
+5.90013 - TRIAL 2012-06-07 20:21:00
+
+ New features:
+  - Merge Catalyst::Controller::ActionRole into Catalyst::Controller.
+
+ Bug fixes:
+  - Fix warnings in some matching cases for Action methods with
+    Args(), when using Catalyst::DispatchType::Chained
+
+  - Fix request body parameters to not be undef if no parameters
+    are supplied.
+
+  - Fix action_args config so that it can be specified in the
+    top level config.
+
+  - Fix t/author/http-server.t on Win32
+
+  - Fix use of Test::Aggregate to make tests faster.
+
+5.90012 - 2012-05-16 09:59:00
+
+ Distribution META.yml changes:
+  - author key is now correct, rather than what Module::Install
+    mis-parses from the documentation.
+  - x_authority key added.
+
  Bug fixes:
   - Fix request body parameters being multiply rebuilt. Fixes both
     RT#75607 and CatalystX::DebugFilter
 
+  - Make plugin de-duplication work as intended originally, as whilst
+    duplicate plugins are totally unwise, the C3 error given to the user
+    is less than helpful.
+
+  - Remove dependence on obscure behaviour in B::Hooks::EndOfScope
+    for backward compatibility. This fixes issues with behaviour changes
+    in bleadperl. RT#76437
+
+  - Work around Moose bug RT#75367 which breaks
+    Catalyst::Controller::DBIC::API.
+
  Documentation:
   - Fix documentation in Catalyst::Component to show attributes and
     calling readers, rather than accessing elements in the $self->{} hash
     directly.
   - Add note in Catalyst::Component to strongly disrecommend $self->config
+  - Fix vague 'checkout' wording in Catalyst::Utils. RT#77000
+  - Fix documentation for the 'secure' method in Catalyst:Request. RT#76710
 
 5.90011 - 2012-03-08 16:43:00
 
index 8ebe336..bcc2810 100644 (file)
@@ -4,20 +4,32 @@ use inc::Module::Install 0.91;
 # Ensure that these get used - yes, M::I loads them for us, but if you're
 # in author mode and don't have them installed, then the error is tres
 # cryptic.
-use Module::Install::AuthorRequires;
-use Module::Install::CheckConflicts;
-use Module::Install::AuthorTests;
+if ($Module::Install::AUTHOR) { # We could just use them, but telling
+    my @fail;                   # people the set of things they need nicer
+    foreach my $module (qw/
+        Module::Install::AuthorRequires
+        Module::Install::CheckConflicts
+        Module::Install::AuthorTests
+        Module::Install::Authority
+    /) {
+        push(@fail, $module)
+            unless eval qq{require $module; 1;};
+    }
+    die("Module::Install extensions failed, not installed? \n"
+        . join("\n", map { "  $_" } @fail) . "\n") if @fail;
+}
 
 perl_version '5.008003';
 
 name 'Catalyst-Runtime';
+author 'Sebastian Riedel <sri@cpan.org>';
+authority('MSTROUT');
 all_from 'lib/Catalyst/Runtime.pm';
 
 requires 'Bread::Board';
 requires 'List::MoreUtils';
 requires 'namespace::autoclean' => '0.09';
-requires 'namespace::clean' => '0.13';
-requires 'B::Hooks::EndOfScope' => '0.10';
+requires 'namespace::clean' => '0.23';
 requires 'MooseX::Emulate::Class::Accessor::Fast' => '0.00903';
 requires 'Class::Load' => '0.12';
 requires 'Class::MOP' => '0.95';
@@ -68,9 +80,10 @@ test_requires 'Data::Dump';
 test_requires 'HTTP::Request::Common';
 
 # aggregate tests if AGGREGATE_TESTS is set and a recent Test::Aggregate and a Test::Simple it works with is available
+my @author_requires;
 if ($ENV{AGGREGATE_TESTS} && can_use('Test::Simple', '0.88') && can_use('Test::Aggregate', '0.364')) {
-    author_requires('Test::Aggregate', '0.364');
-    author_requires('Test::Simple', '0.88');
+    push(@author_requires, 'Test::Aggregate', '0.364');
+    push(@author_requires, 'Test::Simple', '0.88');
     open my $fh, '>', '.aggregating';
 }
 else {
@@ -78,16 +91,18 @@ else {
     tests 't/*.t t/aggregate/*.t';
 }
 
-author_requires 'CatalystX::LeakChecker', '0.05';
-author_requires 'File::Copy::Recursive'; # For http server test
-author_requires 'Catalyst::Devel', '1.0'; # For http server test
-author_requires 'Catalyst::Engine::PSGI';
-author_requires 'Test::Without::Module';
-author_requires 'Starman';
-author_requires 'MooseX::Daemonize';
-
-author_tests 't/author';
-author_requires(map {; $_ => 0 } qw(
+push(@author_requires, 'CatalystX::LeakChecker', '0.05');
+push(@author_requires, 'Catalyst::Devel', '1.0'); # For http server test
+
+author_tests('t/author');
+author_requires(
+  @author_requires,
+  map {; $_ => 0 } qw(
+  File::Copy::Recursive
+  Catalyst::Engine::PSGI
+  Test::Without::Module
+  Starman
+  MooseX::Daemonize
   Test::NoTabs
   Test::Pod
   Test::Pod::Coverage
index fad4c90..da61272 100644 (file)
@@ -4,7 +4,7 @@ use Moose;
 use Moose::Meta::Class ();
 extends 'Catalyst::Component';
 use Moose::Util qw/find_meta/;
-use B::Hooks::EndOfScope ();
+use namespace::clean -except => 'meta';
 use Catalyst::Exception;
 use Catalyst::Exception::Detach;
 use Catalyst::Exception::Go;
@@ -99,7 +99,7 @@ __PACKAGE__->stats_class('Catalyst::Stats');
 
 # Remember to update this in Catalyst::Runtime as well!
 
-our $VERSION = '5.90011';
+our $VERSION = '5.90014';
 
 sub import {
     my ( $class, @arguments ) = @_;
@@ -1015,29 +1015,6 @@ EOF
         $class->log->info("$name powered by Catalyst $Catalyst::VERSION");
     }
 
-    # Make sure that the application class becomes immutable at this point,
-    B::Hooks::EndOfScope::on_scope_end {
-        return if $@;
-        my $meta = Class::MOP::get_metaclass_by_name($class);
-        if (
-            $meta->is_immutable
-            && ! { $meta->immutable_options }->{replace_constructor}
-            && (
-                   $class->isa('Class::Accessor::Fast')
-                || $class->isa('Class::Accessor')
-            )
-        ) {
-            warn "You made your application class ($class) immutable, "
-                . "but did not inline the\nconstructor. "
-                . "This will break catalyst, as your app \@ISA "
-                . "Class::Accessor(::Fast)?\nPlease pass "
-                . "(replace_constructor => 1)\nwhen making your class immutable.\n";
-        }
-        $meta->make_immutable(
-            replace_constructor => 1,
-        ) unless $meta->is_immutable;
-    };
-
     if ($class->config->{case_sensitive}) {
         $class->log->warn($class . "->config->{case_sensitive} is set.");
         $class->log->warn("This setting is deprecated and planned to be removed in Catalyst 5.81.");
@@ -1871,6 +1848,8 @@ sub prepare {
     my $uploadtmp = $class->config->{uploadtmp};
     my $c = $class->context_class->new({ $uploadtmp ? (_uploadtmp => $uploadtmp) : ()});
 
+    $c->response->_context($c);
+
     #surely this is not the most efficient way to do things...
     $c->stats($class->stats_class->new)->enable($c->use_stats);
     if ( $c->debug || $c->config->{enable_catalyst_header} ) {
@@ -2294,11 +2273,35 @@ Starts the engine.
 
 sub run {
   my $app = shift;
+  $app->_make_immutable_if_needed;
   $app->engine_loader->needs_psgi_engine_compat_hack ?
     $app->engine->run($app, @_) :
       $app->engine->run( $app, $app->_finalized_psgi_app, @_ );
 }
 
+sub _make_immutable_if_needed {
+    my $class = shift;
+    my $meta = Class::MOP::get_metaclass_by_name($class);
+    my $isa_ca = $class->isa('Class::Accessor::Fast') || $class->isa('Class::Accessor');
+    if (
+        $meta->is_immutable
+        && ! { $meta->immutable_options }->{replace_constructor}
+        && $isa_ca
+    ) {
+        warn("You made your application class ($class) immutable, "
+            . "but did not inline the\nconstructor. "
+            . "This will break catalyst, as your app \@ISA "
+            . "Class::Accessor(::Fast)?\nPlease pass "
+            . "(replace_constructor => 1)\nwhen making your class immutable.\n");
+    }
+    unless ($meta->is_immutable) {
+        # XXX - FIXME warning here as you should make your app immutable yourself.
+        $meta->make_immutable(
+            replace_constructor => 1,
+        );
+    }
+}
+
 =head2 $c->set_action( $action, $code, $namespace, $attrs )
 
 Sets an action in a given namespace.
@@ -2706,11 +2709,16 @@ the plugin name does not begin with C<Catalyst::Plugin::>.
         Class::MOP::load_class( $plugin );
         $class->log->warn( "$plugin inherits from 'Catalyst::Component' - this is deprecated and will not work in 5.81" )
             if $plugin->isa( 'Catalyst::Component' );
-        $proto->_plugins->{$plugin} = 1;
-        unless ($instant) {
+        my $plugin_meta = Moose::Meta::Class->create($plugin);
+        if (!$plugin_meta->has_method('new')
+            && ( $plugin->isa('Class::Accessor::Fast') || $plugin->isa('Class::Accessor') ) ) {
+            $plugin_meta->add_method('new', Moose::Object->meta->get_method('new'))
+        }
+        if (!$instant && !$proto->_plugins->{$plugin}) {
             my $meta = Class::MOP::get_metaclass_by_name($class);
             $meta->superclasses($plugin, $meta->superclasses);
         }
+        $proto->_plugins->{$plugin} = 1;
         return $class;
     }
 
index 5b94aaa..8cb6d6c 100644 (file)
@@ -1,7 +1,11 @@
 package Catalyst::Controller;
 
 use Moose;
+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 namespace::clean -except => 'meta';
 
@@ -13,28 +17,48 @@ use Catalyst::Utils;
 
 with 'Catalyst::Component::ApplicationAttribute';
 
-has path_prefix =>
-    (
-     is => 'rw',
-     isa => 'Str',
-     init_arg => 'path',
-     predicate => 'has_path_prefix',
-    );
+has path_prefix => (
+    is        => 'rw',
+    isa       => 'Str',
+    init_arg  => 'path',
+    predicate => 'has_path_prefix',
+);
 
-has action_namespace =>
-    (
-     is => 'rw',
-     isa => 'Str',
-     init_arg => 'namespace',
-     predicate => 'has_action_namespace',
-    );
+has action_namespace => (
+    is        => 'rw',
+    isa       => 'Str',
+    init_arg  => 'namespace',
+    predicate => 'has_action_namespace',
+);
 
-has actions =>
-    (
-     accessor => '_controller_actions',
-     isa => 'HashRef',
-     init_arg => undef,
-    );
+has actions => (
+    accessor => '_controller_actions',
+    isa      => 'HashRef',
+    init_arg => undef,
+);
+
+has _action_role_args => (
+    traits     => [qw(Array)],
+    isa        => 'ArrayRef[Str]',
+    init_arg   => 'action_roles',
+    default    => sub { [] },
+    handles    => {
+        _action_role_args => 'elements',
+    },
+);
+
+has _action_roles => (
+    traits     => [qw(Array)],
+    isa        => 'ArrayRef[RoleName]',
+    init_arg   => undef,
+    lazy       => 1,
+    builder    => '_build__action_roles',
+    handles    => {
+        _action_roles => 'elements',
+    },
+);
+
+has action_args => (is => 'ro');
 
 # ->config(actions => { '*' => ...
 has _all_actions_attributes => (
@@ -54,6 +78,14 @@ sub BUILD {
 
     # trigger lazy builder
     $self->_all_actions_attributes;
+    $self->_action_roles;
+}
+
+sub _build__action_roles {
+    my $self = shift;
+    my @roles = $self->_expand_role_shortname($self->_action_role_args);
+    load_class($_) for @roles;
+    return \@roles;
 }
 
 sub _build__all_actions_attributes {
@@ -86,10 +118,11 @@ for more info about how Catalyst dispatches to actions.
 
 #I think both of these could be attributes. doesn't really seem like they need
 #to ble class data. i think that attributes +default would work just fine
-__PACKAGE__->mk_classdata($_) for qw/_dispatch_steps _action_class/;
+__PACKAGE__->mk_classdata($_) for qw/_dispatch_steps _action_class _action_role_prefix/;
 
 __PACKAGE__->_dispatch_steps( [qw/_BEGIN _AUTO _ACTION/] );
 __PACKAGE__->_action_class('Catalyst::Action');
+__PACKAGE__->_action_role_prefix([ 'Catalyst::ActionRole::' ]);
 
 
 sub _DISPATCH : Private {
@@ -268,6 +301,20 @@ sub register_action_methods {
     }
 }
 
+sub _apply_action_class_roles {
+    my ($self, $class, @roles) = @_;
+
+    load_class($_) for @roles;
+    my $meta = Moose::Meta::Class->initialize($class)->create_anon_class(
+        superclasses => [$class],
+        roles        => \@roles,
+        cache        => 1,
+    );
+    $meta->add_method(meta => sub { $meta });
+
+    return $meta->name;
+}
+
 sub action_class {
     my $self = shift;
     my %args = @_;
@@ -285,7 +332,21 @@ sub create_action {
     my %args = @_;
 
     my $class = $self->action_class(%args);
-    my $action_args = $self->config->{action_args};
+
+    load_class($class);
+    Moose->init_meta(for_class => $class)
+        unless Class::MOP::does_metaclass_exist($class);
+
+    unless ($args{name} =~ /^_(DISPATCH|BEGIN|AUTO|ACTION|END)$/) {
+       my @roles = $self->gather_action_roles(%args);
+       $class = $self->_apply_action_class_roles($class, @roles) if @roles;
+    }
+
+    my $action_args = (
+        ref($self)
+            ? $self->action_args
+            : $self->config->{action_args}
+    );
 
     my %extra_args = (
         %{ $action_args->{'*'}           || {} },
@@ -295,6 +356,15 @@ sub create_action {
     return $class->new({ %extra_args, %args });
 }
 
+sub gather_action_roles {
+   my ($self, %args) = @_;
+
+   return (
+      (blessed $self ? $self->_action_roles : ()),
+      @{ $args{attributes}->{Does} || [] },
+   );
+}
+
 sub _parse_attrs {
     my ( $self, $c, $name, @attrs ) = @_;
 
@@ -460,6 +530,32 @@ sub _parse_MyAction_attr {
     return ( 'ActionClass', $value );
 }
 
+sub _parse_Does_attr {
+    my ($self, $app, $name, $value) = @_;
+    return Does => $self->_expand_role_shortname($value);
+}
+
+sub _expand_role_shortname {
+    my ($self, @shortnames) = @_;
+    my $app = $self->_application;
+
+    my $prefix = $self->can('_action_role_prefix') ? $self->_action_role_prefix : ['Catalyst::ActionRole::'];
+    my @prefixes = (qq{${app}::ActionRole::}, @$prefix);
+
+    return String::RewritePrefix->rewrite(
+        { ''  => sub {
+            my $loaded = load_first_existing_class(
+                map { "$_$_[0]" } @prefixes
+            );
+            return first { $loaded =~ /^$_/ }
+              sort { length $b <=> length $a } @prefixes;
+          },
+          '~' => $prefixes[0],
+          '+' => '' },
+        @shortnames,
+    );
+}
+
 __PACKAGE__->meta->make_immutable;
 
 1;
@@ -577,6 +673,10 @@ action class to use.
 Called with a hash of data to be use for construction of a new
 Catalyst::Action (or appropriate sub/alternative class) object.
 
+=head2 $self->gather_action_roles(\%action_args)
+
+Gathers the list of roles to apply to an action with the given %action_args.
+
 =head2 $self->_application
 
 =head2 $self->_app
index 3b7b0c4..5f72fba 100644 (file)
@@ -253,7 +253,7 @@ sub recurse_match {
 
                 if (!$best_action                       ||
                     @parts < @{$best_action->{parts}}   ||
-                    (!@parts && $args_attr eq 0)){
+                    (!@parts && defined($args_attr) && $args_attr eq "0")){
                     $best_action = {
                         actions => [ $action ],
                         captures=> [],
index daf53d1..4781e45 100644 (file)
@@ -180,6 +180,9 @@ sub finalize_error {
         $title = $name = "$name on Catalyst $Catalyst::VERSION";
         $name  = "<h1>$name</h1>";
 
+        # Don't show context in the dump
+        $c->res->_clear_context;
+
         # Don't show body parser in the dump
         $c->req->_clear_body;
 
index 3d0c03a..d2c1c7f 100644 (file)
@@ -23,13 +23,15 @@ has user => (is => 'rw');
 sub snippets        { shift->captures(@_) }
 
 has _read_position => (
-    init_arg => undef,
+    # FIXME: work around Moose bug RT#75367
+    # init_arg => undef,
     is => 'ro',
     writer => '_set_read_position',
     default => 0,
 );
 has _read_length => (
-    init_arg => undef,
+    # FIXME: work around Moose bug RT#75367
+    # init_arg => undef,
     is => 'ro',
     default => sub {
         my $self = shift;
@@ -218,7 +220,7 @@ sub prepare_body_parameters {
     my ( $self ) = @_;
 
     $self->prepare_body if ! $self->_has_body;
-    return unless $self->_body;
+    return {} unless $self->_body;
 
     return $self->_body->param;
 }
@@ -642,9 +644,8 @@ Shortcut for $req->headers->referer. Returns the referring page.
 Returns true or false, indicating whether the connection is secure
 (https). Note that the URI scheme (e.g., http vs. https) must be determined
 through heuristics, and therefore the reliability of $req->secure will depend
-on your server configuration. If you are serving secure pages on the standard
-SSL port (443) and/or setting the HTTPS environment variable, $req->secure
-should be valid.
+on your server configuration. If you are setting the HTTPS environment variable, 
+$req->secure should be valid.
 
 =head2 $req->captures
 
index cd81857..eebe22c 100644 (file)
@@ -43,6 +43,11 @@ has headers   => (
   required => 1,
   lazy => 1,
 );
+has _context => (
+  is => 'rw',
+  weak_ref => 1,
+  clearer => '_clear_context',
+);
 
 sub output { shift->body(@_) }
 
@@ -52,7 +57,7 @@ sub write {
     my ( $self, $buffer ) = @_;
 
     # Finalize headers if someone manually writes output
-    $self->finalize_headers;
+    $self->_context->finalize_headers;
 
     $buffer = q[] unless defined $buffer;
 
index f4df026..f079d05 100644 (file)
@@ -7,7 +7,7 @@ BEGIN { require 5.008003; }
 
 # Remember to update this in Catalyst as well!
 
-our $VERSION = '5.90011';
+our $VERSION = '5.90014';
 
 =head1 NAME
 
index e067248..dc2adde 100644 (file)
@@ -175,7 +175,19 @@ Returns home directory for given class.
 =head2 dist_indicator_file_list
 
 Returns a list of files which can be tested to check if you're inside
-a checkout
+a CPAN distribution which is not yet installed.
+
+These are:
+
+=over
+
+=item Makefile.PL
+
+=item Build.PL
+
+=item dist.ini
+
+=back
 
 =cut
 
index bc2038e..eebfe48 100644 (file)
@@ -193,6 +193,25 @@ sub run_tests {
         is_deeply $action->attributes->{extra_attribute}, [13];
         is_deeply $action->attributes->{another_extra_attribute}, ['foo'];
     }
+    {
+        ok( my $response = request('http://localhost/action_action_nine'),
+            'Request' );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is( $response->header('X-Catalyst-Action'),
+            'action_action_nine', 'Test Action' );
+        is(
+            $response->header('X-Test-Class'),
+            'TestApp::Controller::Action::Action',
+            'Test Class'
+        );
+        is( $response->header('X-TestExtraArgsAction'), '42,13', 'Extra args get passed to action constructor' );
+        like(
+            $response->content,
+            qr/^bless\( .* 'Catalyst::Request' \)$/s,
+            'Content is a serialized Catalyst::Request'
+        );
+    }
 }
 
 done_testing;
index 1bc9cbf..f6d93ee 100644 (file)
@@ -29,6 +29,7 @@ sub run_tests {
         ok( my $response = request('http://localhost/streaming'), 'Request' );
         ok( $response->is_success, 'Response Successful 2xx' );
         is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is( $response->header('X-Test-Header'), 'valid', 'Headers sent properly' );
 
         SKIP:
         {
@@ -67,6 +68,7 @@ EOF
         ok( $response->is_success, 'Response Successful 2xx' );
         is( $response->content_type, 'text/plain', 'Response Content-Type' );
         is( $response->content_length, -s $file, 'Response Content-Length' );
+        is( $response->header('X-Test-Header'), 'valid', 'Headers sent properly' );
         is( $response->content, $buffer, 'Content is read from filehandle' );
 
         ok( $response = request('http://localhost/action/streaming/body_glob'),
@@ -74,6 +76,7 @@ EOF
         ok( $response->is_success, 'Response Successful 2xx' );
         is( $response->content_type, 'text/plain', 'Response Content-Type' );
         is( $response->content_length, -s $file, 'Response Content-Length' );
+        is( $response->header('X-Test-Header'), 'valid', 'Headers sent properly' );
         is( $response->content, $buffer, 'Content is read from filehandle' );
     }
 
@@ -83,6 +86,7 @@ EOF
         ok( my $response = request('http://localhost/action/streaming/body_large'), 'Request' );
         ok( $response->is_success, 'Response Successful 2xx' );
         is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is( $response->header('X-Test-Header'), 'valid', 'Headers sent properly' );
         is( $response->content_length, $size, 'Response Content-Length' );
         is( $response->content, "\0" x $size, 'Content is read from filehandle' );
     }
diff --git a/t/aggregate/live_component_controller_actionroles.t b/t/aggregate/live_component_controller_actionroles.t
new file mode 100644 (file)
index 0000000..0bf1b0c
--- /dev/null
@@ -0,0 +1,36 @@
+use strict;
+use warnings;
+use Test::More;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+
+use Catalyst::Test 'TestApp';
+
+my %roles = (
+    foo  => 'TestApp::ActionRole::Moo',
+    bar  => 'TestApp::ActionRole::Moo',
+    baz  => 'Moo',
+    quux => 'Catalyst::ActionRole::Zoo',
+);
+
+while (my ($path, $role) = each %roles) {
+    my $resp = request("/actionroles/${path}");
+    ok($resp->is_success);
+    is($resp->content, $role);
+    is($resp->header('X-Affe'), 'Tiger');
+}
+
+{
+    my $resp = request("/actionroles/corge");
+    ok($resp->is_success);
+    is($resp->content, 'TestApp::ActionRole::Moo');
+    is($resp->header('X-Affe'), 'Tiger');
+   is($resp->header('X-Action-After'), 'moo');
+}
+{
+    my $resp = request("/actionroles/frew");
+    ok($resp->is_success);
+    is($resp->content, 'hello', 'action_args are honored with ActionRoles');
+ }
+done_testing;
index 0edba01..2927f18 100644 (file)
@@ -77,6 +77,15 @@ rmtree "$FindBin::Bin/../../t/tmp" if -d "$FindBin::Bin/../../t/tmp";
 
 is( $return, 0, 'live tests' );
 
+# kill 'INT' doesn't exist in Windows, so to prevent child hanging,
+# this process will need to commit seppuku to clean up the children.
+if ($^O eq 'MSWin32') {
+    # Furthermore, it needs to do it 'politely' so that TAP doesn't 
+    # smell anything 'dubious'.
+    require Win32::Process;  # core in all versions of Win32 Perl
+    Win32::Process::KillProcess($$, $return);
+}
+
 sub wait_port_timeout {
     my ($port, $timeout) = @_;
 
index 199ea25..2139a8b 100644 (file)
@@ -3,7 +3,8 @@ package Catalyst::Action::TestAfter;
 use strict;
 use warnings;
 
-use base qw/Catalyst::Action/;
+use base qw/Catalyst::Action/; # N.B. Keep as a non-moose class, this also
+                               # tests metaclass initialization works as expected
 
 sub execute {
     my $self = shift;
diff --git a/t/lib/Catalyst/ActionRole/Moo.pm b/t/lib/Catalyst/ActionRole/Moo.pm
new file mode 100644 (file)
index 0000000..3d4aa51
--- /dev/null
@@ -0,0 +1,12 @@
+package Catalyst::ActionRole::Moo;
+
+use Moose::Role;
+
+use namespace::autoclean;
+
+after execute => sub {
+    my ($self, $controller, $c) = @_;
+    $c->response->body(__PACKAGE__);
+};
+
+1;
diff --git a/t/lib/Catalyst/ActionRole/Zoo.pm b/t/lib/Catalyst/ActionRole/Zoo.pm
new file mode 100644 (file)
index 0000000..d4f0c9f
--- /dev/null
@@ -0,0 +1,12 @@
+package Catalyst::ActionRole::Zoo;
+
+use Moose::Role;
+
+use namespace::autoclean;
+
+after execute => sub {
+    my ($self, $controller, $c) = @_;
+    $c->response->body(__PACKAGE__);
+};
+
+1;
diff --git a/t/lib/Moo.pm b/t/lib/Moo.pm
new file mode 100644 (file)
index 0000000..c28806a
--- /dev/null
@@ -0,0 +1,12 @@
+package Moo;
+
+use Moose::Role;
+
+use namespace::autoclean;
+
+after execute => sub {
+    my ($self, $controller, $c) = @_;
+    $c->response->body(__PACKAGE__);
+};
+
+1;
index 1dfe999..29060e8 100644 (file)
@@ -42,7 +42,16 @@ has 'my_greeting_obj_lazy' => (
 
 our $VERSION = '0.01';
 
-TestApp->config( name => 'TestApp', root => '/some/dir', use_request_uri_for_path => 1 );
+TestApp->config( 
+    name => 'TestApp', 
+    root => '/some/dir', 
+    use_request_uri_for_path => 1, 
+    'Controller::Action::Action' => {
+        action_args => {
+            action_action_nine => { another_extra_arg => 13 }
+        }
+    }
+);
 
 # Test bug found when re-adjusting the metaclass compat code in Moose
 # in 292360. Test added to Moose in 4b760d6, but leave this attribute
@@ -116,7 +125,21 @@ sub finalize_error {
     sub Catalyst::Log::error { }
 }
 
+<<<<<<< HEAD
 # Make sure we can load Inline plugins.
+=======
+# Pretend to be Plugin::Session and hook finalize_headers to send a header
+
+sub finalize_headers {
+    my $c = shift;
+
+    $c->res->header('X-Test-Header', 'valid');
+
+    return $c->maybe::next::method(@_);
+}
+
+# Make sure we can load Inline plugins. 
+>>>>>>> origin/master
 
 package Catalyst::Plugin::Test::Inline;
 
diff --git a/t/lib/TestApp/Action/TestActionArgsFromConstructor.pm b/t/lib/TestApp/Action/TestActionArgsFromConstructor.pm
new file mode 100644 (file)
index 0000000..67f8a13
--- /dev/null
@@ -0,0 +1,18 @@
+package TestApp::Action::TestActionArgsFromConstructor;
+
+use Moose;
+use namespace::autoclean;
+
+extends 'Catalyst::Action';
+
+has [qw/extra_arg another_extra_arg/] => ( is => 'ro' );
+
+after execute => sub {
+    my ($self, $controller, $ctx) = @_;
+    $ctx->response->header('X-TestExtraArgsAction' => join q{,} => $self->extra_arg, $self->another_extra_arg);
+};
+
+__PACKAGE__->meta->make_immutable;
+
+1;
+
diff --git a/t/lib/TestApp/ActionRole/Boo.pm b/t/lib/TestApp/ActionRole/Boo.pm
new file mode 100644 (file)
index 0000000..f55f9fe
--- /dev/null
@@ -0,0 +1,16 @@
+package TestApp::ActionRole::Boo;
+
+use Moose::Role;
+
+has boo => (
+    is       => 'ro',
+    required => 1,
+);
+
+around execute => sub {
+    my ($orig, $self, $controller, $ctx, @rest) = @_;
+    $ctx->stash(action_boo => $self->boo);
+    return $self->$orig($controller, $ctx, @rest);
+};
+
+1;
diff --git a/t/lib/TestApp/ActionRole/Kooh.pm b/t/lib/TestApp/ActionRole/Kooh.pm
new file mode 100644 (file)
index 0000000..fc82bf2
--- /dev/null
@@ -0,0 +1,12 @@
+package TestApp::ActionRole::Kooh;
+
+use Moose::Role;
+
+use namespace::autoclean;
+
+after execute => sub {
+    my ($self, $controller, $c) = @_;
+    $c->response->header('X-Affe' => 'Tiger');
+};
+
+1;
diff --git a/t/lib/TestApp/ActionRole/Moo.pm b/t/lib/TestApp/ActionRole/Moo.pm
new file mode 100644 (file)
index 0000000..d0fd290
--- /dev/null
@@ -0,0 +1,10 @@
+package TestApp::ActionRole::Moo;
+
+use Moose::Role;
+
+after execute => sub {
+    my ($self, $controller, $c) = @_;
+    $c->response->body(__PACKAGE__);
+};
+
+1;
index 6cee5f5..515fb2a 100644 (file)
@@ -58,4 +58,8 @@ sub action_action_eight : Global  {
     $c->forward('TestApp::View::Dump::Action');
 }
 
+sub action_action_nine : Global : ActionClass('~TestActionArgsFromConstructor') {
+    my ( $self, $c ) = @_;
+    $c->forward('TestApp::View::Dump::Request');
+}
 1;
diff --git a/t/lib/TestApp/Controller/ActionRoles.pm b/t/lib/TestApp/Controller/ActionRoles.pm
new file mode 100644 (file)
index 0000000..37c24f9
--- /dev/null
@@ -0,0 +1,30 @@
+package TestApp::Controller::ActionRoles;
+
+use Moose;
+
+BEGIN { extends 'Catalyst::Controller' }
+
+__PACKAGE__->config(
+    action_roles => ['~Kooh'],
+    action_args => {
+        frew => { boo => 'hello' },
+    },
+);
+
+sub foo  : Local Does('Moo')  {}
+sub bar  : Local Does('~Moo') {}
+sub baz  : Local Does('+Moo') {}
+sub quux : Local Does('Zoo')  {}
+
+sub corge : Local Does('Moo') ActionClass('TestAfter') {
+    my ($self, $ctx) = @_;
+    $ctx->stash(after_message => 'moo');
+}
+
+sub frew : Local Does('Boo')  {
+    my ($self, $ctx) = @_;
+    my $boo = $ctx->stash->{action_boo};
+    $ctx->response->body($boo);
+}
+
+1;
index 5732211..ea6bf3a 100644 (file)
@@ -10,4 +10,11 @@ sub default : Private {
     $c->res->status(200);
 }
 
+sub no_params : Local {
+    my ( $self, $c ) = @_;
+    my $params = $c->req->body_parameters;
+    $c->res->output(ref $params);
+    $c->res->status(200);
+}
+
 1;
index 2beefe6..48a13fd 100644 (file)
@@ -1,10 +1,12 @@
 package TestAppBadlyImmutable;
 use Catalyst qw/+TestPluginWithConstructor/;
+
+use base qw/Class::Accessor Catalyst/;
+
 use Test::More;
 
 __PACKAGE__->setup;
 
-ok !__PACKAGE__->meta->is_immutable, 'Am not already immutable';
 __PACKAGE__->meta->make_immutable( inline_constructor => 0 );
 ok __PACKAGE__->meta->is_immutable, 'Am now immutable';
 
index e7f8df9..8248527 100644 (file)
@@ -50,5 +50,10 @@ my $req = '/dump/request';
     is($response, 'that', 'body param overridden');
 }
 
+{
+       my $response = request( POST( '/bodyparams/no_params' ) )->content;
+    is($response, 'HASH', 'empty body param is hashref');
+}
+
 done_testing;
 
index c0cb13a..07de6d9 100644 (file)
@@ -8,13 +8,14 @@
 # that plugins don't get it wrong for us.
 
 # Also tests method modifiers and etc in MyApp.pm still work as expected.
-use Test::More tests => 8;
+use Test::More;
 use Test::Exception;
 use Moose::Util qw/find_meta/;
 use FindBin;
 use lib "$FindBin::Bin/lib";
 
 use Catalyst::Test qw/TestAppPluginWithConstructor/;
+TestAppPluginWithConstructor->_make_immutable_if_needed;
 ok find_meta('TestAppPluginWithConstructor')->is_immutable,
     'Am immutable after use';
 
@@ -22,8 +23,13 @@ ok request('/foo')->is_success, 'Can get /foo';
 is $TestAppPluginWithConstructor::MODIFIER_FIRED, 1, 'Before modifier was fired correctly.';
 
 my $warning;
-local $SIG{__WARN__} = sub { $warning = $_[0] };
-eval "use TestAppBadlyImmutable;";
+eval "use TestAppBadlyImmutable";
+local $SIG{__WARN__} = sub { $warning .= $_[0] };
+
+TestAppBadlyImmutable->_make_immutable_if_needed;
+
 like $warning, qr/\QYou made your application class (TestAppBadlyImmutable) immutable/,
     'An application class that is already immutable but does not inline the constructor warns at ->setup';
 
+done_testing;
+
similarity index 98%
rename from t/aggregate/unit_core_script_test.t
rename to t/unit_core_script_test.t
index e475651..1328bde 100644 (file)
@@ -3,7 +3,7 @@ use warnings;
 
 use Carp qw(croak);
 use FindBin qw/$Bin/;
-use lib "$Bin/../lib";
+use lib "$Bin/lib";
 
 use Test::More;
 use Test::Exception;