From: Marcus Ramberg Date: Wed, 25 Jun 2008 20:15:23 +0000 (+0000) Subject: remove a confusing and duplicate bit of documentation X-Git-Tag: 5.7099_04~59 X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Runtime.git;a=commitdiff_plain;h=9bfaf006d5c1cde4938a32e97e1cc58f1c5befa4 remove a confusing and duplicate bit of documentation Fix for LocalRegex when used in the Root controller Update HTTP::Body dep so that the uploadtmp config value will work (RT #22540) remove 0-length query string components so warnings aren't thrown (RT #36428) merge compres branch r21359@Command-Central (orig r7936): bricas | 2008-06-20 20:14:11 +0200 r21361@Command-Central (orig r7938): bricas | 2008-06-23 15:38:24 +0200 r21418@Command-Central (orig r7995): bricas | 2008-06-24 00:01:06 +0200 r21419@Command-Central (orig r7996): bricas | 2008-06-24 02:14:21 +0200 r21423@Command-Central (orig r8000): marcus | 2008-06-25 21:08:09 +0200 --- diff --git a/lib/Catalyst.pm b/lib/Catalyst.pm index a75f43b..22deaf1 100644 --- a/lib/Catalyst.pm +++ b/lib/Catalyst.pm @@ -49,6 +49,7 @@ our $COUNT = 1; 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 @@ -327,6 +328,20 @@ When called with no arguments it escapes the processing chain entirely. sub detach { my $c = shift; $c->dispatcher->detach( $c, @_ ) } +=head2 $c->go( $action [, \@arguments ] ) + +=head2 $c->go( $class, $method, [, \@arguments ] ) + +Almost the same as C, but does a full dispatch, instead of just +calling the new C<$action> / C<$class-E$method>. This means that C, +C and the method you go to is called, just like a new request. + +C<$c-Estash> is kept unchanged. + +=cut + +sub go { my $c = shift; $c->dispatcher->go( $c, @_ ) } + =head2 $c->response =head2 $c->res @@ -1260,7 +1275,12 @@ sub execute { my $last = pop( @{ $c->stack } ); if ( my $error = $@ ) { - if ( !ref($error) and $error eq $DETACH ) { die $DETACH if $c->depth > 1 } + 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'; diff --git a/lib/Catalyst/Dispatcher.pm b/lib/Catalyst/Dispatcher.pm index cd1f57e..8afe7fe 100644 --- a/lib/Catalyst/Dispatcher.pm +++ b/lib/Catalyst/Dispatcher.pm @@ -127,17 +127,15 @@ sub dispatch { } } -=head2 $self->forward( $c, $command [, \@arguments ] ) - -Documented in L - -=cut +# $self->_command2action( $c, $command [, \@arguments ] ) +# Search for an action, from the command and returns C<($action, $args)> on +# success. Returns C<(0)> on error. -sub forward { +sub _command2action { my ( $self, $c, $command, @extra_params ) = @_; unless ($command) { - $c->log->debug('Nothing to forward to') if $c->debug; + $c->log->debug('Nothing to go to') if $c->debug; return 0; } @@ -146,34 +144,76 @@ sub forward { if ( ref( $extra_params[-1] ) eq 'ARRAY' ) { @args = @{ pop @extra_params } } else { - # this is a copy, it may take some abuse from ->_invoke_as_path if the path had trailing parts + # this is a copy, it may take some abuse from + # ->_invoke_as_path if the path had trailing parts @args = @{ $c->request->arguments }; } my $action; - # forward to a string path ("/foo/bar/gorch") or action object which stringifies to that + # go to a string path ("/foo/bar/gorch") + # or action object which stringifies to that $action = $self->_invoke_as_path( $c, "$command", \@args ); - # forward to a component ( "MyApp::*::Foo" or $c->component("...") - a path or an object) + # go to a component ( "MyApp::*::Foo" or $c->component("...") + # - a path or an object) unless ($action) { my $method = @extra_params ? $extra_params[0] : "process"; $action = $self->_invoke_as_component( $c, $command, $method ); } + return $action, \@args; +} + +=head2 $self->go( $c, $command [, \@arguments ] ) + +Documented in L + +=cut + +sub go { + my $self = shift; + my ( $c, $command ) = @_; + my ( $action, $args ) = $self->_command2action(@_); unless ($action) { my $error = - qq/Couldn't forward to command "$command": / + qq/Couldn't go to command "$command": / . qq/Invalid action or component./; $c->error($error); $c->log->debug($error) if $c->debug; return 0; } - #push @$args, @_; + local $c->request->{arguments} = $args; + $c->namespace($action->namespace); + $c->action($action); + $self->dispatch($c); + + die $Catalyst::GO; +} + +=head2 $self->forward( $c, $command [, \@arguments ] ) + +Documented in L + +=cut + +sub forward { + my $self = shift; + my ( $c, $command ) = @_; + my ( $action, $args ) = $self->_command2action(@_); + + unless ($action) { + my $error = + qq/Couldn't forward to command "$command": / + . qq/Invalid action or component./; + $c->error($error); + $c->log->debug($error) if $c->debug; + return 0; + } - local $c->request->{arguments} = \@args; + local $c->request->{arguments} = $args; $action->dispatch( $c ); return $c->state; diff --git a/t/lib/TestApp.pm b/t/lib/TestApp.pm index f18228c..83eb942 100644 --- a/t/lib/TestApp.pm +++ b/t/lib/TestApp.pm @@ -72,6 +72,11 @@ sub class_forward_test_method :Private { $c->response->headers->header( 'X-Class-Forward-Test-Method' => 1 ); } +sub class_go_test_method :Private { + my ( $self, $c ) = @_; + $c->response->headers->header( 'X-Class-Go-Test-Method' => 1 ); +} + sub loop_test : Local { my ( $self, $c ) = @_; @@ -98,4 +103,4 @@ use strict; use base qw/Catalyst::Base Class::Data::Inheritable/; -1; \ No newline at end of file +1; diff --git a/t/lib/TestApp/Controller/Action/Go.pm b/t/lib/TestApp/Controller/Action/Go.pm new file mode 100644 index 0000000..9ad2ae2 --- /dev/null +++ b/t/lib/TestApp/Controller/Action/Go.pm @@ -0,0 +1,85 @@ +package TestApp::Controller::Action::Go; + +use strict; +use base 'TestApp::Controller::Action'; + +sub one : Local { + my ( $self, $c ) = @_; + $c->go('two'); +} + +sub two : Private { + my ( $self, $c ) = @_; + $c->go('three'); +} + +sub three : Local { + my ( $self, $c ) = @_; + $c->go( $self, 'four' ); +} + +sub four : Private { + my ( $self, $c ) = @_; + $c->go('/action/go/five'); +} + +sub five : Local { + my ( $self, $c ) = @_; + $c->go('View::Dump::Request'); +} + +sub inheritance : Local { + my ( $self, $c ) = @_; + $c->go('/action/inheritance/a/b/default'); +} + +sub global : Local { + my ( $self, $c ) = @_; + $c->go('/global_action'); +} + +sub with_args : Local { + my ( $self, $c, $arg ) = @_; + $c->go( 'args', [$arg] ); +} + +sub with_method_and_args : Local { + my ( $self, $c, $arg ) = @_; + $c->go( qw/TestApp::Controller::Action::Go 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 go_die : Local { + my ( $self, $c, $val ) = @_; + eval { $c->go( 'args', [qq/new/] ) }; + $c->res->body( $@ ? $@ : "go() did not die" ); + die $Catalyst::GO; +} + +sub args_embed_relative : Local { + my ( $self, $c ) = @_; + $c->go('embed/ok'); +} + +sub args_embed_absolute : Local { + my ( $self, $c ) = @_; + $c->go('/action/go/embed/ok'); +} + +sub embed : Local { + my ( $self, $c, $ok ) = @_; + $ok ||= 'not ok'; + $c->res->body($ok); +} + +sub class_go_test_action : Local { + my ( $self, $c ) = @_; + $c->go(qw/TestApp class_go_test_method/); +} + +1; diff --git a/t/lib/TestApp/Controller/Action/TestRelative.pm b/t/lib/TestApp/Controller/Action/TestRelative.pm index 951345e..2efe3f1 100644 --- a/t/lib/TestApp/Controller/Action/TestRelative.pm +++ b/t/lib/TestApp/Controller/Action/TestRelative.pm @@ -17,4 +17,13 @@ sub relative_two : Local { $c->forward( 'TestApp::Controller::Action::Forward', 'one' ); } +sub relative_go : Local { + my ( $self, $c ) = @_; + $c->go('/action/go/one'); +} + +sub relative_go_two : Local { + my ( $self, $c ) = @_; + $c->go( 'TestApp::Controller::Action::Go', 'one' ); +} 1; diff --git a/t/live_component_controller_action_go.t b/t/live_component_controller_action_go.t new file mode 100644 index 0000000..ac0112d --- /dev/null +++ b/t/live_component_controller_action_go.t @@ -0,0 +1,233 @@ +#!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 => 47 * $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 { + { + 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 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' ); + + # Test go to chain of actions. + ok( $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/with_args/old'), + 'Request with args' + ); + ok( $response->is_success, 'Response Successful 2xx' ); + is( $response->content, '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' ); + } + + # 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' ); + } + + { + ok( + my $response = + request('http://localhost/action/go/args_embed_absolute'), + 'Request' + ); + ok( $response->is_success, 'Response Successful 2xx' ); + is( $response->content, 'ok' ); + } + { + 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 + { + ok( + my $response = request( + 'http://localhost/action/go/class_go_test_action'), + 'Request' + ); + ok( $response->is_success, 'Response Successful 2xx' ); + is( $response->header('X-Class-Go-Test-Method'), 1, + 'Test Method' ); + } + +} + +sub _begin { + local $_ = shift; + s/->(.*)$/->begin/; + return $_; +} +