X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?p=catagits%2FCatalyst-Runtime.git;a=blobdiff_plain;f=lib%2FCatalyst.pm;h=9935d18ff49e9d677c50ee8eab74c2326ed4b9e3;hp=aca49201cfe30c72d81b99f005cff2ce518e49ec;hb=2666dd3ba45edb0fa31508f67d94fe80072f94f6;hpb=b5ecfcf07b8ffe7e9984f0279c8781ce51c6ac6a diff --git a/lib/Catalyst.pm b/lib/Catalyst.pm index aca4920..9935d18 100644 --- a/lib/Catalyst.pm +++ b/lib/Catalyst.pm @@ -10,27 +10,17 @@ use Catalyst::Request; use Catalyst::Request::Upload; use Catalyst::Response; use Catalyst::Utils; +use File::stat; use NEXT; use Text::SimpleTable; use Path::Class; use Time::HiRes qw/gettimeofday tv_interval/; use URI; use Scalar::Util qw/weaken/; +use Tree::Simple qw/use_weak_refs/; +use Tree::Simple::Visitor::FindByUID; use attributes; -# For PAR -require Catalyst::Helper; -require Catalyst::PAR; -require Catalyst::Build; -require Catalyst::Test; - -require Catalyst::Engine::HTTP; -require Catalyst::Engine::CGI; - -require Catalyst::Controller; -require Catalyst::Model; -require Catalyst::View; - __PACKAGE__->mk_accessors( qw/counter request response state action stack namespace/ ); @@ -56,7 +46,7 @@ our $DETACH = "catalyst_detach\n"; require Module::Pluggable::Fast; # Helper script generation -our $CATALYST_SCRIPT_GEN = 18; +our $CATALYST_SCRIPT_GEN = 25; __PACKAGE__->mk_classdata($_) for qw/components arguments dispatcher engine log dispatcher_class @@ -67,7 +57,7 @@ __PACKAGE__->engine_class('Catalyst::Engine::CGI'); __PACKAGE__->request_class('Catalyst::Request'); __PACKAGE__->response_class('Catalyst::Response'); -our $VERSION = '5.57'; +our $VERSION = '5.62'; sub import { my ( $class, @arguments ) = @_; @@ -401,7 +391,7 @@ Gets a L instance by name. sub controller { my ( $c, $name ) = @_; my $controller = $c->comp("Controller::$name"); - return $controller if $controller; + return $controller if defined $controller; return $c->comp("C::$name"); } @@ -416,7 +406,7 @@ Gets a L instance by name. sub model { my ( $c, $name ) = @_; my $model = $c->comp("Model::$name"); - return $model if $model; + return $model if defined $model; return $c->comp("M::$name"); } @@ -431,7 +421,7 @@ Gets a L instance by name. sub view { my ( $c, $name ) = @_; my $view = $c->comp("View::$name"); - return $view if $view; + return $view if defined $view; return $c->comp("V::$name"); } @@ -604,7 +594,9 @@ EOF { no strict 'refs'; - @plugins = grep { /^Catalyst::Plugin/ } @{"$class\::ISA"}; + @plugins = + map { $_ . ' ' . ( $_->VERSION || '' ) } + grep { /^Catalyst::Plugin/ } @{"$class\::ISA"}; } if (@plugins) { @@ -687,8 +679,11 @@ sub uri_for { # join args with '/', or a blank string my $args = ( scalar @args ? '/' . join( '/', @args ) : '' ); - return URI->new_abs( URI->new_abs( "$path$args", "$basepath$namespace" ), - $base )->canonical; + $args =~ s/^\/// unless $path; + my $res = + URI->new_abs( URI->new_abs( "$path$args", "$basepath$namespace" ), $base ) + ->canonical; + $res; } =head2 $c->welcome_message @@ -791,7 +786,7 @@ sub welcome_message {

Welcome to the wonderful world of Catalyst. This MVC framework will make web development something you had - never expected it to be: Fun, rewarding and quick.

+ never expected it to be: Fun, rewarding, and quick.

What to do now?

That really depends on what you want to do. We do, however, provide you with a few starting points.

@@ -802,12 +797,12 @@ perldoc controllers, - models and - views, + models, and + views; they can save you a lot of work.

script/${prefix}_create.pl -help

Also, be sure to check out the vast and growing - collection of plugins for Catalyst on CPAN, + collection of plugins for Catalyst on CPAN; you are likely to find what you need there.

