X-Git-Url: http://git.shadowcat.co.uk/gitweb/gitweb.cgi?a=blobdiff_plain;f=lib%2FCatalyst.pm;h=b742de064eefb8e427a0dfac49e65623305348f8;hb=f0e9921a9b807f883f868d95ec2ea2975669305c;hp=25f4caf91b9345e65206a5269c9e8f70a0fcb339;hpb=1bebcac61bb8a8decc34b48cf823b5004c32fd9c;p=catagits%2FCatalyst-Runtime.git
diff --git a/lib/Catalyst.pm b/lib/Catalyst.pm
index 25f4caf..b742de0 100644
--- a/lib/Catalyst.pm
+++ b/lib/Catalyst.pm
@@ -78,7 +78,9 @@ __PACKAGE__->stats_class('Catalyst::Stats');
# Remember to update this in Catalyst::Runtime as well!
-our $VERSION = '5.80020';
+our $VERSION = '5.80021';
+our $PRETTY_VERSION = $VERSION;
+
$VERSION = eval $VERSION;
sub import {
@@ -638,7 +640,13 @@ If you want to search for controllers, pass in a regexp as the argument.
sub controller {
my ( $c, $name, @args ) = @_;
+ my $appclass = ref($c) || $c;
if( $name ) {
+ unless ( ref($name) ) { # Direct component hash lookup to avoid costly regexps
+ my $comps = $c->components;
+ my $check = $appclass."::Controller::".$name;
+ return $c->_filter_component( $comps->{$check}, @args ) if exists $comps->{$check};
+ }
my @result = $c->_comp_search_prefixes( $name, qw/Controller C/ );
return map { $c->_filter_component( $_, @args ) } @result if ref $name;
return $c->_filter_component( $result[ 0 ], @args );
@@ -672,6 +680,11 @@ sub model {
my ( $c, $name, @args ) = @_;
my $appclass = ref($c) || $c;
if( $name ) {
+ unless ( ref($name) ) { # Direct component hash lookup to avoid costly regexps
+ my $comps = $c->components;
+ my $check = $appclass."::Model::".$name;
+ return $c->_filter_component( $comps->{$check}, @args ) if exists $comps->{$check};
+ }
my @result = $c->_comp_search_prefixes( $name, qw/Model M/ );
return map { $c->_filter_component( $_, @args ) } @result if ref $name;
return $c->_filter_component( $result[ 0 ], @args );
@@ -726,6 +739,11 @@ sub view {
my $appclass = ref($c) || $c;
if( $name ) {
+ unless ( ref($name) ) { # Direct component hash lookup to avoid costly regexps
+ my $comps = $c->components;
+ my $check = $appclass."::View::".$name;
+ return $c->_filter_component( $comps->{$check}, @args ) if exists $comps->{$check};
+ }
my @result = $c->_comp_search_prefixes( $name, qw/View V/ );
return map { $c->_filter_component( $_, @args ) } @result if ref $name;
return $c->_filter_component( $result[ 0 ], @args );
@@ -1144,7 +1162,7 @@ EOF
if ( $class->debug ) {
my $name = $class->config->{name} || 'Application';
- $class->log->info("$name powered by Catalyst $Catalyst::VERSION");
+ $class->log->info("$name powered by Catalyst $Catalyst::PRETTY_VERSION");
}
# Make sure that the application class becomes immutable at this point,
@@ -1182,23 +1200,20 @@ EOF
return 1; # Explicit return true as people have __PACKAGE__->setup as the last thing in their class. HATE.
}
-
=head2 $app->setup_finalize
-A hook to attach modifiers to.
-Using C<< after setup => sub{}; >> doesn't work, because of quirky things done for plugin setup.
-Also better than C< setup_finished(); >, as that is a getter method.
-
- sub setup_finalize {
-
- my $app = shift;
+A hook to attach modifiers to. This method does not do anything except set the
+C accessor.
- ## do stuff, i.e., determine a primary key column for sessions stored in a DB
+Applying method modifiers to the C method doesn't work, because of quirky thingsdone for plugin setup.
- $app->next::method(@_);
+Example:
+ after setup_finalize => sub {
+ my $app = shift;
- }
+ ## do stuff here..
+ };
=cut
@@ -1265,6 +1280,9 @@ sub uri_for {
( scalar @args && ref $args[$#args] eq 'HASH' ? pop @args : {} );
carp "uri_for called with undef argument" if grep { ! defined $_ } @args;
+ foreach my $arg (@args) {
+ utf8::encode($arg) if utf8::is_utf8($arg);
+ }
s/([^$URI::uric])/$URI::Escape::escapes{$1}/go for @args;
if (blessed $path) { # Action object only.
s|/|%2F|g for @args;
@@ -1275,6 +1293,12 @@ sub uri_for {
( scalar @args && ref $args[0] eq 'ARRAY'
? @{ shift(@args) }
: ()) ];
+
+ foreach my $capture (@$captures) {
+ utf8::encode($capture) if utf8::is_utf8($capture);
+ $capture =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;
+ }
+
my $action = $path;
$path = $c->dispatcher->uri_for_action($action, $captures);
if (not defined $path) {
@@ -1285,6 +1309,8 @@ sub uri_for {
$path = '/' if $path eq '';
}
+ undef($path) if (defined $path && $path eq '');
+
unshift(@args, $path);
unless (defined $path && $path =~ s!^/!!) { # in-place strip
@@ -1490,7 +1516,7 @@ sub welcome_message {
models, and
views;
they can save you a lot of work.
- script/${prefix}_create.pl -help
+ script/${prefix}_create.pl --help
Also, be sure to check out the vast and growing
collection of plugins for Catalyst on CPAN;
you are likely to find what you need there.
@@ -1733,6 +1759,8 @@ sub finalize {
$c->finalize_body;
}
+ $c->log_response;
+
if ($c->use_stats) {
my $elapsed = sprintf '%f', $c->stats->elapsed;
my $av = $elapsed == 0 ? '??' : sprintf '%.3f', 1 / $elapsed;
@@ -1957,8 +1985,7 @@ sub prepare {
$path = '/' unless length $path;
my $address = $c->req->address || '';
- $c->log->debug(qq/"$method" request for "$path" from "$address"/)
- if $c->debug;
+ $c->log_request;
$c->prepare_action;
@@ -1988,17 +2015,6 @@ sub prepare_body {
$c->engine->prepare_body( $c, @_ );
$c->prepare_parameters;
$c->prepare_uploads;
-
- if ( $c->debug && keys %{ $c->req->body_parameters } ) {
- my $t = Text::SimpleTable->new( [ 35, 'Parameter' ], [ 36, 'Value' ] );
- for my $key ( sort keys %{ $c->req->body_parameters } ) {
- my $param = $c->req->body_parameters->{$key};
- my $value = defined($param) ? $param : '';
- $t->row( $key,
- ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
- }
- $c->log->debug( "Body Parameters are:\n" . $t->draw );
- }
}
=head2 $c->prepare_body_chunk( $chunk )
@@ -2082,55 +2098,156 @@ sub prepare_query_parameters {
my $c = shift;
$c->engine->prepare_query_parameters( $c, @_ );
+}
- if ( $c->debug && keys %{ $c->request->query_parameters } ) {
- my $t = Text::SimpleTable->new( [ 35, 'Parameter' ], [ 36, 'Value' ] );
- for my $key ( sort keys %{ $c->req->query_parameters } ) {
- my $param = $c->req->query_parameters->{$key};
- my $value = defined($param) ? $param : '';
- $t->row( $key,
- ref $value eq 'ARRAY' ? ( join ', ', @$value ) : $value );
- }
- $c->log->debug( "Query Parameters are:\n" . $t->draw );
+=head2 $c->log_request
+
+Writes information about the request to the debug logs. This includes:
+
+=over 4
+
+=item * Request method, path, and remote IP address
+
+=item * Query keywords (see L)
+
+=item * Request parameters
+
+=item * File uploads
+
+=back
+
+=cut
+
+sub log_request {
+ my $c = shift;
+
+ return unless $c->debug;
+
+ my($dump) = grep {$_->[0] eq 'Request' } $c->dump_these;
+ my $request = $dump->[1];
+
+ my ( $method, $path, $address ) = ( $request->method, $request->path, $request->address );
+ $method ||= '';
+ $path = '/' unless length $path;
+ $address ||= '';
+ $c->log->debug(qq/"$method" request for "$path" from "$address"/);
+
+ $c->log_request_headers($request->headers);
+
+ if ( my $keywords = $request->query_keywords ) {
+ $c->log->debug("Query keywords are: $keywords");
}
+
+ $c->log_request_parameters( query => $request->query_parameters, body => $request->body_parameters );
+
+ $c->log_request_uploads($request);
}
-=head2 $c->prepare_read
+=head2 $c->log_response
-Prepares the input for reading.
+Writes information about the response to the debug logs by calling
+C<< $c->log_response_status_line >> and C<< $c->log_response_headers >>.
=cut
-sub prepare_read { my $c = shift; $c->engine->prepare_read( $c, @_ ) }
+sub log_response {
+ my $c = shift;
-=head2 $c->prepare_request
+ return unless $c->debug;
-Prepares the engine request.
+ my($dump) = grep {$_->[0] eq 'Response' } $c->dump_these;
+ my $response = $dump->[1];
+
+ $c->log_response_status_line($response);
+ $c->log_response_headers($response->headers);
+}
+
+=head2 $c->log_response_status_line($response)
+
+Writes one line of information about the response to the debug logs. This includes:
+
+=over 4
+
+=item * Response status code
+
+=item * Content-Type header (if present)
+
+=item * Content-Length header (if present)
+
+=back
=cut
-sub prepare_request { my $c = shift; $c->engine->prepare_request( $c, @_ ) }
+sub log_response_status_line {
+ my ($c, $response) = @_;
-=head2 $c->prepare_uploads
+ $c->log->debug(
+ sprintf(
+ 'Response Code: %s; Content-Type: %s; Content-Length: %s',
+ $response->status || 'unknown',
+ $response->headers->header('Content-Type') || 'unknown',
+ $response->headers->header('Content-Length') || 'unknown'
+ )
+ );
+}
-Prepares uploads.
+=head2 $c->log_response_headers($headers);
+
+Hook method which can be wrapped by plugins to log the responseheaders.
+No-op in the default implementation.
=cut
-sub prepare_uploads {
- my $c = shift;
+sub log_response_headers {}
- $c->engine->prepare_uploads( $c, @_ );
+=head2 $c->log_request_parameters( query => {}, body => {} )
+
+Logs request parameters to debug logs
+
+=cut
+
+sub log_request_parameters {
+ my $c = shift;
+ my %all_params = @_;
+
+ return unless $c->debug;
+
+ my $column_width = Catalyst::Utils::term_width() - 44;
+ foreach my $type (qw(query body)) {
+ my $params = $all_params{$type};
+ 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 );
+ }
+ $c->log->debug( ucfirst($type) . " Parameters are:\n" . $t->draw );
+ }
+}
+
+=head2 $c->log_request_uploads
+
+Logs file uploads included in the request to the debug logs.
+The parameter name, filename, file type, and file size are all included in
+the debug logs.
- if ( $c->debug && keys %{ $c->request->uploads } ) {
+=cut
+
+sub log_request_uploads {
+ my $c = shift;
+ my $request = shift;
+ return unless $c->debug;
+ my $uploads = $request->uploads;
+ if ( keys %$uploads ) {
my $t = Text::SimpleTable->new(
[ 12, 'Parameter' ],
[ 26, 'Filename' ],
[ 18, 'Type' ],
[ 9, 'Size' ]
);
- for my $key ( sort keys %{ $c->request->uploads } ) {
- my $upload = $c->request->uploads->{$key};
+ for my $key ( sort keys %$uploads ) {
+ my $upload = $uploads->{$key};
for my $u ( ref $upload eq 'ARRAY' ? @{$upload} : ($upload) ) {
$t->row( $key, $u->filename, $u->type, $u->size );
}
@@ -2139,6 +2256,68 @@ sub prepare_uploads {
}
}
+=head2 $c->log_request_headers($headers);
+
+Hook method which can be wrapped by plugins to log the request headers.
+No-op in the default implementation.
+
+=cut
+
+sub log_request_headers {}
+
+=head2 $c->log_headers($type => $headers)
+
+Logs L (either request or response) to the debug logs.
+
+=cut
+
+sub log_headers {
+ my $c = shift;
+ my $type = shift;
+ my $headers = shift; # an HTTP::Headers instance
+
+ return unless $c->debug;
+
+ my $column_width = Catalyst::Utils::term_width() - 28;
+ my $t = Text::SimpleTable->new( [ 15, 'Header Name' ], [ $column_width, 'Value' ] );
+ $headers->scan(
+ sub {
+ my ( $name, $value ) = @_;
+ $t->row( $name, $value );
+ }
+ );
+ $c->log->debug( ucfirst($type) . " Headers:\n" . $t->draw );
+}
+
+
+=head2 $c->prepare_read
+
+Prepares the input for reading.
+
+=cut
+
+sub prepare_read { my $c = shift; $c->engine->prepare_read( $c, @_ ) }
+
+=head2 $c->prepare_request
+
+Prepares the engine request.
+
+=cut
+
+sub prepare_request { my $c = shift; $c->engine->prepare_request( $c, @_ ) }
+
+=head2 $c->prepare_uploads
+
+Prepares uploads.
+
+=cut
+
+sub prepare_uploads {
+ my $c = shift;
+
+ $c->engine->prepare_uploads( $c, @_ );
+}
+
=head2 $c->prepare_write
Prepares the output for writing.
@@ -2992,6 +3171,8 @@ Robert Sedlacek C<< >>
sky: Arthur Bergman
+szbalint: Balint Szilakszi
+
t0m: Tomas Doran
Ulf Edvinsson