[Catalyst-commits] r9074 - in Catalyst-Runtime/5.70/trunk: . lib lib/Catalyst lib/Catalyst/DispatchType t t/lib t/lib/TestApp/Controller/Action t/lib/TestApp/View

marcus at dev.catalyst.perl.org marcus at dev.catalyst.perl.org
Mon Jan 12 12:07:37 GMT 2009


Author: marcus
Date: 2009-01-12 12:07:37 +0000 (Mon, 12 Jan 2009)
New Revision: 9074

Added:
   Catalyst-Runtime/5.70/trunk/t/lib/TestApp/Controller/Action/Visit.pm
   Catalyst-Runtime/5.70/trunk/t/live_component_controller_action_go.t
   Catalyst-Runtime/5.70/trunk/t/live_component_controller_action_visit.t
Modified:
   Catalyst-Runtime/5.70/trunk/Changes
   Catalyst-Runtime/5.70/trunk/lib/Catalyst.pm
   Catalyst-Runtime/5.70/trunk/lib/Catalyst/DispatchType.pm
   Catalyst-Runtime/5.70/trunk/lib/Catalyst/DispatchType/Chained.pm
   Catalyst-Runtime/5.70/trunk/lib/Catalyst/Dispatcher.pm
   Catalyst-Runtime/5.70/trunk/lib/Catalyst/Runtime.pm
   Catalyst-Runtime/5.70/trunk/t/lib/TestApp.pm
   Catalyst-Runtime/5.70/trunk/t/lib/TestApp/Controller/Action/Go.pm
   Catalyst-Runtime/5.70/trunk/t/lib/TestApp/Controller/Action/TestRelative.pm
   Catalyst-Runtime/5.70/trunk/t/lib/TestApp/View/Dump.pm
   Catalyst-Runtime/5.70/trunk/t/unit_core_mvc.t
Log:
Backport go/visit from 5.8 (janh), prepare for dev release

Modified: Catalyst-Runtime/5.70/trunk/Changes
===================================================================
--- Catalyst-Runtime/5.70/trunk/Changes	2009-01-12 05:08:43 UTC (rev 9073)
+++ Catalyst-Runtime/5.70/trunk/Changes	2009-01-12 12:07:37 UTC (rev 9074)
@@ -1,6 +1,6 @@
 # This file documents the revision history for Perl extension Catalyst.
 
-5.7XXXXXX XXXX
+5.7099_04 2009-01-12 13:06:00
         - Add environment hack for FastCGI under IIS (Simon Bertrang)
           - Test for this and preexisting Lighty hack (Simon Bertrang)
         - Change streaming test to serve itself rather than 01use.t, making test

Modified: Catalyst-Runtime/5.70/trunk/lib/Catalyst/DispatchType/Chained.pm
===================================================================
--- Catalyst-Runtime/5.70/trunk/lib/Catalyst/DispatchType/Chained.pm	2009-01-12 05:08:43 UTC (rev 9073)
+++ Catalyst-Runtime/5.70/trunk/lib/Catalyst/DispatchType/Chained.pm	2009-01-12 12:07:37 UTC (rev 9074)
@@ -294,6 +294,31 @@
    
 }
 
+=head2 $c->expand_action($action)
+
+Return a list of actions that represents a chained action. See 
+L<Catalyst::Dispatcher> for more info. You probably want to
+use the expand_action it provides rather than this directly.
+
+=cut
+
+sub expand_action {
+    my ($self, $action) = @_;
+
+    return unless $action->attributes && $action->attributes->{Chained};
+
+    my @chain;
+    my $curr = $action;
+
+    while ($curr) {
+        push @chain, $curr;
+        my $parent = $curr->attributes->{Chained}->[0];
+        $curr = $self->{'actions'}{$parent};
+    }
+
+    return Catalyst::ActionChain->from_chain([reverse @chain]);
+}
+
 =head1 USAGE
 
 =head2 Introduction

Modified: Catalyst-Runtime/5.70/trunk/lib/Catalyst/DispatchType.pm
===================================================================
--- Catalyst-Runtime/5.70/trunk/lib/Catalyst/DispatchType.pm	2009-01-12 05:08:43 UTC (rev 9073)
+++ Catalyst-Runtime/5.70/trunk/lib/Catalyst/DispatchType.pm	2009-01-12 12:07:37 UTC (rev 9074)
@@ -46,6 +46,15 @@
 
 sub register { }
 
+=head2 $self->expand_action
+
+Default fallback, returns nothing. See L<Catalyst::Dispatcher> for more info
+about expand_action.
+
+=cut
+
+sub expand_action { }
+
 =head2 $self->uri_for_action( $action, \@captures )
 
 abstract method, to be implemented by dispatchtypes. Takes a