@@ -897,14 +892,8 @@ sub execute { $class = $c->components->{$class} || $class; $c->state(0); - my $callsub = - ( caller(0) )[0]->isa('Catalyst::Action') - ? ( caller(2) )[3] - : ( caller(1) )[3]; - - my $action = ''; if ( $c->debug ) { - $action = "$code"; + my $action = "$code"; $action = "/$action" unless $action =~ /\-\>/; $c->counter->{"$code"}++; @@ -915,9 +904,51 @@ sub execute { $c->state(0); return $c->state; } + + # determine if the call was the result of a forward + my $callsub_index = ( caller(0) )[0]->isa('Catalyst::Action') ? 2 : 1; + if ( ( caller($callsub_index) )[3] =~ /^NEXT/ ) { + # work around NEXT if execute was extended by a plugin + $callsub_index += 3; + } + my $callsub = ( caller($callsub_index) )[3]; $action = "-> $action" if $callsub =~ /forward$/; + + my $node = Tree::Simple->new( { + action => $action, + elapsed => undef, # to be filled in later + } ); + $node->setUID( "$code" . $c->counter->{"$code"} ); + + unless ( ( $code->name =~ /^_.*/ ) + && ( !$c->config->{show_internal_actions} ) ) + { + # is this a root-level call or a forwarded call? + if ( $callsub =~ /forward$/ ) { + + # forward, locate the caller + if ( my $parent = $c->stack->[-1] ) { + my $visitor = Tree::Simple::Visitor::FindByUID->new; + $visitor->searchForUID( + "$parent" . $c->counter->{"$parent"} ); + $c->{stats}->accept( $visitor ); + if ( my $result = $visitor->getResult ) { + $result->addChild( $node ); + } + } + else { + # forward with no caller may come from a plugin + $c->{stats}->addChild( $node ); + } + } + else { + # root-level call + $c->{stats}->addChild( $node ); + } + } } + push( @{ $c->stack }, $code ); my $elapsed = 0; my $start = 0; @@ -929,18 +960,34 @@ sub execute { unless ( ( $code->name =~ /^_.*/ ) && ( !$c->config->{show_internal_actions} ) ) { - push @{ $c->{stats} }, [ $action, sprintf( '%fs', $elapsed ) ]; + # FindByUID uses an internal die, so we save the existing error + my $error = $@; + + # locate the node in the tree and update the elapsed time + my $visitor = Tree::Simple::Visitor::FindByUID->new; + $visitor->searchForUID( "$code" . $c->counter->{"$code"} ); + $c->{stats}->accept( $visitor ); + if ( my $result = $visitor->getResult ) { + my $value = $result->getNodeValue; + $value->{elapsed} = sprintf( '%fs', $elapsed ); + $result->setNodeValue( $value ); + } + + # restore error + $@ = $error || undef; } } + my $last = ${ $c->stack }[-1]; pop( @{ $c->stack } ); if ( my $error = $@ ) { - if ( $error eq $DETACH ) { die $DETACH if $c->depth > 1 } else { unless ( ref $error ) { chomp $error; - $error = qq/Caught exception "$error"/; + my $class = $last->class; + my $name = $last->name; + $error = qq/Caught exception in $class->$name "$error"/; } $c->error($error); $c->state(0); @@ -1025,7 +1072,19 @@ sub finalize_headers { # Content-Length if ( $c->response->body && !$c->response->content_length ) { - $c->response->content_length( bytes::length( $c->response->body ) ); + + # get the length from a filehandle + if ( ref $c->response->body && $c->response->body->can('read') ) { + if ( my $stat = stat $c->response->body ) { + $c->response->content_length( $stat->size ); + } + else { + $c->log->warn('Serving filehandle without a content-length'); + } + } + else { + $c->response->content_length( bytes::length( $c->response->body ) ); + } } # Errors @@ -1091,11 +1150,11 @@ sub handle_request { # Always expect worst case! my $status = -1; eval { - my @stats = (); + my $stats = ( $class->debug ) ? Tree::Simple->new : q{}; my $handler = sub { my $c = $class->prepare(@arguments); - $c->{stats} = \@stats; + $c->{stats} = $stats; $c->dispatch; return $c->finalize; }; @@ -1108,8 +1167,16 @@ sub handle_request { my $av = sprintf '%.3f', ( $elapsed == 0 ? '??' : ( 1 / $elapsed ) ); my $t = Text::SimpleTable->new( [ 64, 'Action' ], [ 9, 'Time' ] ); - - for my $stat (@stats) { $t->row( $stat->[0], $stat->[1] ) } + + $stats->traverse( sub { + my $action = shift; + my $stat = $action->getNodeValue; + $t->row( + ( q{ } x $action->getDepth ) . $stat->{action}, + $stat->{elapsed} || '??' + ); + } ); + $class->log->info( "Request took ${elapsed}s ($av/s)\n" . $t->draw ); } @@ -1551,7 +1618,7 @@ sub setup_engine { $engine = 'Catalyst::Engine::' . $ENV{ uc($class) . '_ENGINE' }; } - if ( !$engine && $ENV{MOD_PERL} ) { + if ( $ENV{MOD_PERL} ) { # create the apache method { @@ -1567,21 +1634,25 @@ sub setup_engine { if ( $software eq 'mod_perl' ) { - if ( $version >= 1.99922 ) { - $engine = 'Catalyst::Engine::Apache2::MP20'; - } - - elsif ( $version >= 1.9901 ) { - $engine = 'Catalyst::Engine::Apache2::MP19'; - } - - elsif ( $version >= 1.24 ) { - $engine = 'Catalyst::Engine::Apache::MP13'; - } + if ( !$engine ) { + + if ( $version >= 1.99922 ) { + $engine = 'Catalyst::Engine::Apache2::MP20'; + } + + elsif ( $version >= 1.9901 ) { + $engine = 'Catalyst::Engine::Apache2::MP19'; + } + + elsif ( $version >= 1.24 ) { + $engine = 'Catalyst::Engine::Apache::MP13'; + } + + else { + Catalyst::Exception->throw( message => + qq/Unsupported mod_perl version: $ENV{MOD_PERL}/ ); + } - else { - Catalyst::Exception->throw( message => - qq/Unsupported mod_perl version: $ENV{MOD_PERL}/ ); } # install the correct mod_perl handler @@ -1855,6 +1926,8 @@ Wiki: =head1 SEE ALSO +=head2 L - All you need to start with Catalyst + =head2 L - The Catalyst Manual =head2 L, L - Base classes for components