Modified: Catalyst-Runtime/5.70/trunk/lib/Catalyst/Dispatcher.pm
===================================================================
--- Catalyst-Runtime/5.70/trunk/lib/Catalyst/Dispatcher.pm	2009-01-12 05:08:43 UTC (rev 9073)
+++ Catalyst-Runtime/5.70/trunk/lib/Catalyst/Dispatcher.pm	2009-01-12 12:07:37 UTC (rev 9074)
@@ -151,9 +151,14 @@
 
     my $action;
 
-    # go to a string path ("/foo/bar/gorch")
-    # or action object which stringifies to that
-    $action = $self->_invoke_as_path( $c, "$command", \@args );
+    if (Scalar::Util::blessed($command) && $command->isa('Catalyst::Action')) {
+        $action = $command;
+    }
+    else {
+        # go to a string path ("/foo/bar/gorch")
+        # or action object which stringifies to that
+        $action = $self->_invoke_as_path( $c, "$command", \@args );
+    }
 
     # go to a component ( "MyApp::*::Foo" or $c->component("...")
     # - a path or an object)
@@ -165,6 +170,67 @@
     return $action, \@args;
 }
 
+=head2 $self->visit( $c, $command [, \@arguments ] )
+
+Documented in L<Catalyst>
+
+=cut
+
+sub visit {
+    my $self = shift;
+    $self->_do_visit('visit', @_);
+}
+
+sub _do_visit {
+    my $self = shift;
+    my $opname = shift;
+    my ( $c, $command ) = @_;
+    my ( $action, $args ) = $self->_command2action(@_);
+    my $error = qq/Couldn't $opname("$command"): /;
+
+    if (!$action) {
+        $error .= qq/Couldn't $opname to command "$command": /
+                 .qq/Invalid action or component./;
+    }
+    elsif (!defined $action->namespace) {
+        $error .= qq/Action has no namespace: cannot $opname() to a plain /
+                 .qq/method or component, must be a :Action or some sort./
+    }
+    elsif (!$action->class->can('_DISPATCH')) {
+        $error .= qq/Action cannot _DISPATCH. /
+                 .qq/Did you try to $opname() a non-controller action?/;
+    }
+    else {
+        $error = q();
+    }
+
+    if($error) {
+        $c->error($error);
+        $c->log->debug($error) if $c->debug;
+        return 0;
+    }
+
+    $action = $self->expand_action($action);
+
+    local $c->request->{arguments} = $args;
+    local $c->{namespace} = $action->{'namespace'};
+    local $c->{action} = $action;
+
+    $self->dispatch($c);
+}
+
+=head2 $self->go( $c, $command [, \@arguments ] )
+
+Documented in L<Catalyst>
+
+=cut
+
+sub go {
+    my $self = shift;
+    $self->_do_visit('go', @_);
+    die $Catalyst::GO;
+}
+
 =head2 $self->forward( $c, $command [, \@arguments ] )
 
 Documented in L<Catalyst>
@@ -392,6 +458,25 @@
     return undef;
 }
 
+=head2 expand_action 
+
+expand an action into a full representation of the dispatch.
+mostly useful for chained, other actions will just return a
+single action.
+
+=cut
+
+sub expand_action {
+    my ($self, $action) = @_;
+
+    foreach my $dispatch_type (@{ $self->dispatch_types }) {
+        my $expanded = $dispatch_type->expand_action($action);
+        return $expanded if $expanded;
+    }
+
+    return $action;
+}
+
 =head2 $self->register( $c, $action )
 
 Make sure all required dispatch types for this action are loaded, then

Modified: Catalyst-Runtime/5.70/trunk/lib/Catalyst/Runtime.pm
===================================================================
--- Catalyst-Runtime/5.70/trunk/lib/Catalyst/Runtime.pm	2009-01-12 05:08:43 UTC (rev 9073)
+++ Catalyst-Runtime/5.70/trunk/lib/Catalyst/Runtime.pm	2009-01-12 12:07:37 UTC (rev 9074)
@@ -7,7 +7,7 @@
 
 # Remember to update this in Catalyst as well!
 
-our $VERSION='5.7099_03';
+our $VERSION='5.7099_04';
 
 $VERSION= eval $VERSION; 
 

Modified: Catalyst-Runtime/5.70/trunk/lib/Catalyst.pm
===================================================================
--- Catalyst-Runtime/5.70/trunk/lib/Catalyst.pm	2009-01-12 05:08:43 UTC (rev 9073)
+++ Catalyst-Runtime/5.70/trunk/lib/Catalyst.pm	2009-01-12 12:07:37 UTC (rev 9074)
@@ -49,6 +49,7 @@
 our $START     = time;
 our $RECURSION = 1000;
 our $DETACH    = "catalyst_detach\n";
+our $GO        = "catalyst_go\n";
 
 __PACKAGE__->mk_classdata($_)
   for qw/components arguments dispatcher engine log dispatcher_class
@@ -63,7 +64,7 @@
 
 # Remember to update this in Catalyst::Runtime as well!
 
-our $VERSION = '5.7099_03';
+our $VERSION = '5.7099_04';
 
 sub import {
     my ( $class, @arguments ) = @_;
@@ -327,6 +328,40 @@
 
 sub detach { my $c = shift; $c->dispatcher->detach( $c, @_ ) }
 
+=head2 $c->visit( $action [, \@arguments ] )
+
+=head2 $c->visit( $class, $method, [, \@arguments ] )
+
+Almost the same as C<forward>, but does a full dispatch, instead of just
+calling the new C<$action> / C<$class-E<gt>$method>. This means that C<begin>,
+C<auto> and the method you go to are called, just like a new request.
+
+C<$c-E<gt>stash> is kept unchanged.
+
+In effect, C<visit> allows you to "wrap" another action, just as it
+would have been called by dispatching from a URL, while the analogous
+C<go> allows you to transfer control to another action as if it had
+been reached directly from a URL.
+
+=cut
+
+sub visit { my $c = shift; $c->dispatcher->visit( $c, @_ ) }
+
+=head2 $c->go( $action [, \@arguments ] )
+
+=head2 $c->go( $class, $method, [, \@arguments ] )
+
+Almost the same as C<detach>, but does a full dispatch like C<visit>,
+instead of just calling the new C<$action> /
+C<$class-E<gt>$method>. This means that C<begin>, C<auto> and the
+method you visit are called, just like a new request.
+
+C<$c-E<gt>stash> is kept unchanged.
+
+=cut
+
+sub go { my $c = shift; $c->dispatcher->go( $c, @_ ) }
+
 =head2 $c->response
 
 =head2 $c->res
@@ -1339,6 +1374,9 @@
         if ( !ref($error) and $error eq $DETACH ) {
             die $DETACH if($c->depth > 1);
         }
+        elsif ( !ref($error) and $error eq $GO ) {
+            die $GO if($c->depth > 0);
+        }
         else {
             unless ( ref $error ) {
                 no warnings 'uninitialized';
@@ -2554,6 +2592,8 @@
 
 willert: Sebastian Willert <willert at cpan.org>
 
+batman: Jan Henning Thorsen <pm at flodhest.net>
+
 =head1 LICENSE
 
 This library is free software, you can redistribute it and/or modify it under

Modified: Catalyst-Runtime/5.70/trunk/t/lib/TestApp/Controller/Action/Go.pm
===================================================================
--- Catalyst-Runtime/5.70/trunk/t/lib/TestApp/Controller/Action/Go.pm	2009-01-12 05:08:43 UTC (rev 9073)
+++ Catalyst-Runtime/5.70/trunk/t/lib/TestApp/Controller/Action/Go.pm	2009-01-12 12:07:37 UTC (rev 9074)
@@ -25,7 +25,7 @@
 
 sub five : Local {
     my ( $self, $c ) = @_;
-    $c->go('View::Dump::Request');
+    $c->forward('View::Dump::Request');
 }
 
 sub inheritance : Local {
@@ -66,6 +66,18 @@
     $c->go('/action/chained/foo/spoon',[1]);
 }
 
+sub view : Local {
+    my ( $self, $c, $val ) = @_;
+    eval { $c->go('View::Dump') };
+    $c->res->body( $@ ? $@ : "go() did not die" );
+}
+
+sub model : Local {
+    my ( $self, $c, $val ) = @_;
+    eval { $c->go('Model::Foo') };
+    $c->res->body( $@ ? $@ : "go() did not die" );
+}
+
 sub args_embed_relative : Local {
     my ( $self, $c ) = @_;
     $c->go('embed/ok');

Modified: Catalyst-Runtime/5.70/trunk/t/lib/TestApp/Controller/Action/TestRelative.pm
===================================================================
--- Catalyst-Runtime/5.70/trunk/t/lib/TestApp/Controller/Action/TestRelative.pm	2009-01-12 05:08:43 UTC (rev 9073)
+++ Catalyst-Runtime/5.70/trunk/t/lib/TestApp/Controller/Action/TestRelative.pm	2009-01-12 12:07:37 UTC (rev 9074)
@@ -26,4 +26,15 @@
     my ( $self, $c ) = @_;
     $c->go( 'TestApp::Controller::Action::Go', 'one' );
 }
+
+sub relative_visit : Local {
+    my ( $self, $c ) = @_;
+    $c->visit('/action/visit/one');
+}
+
+sub relative_visit_two : Local {
+    my ( $self, $c ) = @_;
+    $c->visit( 'TestApp::Controller::Action::Visit', 'one' );
+}
+
 1;

Added: Catalyst-Runtime/5.70/trunk/t/lib/TestApp/Controller/Action/Visit.pm
===================================================================
--- Catalyst-Runtime/5.70/trunk/t/lib/TestApp/Controller/Action/Visit.pm	                        (rev 0)
+++ Catalyst-Runtime/5.70/trunk/t/lib/TestApp/Controller/Action/Visit.pm	2009-01-12 12:07:37 UTC (rev 9074)
@@ -0,0 +1,101 @@
+package TestApp::Controller::Action::Visit;
+
+use strict;
+use base 'TestApp::Controller::Action';
+
+sub one : Local {
+    my ( $self, $c ) = @_;
+    $c->visit('two');
+}
+
+sub two : Private {
+    my ( $self, $c ) = @_;
+    $c->visit('three');
+}
+
+sub three : Local {
+    my ( $self, $c ) = @_;
+    $c->visit( $self, 'four' );
+}
+
+sub four : Private {
+    my ( $self, $c ) = @_;
+    $c->visit('/action/visit/five');
+}
+
+sub five : Local {
+    my ( $self, $c ) = @_;
+    $c->forward('View::Dump::Request');
+}
+
+sub inheritance : Local {
+    my ( $self, $c ) = @_;
+    $c->visit('/action/inheritance/a/b/default');
+}
+
+sub global : Local {
+    my ( $self, $c ) = @_;
+    $c->visit('/global_action');
+}
+
+sub with_args : Local {
+    my ( $self, $c, $arg ) = @_;
+    $c->visit( 'args', [$arg] );
+}
+
+sub with_method_and_args : Local {
+    my ( $self, $c, $arg ) = @_;
+    $c->visit( qw/TestApp::Controller::Action::Visit args/, [$arg] );
+}
+
+sub args : Local {
+    my ( $self, $c, $val ) = @_;
+    die "passed argument does not match args" unless $val eq $c->req->args->[0];
+    $c->res->body($val);
+}
+
+sub visit_die : Local {
+    my ( $self, $c, $val ) = @_;
+    eval { $c->visit( 'args', [qq/new/] ) };
+    $c->res->body( $@ ? $@ : "visit() doesn't die" );
+}
+
+sub visit_chained : Local {
+    my ( $self, $c, $val ) = @_;
+    $c->visit('/action/chained/foo/spoon',[1]);
+}
+
+sub view : Local {
+    my ( $self, $c, $val ) = @_;
+    eval { $c->visit('View::Dump') };
+    $c->res->body( $@ ? $@ : "visit() did not die" );
+}
+
+sub model : Local {
+    my ( $self, $c, $val ) = @_;
+    eval { $c->visit('Model::Foo') };
+    $c->res->body( $@ ? $@ : "visit() did not die" );
+}
+
+sub args_embed_relative : Local {
+    my ( $self, $c ) = @_;
+    $c->visit('embed/ok');
+}
+
+sub args_embed_absolute : Local {
+    my ( $self, $c ) = @_;
+    $c->visit('/action/visit/embed/ok');
+}
+
+sub embed : Local {
+    my ( $self, $c, $ok ) = @_;
+    $ok ||= 'not ok';
+    $c->res->body($ok);
+}
+
+sub class_visit_test_action : Local {
+    my ( $self, $c ) = @_;
+    $c->visit(qw/TestApp class_visit_test_method/);
+}
+
+1;

Modified: Catalyst-Runtime/5.70/trunk/t/lib/TestApp/View/Dump.pm
===================================================================
--- Catalyst-Runtime/5.70/trunk/t/lib/TestApp/View/Dump.pm	2009-01-12 05:08:43 UTC (rev 9073)
+++ Catalyst-Runtime/5.70/trunk/t/lib/TestApp/View/Dump.pm	2009-01-12 12:07:37 UTC (rev 9074)
@@ -1,7 +1,7 @@
 package TestApp::View::Dump;
 
 use strict;
-use base 'Catalyst::Base';
+use base 'Catalyst::View';
 
 use Data::Dumper ();
 use Scalar::Util qw(weaken);

Modified: Catalyst-Runtime/5.70/trunk/t/lib/TestApp.pm
===================================================================
--- Catalyst-Runtime/5.70/trunk/t/lib/TestApp.pm	2009-01-12 05:08:43 UTC (rev 9073)
+++ Catalyst-Runtime/5.70/trunk/t/lib/TestApp.pm	2009-01-12 12:07:37 UTC (rev 9074)
@@ -77,6 +77,11 @@
     $c->response->headers->header( 'X-Class-Go-Test-Method' => 1 );
 }
 
+sub class_visit_test_method :Private {
+    my ( $self, $c ) = @_;
+    $c->response->headers->header( 'X-Class-Visit-Test-Method' => 1 );
+}
+
 sub loop_test : Local {
     my ( $self, $c ) = @_;
 

Added: Catalyst-Runtime/5.70/trunk/t/live_component_controller_action_go.t
===================================================================
--- Catalyst-Runtime/5.70/trunk/t/live_component_controller_action_go.t	                        (rev 0)
+++ Catalyst-Runtime/5.70/trunk/t/live_component_controller_action_go.t	2009-01-12 12:07:37 UTC (rev 9074)
@@ -0,0 +1,277 @@
+#!perl
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+
+our $iters;
+
+BEGIN { $iters = $ENV{CAT_BENCH_ITERS} || 1; }
+
+use Test::More tests => 54 * $iters;
+use Catalyst;
+use Catalyst::Test 'TestApp';
+
+if ( $ENV{CAT_BENCHMARK} ) {
+    require Benchmark;
+    Benchmark::timethis( $iters, \&run_tests );
+}
+else {
+    for ( 1 .. $iters ) {
+        run_tests();
+    }
+}
+
+sub run_tests {
+    {
+        # Test go to global private action
+        ok( my $response = request('http://localhost/action/go/global'),
+            'Request' );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is( $response->header('X-Catalyst-Action'),
+            'action/go/global', 'Main Class Action' );
+    }
+
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::Go->one
+          TestApp::Controller::Action::Go->two
+          TestApp::Controller::Action::Go->three
+          TestApp::Controller::Action::Go->four
+          TestApp::Controller::Action::Go->five
+          TestApp::View::Dump::Request->process
+          TestApp->end
+        ];
+
+        @expected = map { /Action/ ? (_begin($_), $_) : ($_) } @expected;
+        my $expected = join( ", ", @expected );
+
+        # Test go to chain of actions.
+        ok( my $response = request('http://localhost/action/go/one'),
+            'Request' );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is( $response->header('X-Catalyst-Action'),
+            'action/go/one', 'Test Action' );
+        is(
+            $response->header('X-Test-Class'),
+            'TestApp::Controller::Action::Go',
+            'Test Class'
+        );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        like(
+            $response->content,
+            qr/^bless\( .* 'Catalyst::Request' \)$/s,
+            'Content is a serialized Catalyst::Request'
+        );
+    }
+
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::Go->go_die
+          TestApp::Controller::Action::Go->args
+          TestApp->end
+        ];
+
+        @expected = map { /Action/ ? (_begin($_), $_) : ($_) } @expected;
+        my $expected = join( ", ", @expected );
+
+        ok( my $response = request('http://localhost/action/go/go_die'),
+            'Request' );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is( $response->header('X-Catalyst-Action'),
+            'action/go/go_die', 'Test Action'
+        );
+        is(
+            $response->header('X-Test-Class'),
+            'TestApp::Controller::Action::Go',
+            'Test Class'
+        );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        is( $response->content, $Catalyst::GO, "Go died as expected" );
+    }
+    {
+        ok(
+            my $response = request('http://localhost/action/go/model'),
+            'Request with args'
+        );
+        is( $response->content,
+            q[FATAL ERROR: Couldn't go("Model::Foo"): Action cannot _DISPATCH. Did you try to go() a non-controller action?],
+            q[go('Model::...') test]
+        );
+    }
+    {
+        ok(
+            my $response = request('http://localhost/action/go/view'),
+            'Request with args'
+        );
+        is( $response->content,
+            q[FATAL ERROR: Couldn't go("View::Dump"): Action cannot _DISPATCH. Did you try to go() a non-controller action?],
+            q[go('View::...') test]
+        );
+    }
+    {
+        ok(
+            my $response =
+              request('http://localhost/action/go/with_args/old'),
+            'Request with args'
+        );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content, 'old', 'go() with args (old)' );
+    }
+
+    {
+        ok(
+            my $response = request(
+                'http://localhost/action/go/with_method_and_args/new'),
+            'Request with args and method'
+        );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content, 'new', 'go() with args (new)' );
+    }
+
+    # test go with embedded args
+    {
+        ok(
+            my $response =
+              request('http://localhost/action/go/args_embed_relative'),
+            'Request'
+        );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content, 'ok', 'go() with args_embed_relative' );
+    }
+
+    {
+        ok(
+            my $response =
+              request('http://localhost/action/go/args_embed_absolute'),
+            'Request'
+        );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content, 'ok', 'go() with args_embed_absolute' );
+    }
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::TestRelative->relative_go
+          TestApp::Controller::Action::Go->one
+          TestApp::Controller::Action::Go->two
+          TestApp::Controller::Action::Go->three
+          TestApp::Controller::Action::Go->four
+          TestApp::Controller::Action::Go->five
+          TestApp::View::Dump::Request->process
+          TestApp->end
+        ];
+
+        @expected = map { /Action/ ? (_begin($_), $_) : ($_) } @expected;
+        my $expected = join( ", ", @expected );
+
+        # Test go to chain of actions.
+        ok( my $response = request('http://localhost/action/relative/relative_go'),
+            'Request' );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is( $response->header('X-Catalyst-Action'),
+            'action/relative/relative_go', 'Test Action' );
+        is(
+            $response->header('X-Test-Class'),
+            'TestApp::Controller::Action::Go',
+            'Test Class'
+        );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        like(
+            $response->content,
+            qr/^bless\( .* 'Catalyst::Request' \)$/s,
+            'Content is a serialized Catalyst::Request'
+        );
+    }
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::TestRelative->relative_go_two
+          TestApp::Controller::Action::Go->one
+          TestApp::Controller::Action::Go->two
+          TestApp::Controller::Action::Go->three
+          TestApp::Controller::Action::Go->four
+          TestApp::Controller::Action::Go->five
+          TestApp::View::Dump::Request->process
+          TestApp->end
+        ];
+
+        @expected = map { /Action/ ? (_begin($_), $_) : ($_) } @expected;
+        my $expected = join( ", ", @expected );
+
+        # Test go to chain of actions.
+        ok(
+            my $response =
+              request('http://localhost/action/relative/relative_go_two'),
+            'Request'
+        );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is(
+            $response->header('X-Catalyst-Action'),
+            'action/relative/relative_go_two',
+            'Test Action'
+        );
+        is(
+            $response->header('X-Test-Class'),
+            'TestApp::Controller::Action::Go',
+            'Test Class'
+        );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        like(
+            $response->content,
+            qr/^bless\( .* 'Catalyst::Request' \)$/s,
+            'Content is a serialized Catalyst::Request'
+        );
+    }
+
+    # test class go -- MUST FAIL!
+    {
+        ok(
+            my $response = request(
+                'http://localhost/action/go/class_go_test_action'),
+            'Request'
+        );
+        ok( !$response->is_success, 'Response Fails' );
+        is( $response->content,
+            q(FATAL ERROR: Couldn't go("TestApp"): Action has no namespace: cannot go() to a plain method or component, must be a :Action or some sort.),
+            'Error message'
+        );
+    }
+
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::Go->begin
+          TestApp::Controller::Action::Go->go_chained
+          TestApp::Controller::Action::Chained->begin
+          TestApp::Controller::Action::Chained->foo
+          TestApp::Controller::Action::Chained::Foo->spoon
+          TestApp::Controller::Action::Chained->end
+        ];
+
+        my $expected = join( ", ", @expected );
+
+        ok( my $response = request('http://localhost/action/go/go_chained'), 'go to chained + subcontroller endpoint' );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        is( $response->content, '; 1', 'Content OK' );
+    }
+
+}
+
+
+
+sub _begin {
+    local $_ = shift;
+    s/->(.*)$/->begin/;
+    return $_;
+}
+

Added: Catalyst-Runtime/5.70/trunk/t/live_component_controller_action_visit.t
===================================================================
--- Catalyst-Runtime/5.70/trunk/t/live_component_controller_action_visit.t	                        (rev 0)
+++ Catalyst-Runtime/5.70/trunk/t/live_component_controller_action_visit.t	2009-01-12 12:07:37 UTC (rev 9074)
@@ -0,0 +1,289 @@
+#!perl
+
+use strict;
+use warnings;
+
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+
+our $iters;
+
+BEGIN { $iters = $ENV{CAT_BENCH_ITERS} || 1; }
+
+use Test::More tests => 54 * $iters;
+use Catalyst::Test 'TestApp';
+
+if ( $ENV{CAT_BENCHMARK} ) {
+    require Benchmark;
+    Benchmark::timethis( $iters, \&run_tests );
+}
+else {
+    for ( 1 .. $iters ) {
+        run_tests();
+    }
+}
+
+sub run_tests {
+    {
+        # Test visit to global private action
+        ok( my $response = request('http://localhost/action/visit/global'),
+            'Request' );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is( $response->header('X-Catalyst-Action'),
+            'action/visit/global', 'Main Class Action' );
+    }
+
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::Visit->one
+          TestApp::Controller::Action::Visit->two
+          TestApp::Controller::Action::Visit->three
+          TestApp::Controller::Action::Visit->four
+          TestApp::Controller::Action::Visit->five
+          TestApp::View::Dump::Request->process
+          TestApp->end
+          TestApp->end
+          TestApp->end
+          TestApp->end
+          TestApp->end
+        ];
+
+        @expected = map { /Action/ ? (_begin($_), $_) : ($_) } @expected;
+        my $expected = join( ", ", @expected );
+
+        # Test visit to chain of actions.
+        ok( my $response = request('http://localhost/action/visit/one'),
+            'Request' );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is( $response->header('X-Catalyst-Action'),
+            'action/visit/one', 'Test Action' );
+        is(
+            $response->header('X-Test-Class'),
+            'TestApp::Controller::Action::Visit',
+            'Test Class'
+        );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        like(
+            $response->content,
+            qr/^bless\( .* 'Catalyst::Request' \)$/s,
+            'Content is a serialized Catalyst::Request'
+        );
+    }
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::Visit->visit_die
+          TestApp::Controller::Action::Visit->args
+          TestApp->end
+          TestApp->end
+        ];
+
+        @expected = map { /Action/ ? (_begin($_), $_) : ($_) } @expected;
+        my $expected = join( ", ", @expected );
+
+        ok( my $response = request('http://localhost/action/visit/visit_die'),
+            'Request' );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is( $response->header('X-Catalyst-Action'),
+            'action/visit/visit_die', 'Test Action'
+        );
+        is(
+            $response->header('X-Test-Class'),
+            'TestApp::Controller::Action::Visit',
+            'Test Class'
+        );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        is( $response->content, "visit() doesn't die", "Visit does not die" );
+    }
+    {
+        ok(
+            my $response = request('http://localhost/action/visit/model'),
+            'Request with args'
+        );
+        is( $response->content,
+            q[FATAL ERROR: Couldn't visit("Model::Foo"): Action cannot _DISPATCH. Did you try to visit() a non-controller action?]
+        );
+    }
+    {
+        ok(
+            my $response = request('http://localhost/action/visit/view'),
+            'Request with args'
+        );
+        is( $response->content,
+            q[FATAL ERROR: Couldn't visit("View::Dump"): Action cannot _DISPATCH. Did you try to visit() a non-controller action?]
+        );
+    }
+    {
+        ok(
+            my $response =
+              request('http://localhost/action/visit/with_args/old'),
+            'Request with args'
+        );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content, 'old', 'visit() with args (old)' );
+    }
+
+    {
+        ok(
+            my $response = request(
+                'http://localhost/action/visit/with_method_and_args/new'),
+            'Request with args and method'
+        );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content, 'new', 'visit() with args (new)' );
+    }
+
+    # test visit with embedded args
+    {
+        ok(
+            my $response =
+              request('http://localhost/action/visit/args_embed_relative'),
+            'Request'
+        );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content, 'ok', 'visit() with args_embed_relative' );
+    }
+
+    {
+        ok(
+            my $response =
+              request('http://localhost/action/visit/args_embed_absolute'),
+            'Request'
+        );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content, 'ok', 'visit() with args_embed_absolute' );
+    }
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::TestRelative->relative_visit
+          TestApp::Controller::Action::Visit->one
+          TestApp::Controller::Action::Visit->two
+          TestApp::Controller::Action::Visit->three
+          TestApp::Controller::Action::Visit->four
+          TestApp::Controller::Action::Visit->five
+          TestApp::View::Dump::Request->process
+          TestApp->end
+          TestApp->end
+          TestApp->end
+          TestApp->end
+          TestApp->end
+          TestApp->end
+        ];
+
+        @expected = map { /Action/ ? (_begin($_), $_) : ($_) } @expected;
+        my $expected = join( ", ", @expected );
+
+        # Test visit to chain of actions.
+        ok( my $response = request('http://localhost/action/relative/relative_visit'),
+            'Request' );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is( $response->header('X-Catalyst-Action'),
+            'action/relative/relative_visit', 'Test Action' );
+        is(
+            $response->header('X-Test-Class'),
+            'TestApp::Controller::Action::Visit',
+            'Test Class'
+        );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        like(
+            $response->content,
+            qr/^bless\( .* 'Catalyst::Request' \)$/s,
+            'Content is a serialized Catalyst::Request'
+        );
+    }
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::TestRelative->relative_visit_two
+          TestApp::Controller::Action::Visit->one
+          TestApp::Controller::Action::Visit->two
+          TestApp::Controller::Action::Visit->three
+          TestApp::Controller::Action::Visit->four
+          TestApp::Controller::Action::Visit->five
+          TestApp::View::Dump::Request->process
+          TestApp->end
+          TestApp->end
+          TestApp->end
+          TestApp->end
+          TestApp->end
+          TestApp->end
+        ];
+
+        @expected = map { /Action/ ? (_begin($_), $_) : ($_) } @expected;
+        my $expected = join( ", ", @expected );
+
+        # Test visit to chain of actions.
+        ok(
+            my $response =
+              request('http://localhost/action/relative/relative_visit_two'),
+            'Request'
+        );
+        ok( $response->is_success, 'Response Successful 2xx' );
+        is( $response->content_type, 'text/plain', 'Response Content-Type' );
+        is(
+            $response->header('X-Catalyst-Action'),
+            'action/relative/relative_visit_two',
+            'Test Action'
+        );
+        is(
+            $response->header('X-Test-Class'),
+            'TestApp::Controller::Action::Visit',
+            'Test Class'
+        );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        like(
+            $response->content,
+            qr/^bless\( .* 'Catalyst::Request' \)$/s,
+            'Content is a serialized Catalyst::Request'
+        );
+    }
+
+    # test class visit -- MUST FAIL!
+    {
+        ok(
+            my $response = request(
+                'http://localhost/action/visit/class_visit_test_action'),
+            'Request'
+        );
+        ok( !$response->is_success, 'Response Fails' );
+        is( $response->content,
+            q[FATAL ERROR: Couldn't visit("TestApp"): Action has no namespace: cannot visit() to a plain method or component, must be a :Action or some sort.],
+            "Cannot visit app namespace"
+        );
+    }
+
+    {
+        my @expected = qw[
+          TestApp::Controller::Action::Visit->begin
+          TestApp::Controller::Action::Visit->visit_chained
+          TestApp::Controller::Action::Chained->begin
+          TestApp::Controller::Action::Chained->foo
+          TestApp::Controller::Action::Chained::Foo->spoon
+          TestApp::Controller::Action::Chained->end
+          TestApp->end
+        ];
+
+        my $expected = join( ", ", @expected );
+
+        ok( my $response = request('http://localhost/action/visit/visit_chained'), 'visit to chained + subcontroller endpoint' );
+        is( $response->header('X-Catalyst-Executed'),
+            $expected, 'Executed actions' );
+        is( $response->content, '; 1', 'Content OK' );
+    }
+
+}
+
+
+
+sub _begin {
+    local $_ = shift;
+    s/->(.*)$/->begin/;
+    return $_;
+}
+

Modified: Catalyst-Runtime/5.70/trunk/t/unit_core_mvc.t
===================================================================
--- Catalyst-Runtime/5.70/trunk/t/unit_core_mvc.t	2009-01-12 05:08:43 UTC (rev 9073)
+++ Catalyst-Runtime/5.70/trunk/t/unit_core_mvc.t	2009-01-12 12:07:37 UTC (rev 9074)
@@ -1,4 +1,4 @@
-use Test::More tests => 44;
+use Test::More tests => 45;
 use strict;
 use warnings;
 
@@ -154,9 +154,14 @@
     *MyApp::Model::M::ACCEPT_CONTEXT = sub { my ($self, $c, @args) = @_; $args= \@args};
     *MyApp::View::V::ACCEPT_CONTEXT = sub { my ($self, $c, @args) = @_; $args= \@args};
 
+    # test accept-context with class rather than instance
     MyApp->model('M', qw/foo bar/);
     is_deeply($args, [qw/foo bar/], '$c->model args passed to ACCEPT_CONTEXT ok');
 
+
+    MyApp->model('M', qw/foo bar/);
+    is_deeply($args, [qw/foo bar/], '$c->model args passed to ACCEPT_CONTEXT ok');
+
     my $x = MyApp->view('V', qw/foo2 bar2/);
     is_deeply($args, [qw/foo2 bar2/], '$c->view args passed to ACCEPT_CONTEXT ok');
 




More information about the Catalyst-commits mailing list