prep release
[catagits/Catalyst-Runtime.git] / lib / Catalyst.pm
index 574ee01..1808a4d 100644 (file)
@@ -81,6 +81,8 @@ sub _build_request_constructor_args {
 
 sub composed_request_class {
   my $class = shift;
+  return $class->_composed_request_class if $class->_composed_request_class;
+
   my @traits = (@{$class->request_class_traits||[]}, @{$class->config->{request_class_traits}||[]});
 
   # For each trait listed, figure out what the namespace is.  First we try the $trait
@@ -92,8 +94,14 @@ sub composed_request_class {
     Class::Load::load_first_existing_class($_, $class.'::'.$trait_ns.'::'. $_, 'Catalyst::'.$trait_ns.'::'.$_)
   } @traits;
 
-  return $class->_composed_request_class ||
-    $class->_composed_request_class(Moose::Util::with_traits($class->request_class, @normalized_traits));
+  if ($class->debug && scalar(@normalized_traits)) {
+      my $column_width = Catalyst::Utils::term_width() - 6;
+      my $t = Text::SimpleTable->new($column_width);
+      $t->row($_) for @normalized_traits;
+      $class->log->debug( "Composed Request Class Traits:\n" . $t->draw . "\n" );
+  }
+
+  return $class->_composed_request_class(Moose::Util::with_traits($class->request_class, @normalized_traits));
 }
 
 has response => (
@@ -115,6 +123,8 @@ sub _build_response_constructor_args {
 
 sub composed_response_class {
   my $class = shift;
+  return $class->_composed_response_class if $class->_composed_response_class;
+  
   my @traits = (@{$class->response_class_traits||[]}, @{$class->config->{response_class_traits}||[]});
 
   my $trait_ns = 'TraitFor::Response';
@@ -122,8 +132,14 @@ sub composed_response_class {
     Class::Load::load_first_existing_class($_, $class.'::'.$trait_ns.'::'. $_, 'Catalyst::'.$trait_ns.'::'.$_)
   } @traits;
 
-  return $class->_composed_response_class ||
-    $class->_composed_response_class(Moose::Util::with_traits($class->response_class, @normalized_traits));
+  if ($class->debug && scalar(@normalized_traits)) {
+      my $column_width = Catalyst::Utils::term_width() - 6;
+      my $t = Text::SimpleTable->new($column_width);
+      $t->row($_) for @normalized_traits;
+      $class->log->debug( "Composed Response Class Traits:\n" . $t->draw . "\n" );
+  }
+
+  return $class->_composed_response_class(Moose::Util::with_traits($class->response_class, @normalized_traits));
 }
 
 has namespace => (is => 'rw');
@@ -166,6 +182,8 @@ __PACKAGE__->stats_class('Catalyst::Stats');
 
 sub composed_stats_class {
   my $class = shift;
+  return $class->_composed_stats_class if $class->_composed_stats_class;
+
   my @traits = (@{$class->stats_class_traits||[]}, @{$class->config->{stats_class_traits}||[]});
 
   my $trait_ns = 'TraitFor::Stats';
@@ -173,14 +191,20 @@ sub composed_stats_class {
     Class::Load::load_first_existing_class($_, $class.'::'.$trait_ns.'::'. $_, 'Catalyst::'.$trait_ns.'::'.$_)
   } @traits;
 
-  return $class->_composed_stats_class ||
-    $class->_composed_stats_class(Moose::Util::with_traits($class->stats_class, @normalized_traits));
+  if ($class->debug && scalar(@normalized_traits)) {
+      my $column_width = Catalyst::Utils::term_width() - 6;
+      my $t = Text::SimpleTable->new($column_width);
+      $t->row($_) for @normalized_traits;
+      $class->log->debug( "Composed Stats Class Traits:\n" . $t->draw . "\n" );
+  }
+
+  return $class->_composed_stats_class(Moose::Util::with_traits($class->stats_class, @normalized_traits));
 }
 
 __PACKAGE__->_encode_check(Encode::FB_CROAK | Encode::LEAVE_SRC);
 
 # Remember to update this in Catalyst::Runtime as well!
-our $VERSION = '5.90098';
+our $VERSION = '5.90110';
 $VERSION = eval $VERSION if $VERSION =~ /_/; # numify for warning-free dev releases
 
 sub import {
@@ -606,13 +630,17 @@ sub error {
     return $c->{error} || [];
 }
 
-
 =head2 $c->state
 
 Contains the return value of the last executed action.
 Note that << $c->state >> operates in a scalar context which means that all
 values it returns are scalar.
 
+Please note that if an action throws an exception, the value of state
+should no longer be considered the return if the last action.  It is generally
+going to be 0, which indicates an error state.  Examine $c->error for error
+details.
+
 =head2 $c->clear_errors
 
 Clear errors.  You probably don't want to clear the errors unless you are
@@ -807,6 +835,11 @@ sub controller {
             my $comps = $c->components;
             my $check = $appclass."::Controller::".$name;
             return $c->_filter_component( $comps->{$check}, @args ) if exists $comps->{$check};
+            foreach my $path (@{$appclass->config->{ setup_components }->{ search_extra }}) {
+                next unless $path =~ /.*::Controller/;
+                $check = $path."::".$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;
@@ -846,6 +879,11 @@ sub model {
             my $comps = $c->components;
             my $check = $appclass."::Model::".$name;
             return $c->_filter_component( $comps->{$check}, @args ) if exists $comps->{$check};
+            foreach my $path (@{$appclass->config->{ setup_components }->{ search_extra }}) {
+                next unless $path =~ /.*::Model/;
+                $check = $path."::".$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;
@@ -910,6 +948,11 @@ sub view {
             else {
                 $c->log->warn( "Attempted to use view '$check', but does not exist" );
             }
+            foreach my $path (@{$appclass->config->{ setup_components }->{ search_extra }}) {
+                next unless $path =~ /.*::View/;
+                $check = $path."::".$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;
@@ -1454,6 +1497,11 @@ EOF
         $class->log->warn("This setting is deprecated and planned to be removed in Catalyst 5.81.");
     }
 
+    # call these so we pre setup the composed classes
+    $class->composed_request_class;
+    $class->composed_response_class;
+    $class->composed_stats_class;
+
     $class->setup_finalize;
 
     # Flush the log for good measure (in case something turned off 'autoflush' early)
@@ -1560,7 +1608,7 @@ sub uri_for {
     my $fragment =  ((scalar(@args) && ref($args[-1]) eq 'SCALAR') ? pop @args : undef );
 
     unless(blessed $path) {
-      if ($path =~ s/#(.+)$//)  {
+      if (defined($path) and $path =~ s/#(.+)$//)  {
         if(defined($1) and $fragment) {
           carp "Abiguious fragment declaration: You cannot define a fragment in '$path' and as an argument '$fragment'";
         }
@@ -1986,7 +2034,7 @@ via $c->error.
 sub execute {
     my ( $c, $class, $code ) = @_;
     $class = $c->component($class) || $class;
-    $c->state(0);
+    #$c->state(0);
 
     if ( $c->depth >= $RECURSION ) {
         my $action = $code->reverse();
@@ -2038,7 +2086,7 @@ sub execute {
             }
             $c->error($error);
         }
-        $c->state(0);
+        #$c->state(0);
     }
     return $c->state;
 }
@@ -2385,7 +2433,6 @@ sub prepare {
     my $c = $class->context_class->new({ $uploadtmp ? (_uploadtmp => $uploadtmp) : ()});
 
     $c->response->_context($c);
-
     $c->stats($class->stats_class->new)->enable($c->use_stats);
 
     if ( $c->debug || $c->config->{enable_catalyst_header} ) {
@@ -2419,9 +2466,6 @@ sub prepare {
     # VERY ugly and probably shouldn't rely on ->finalize actually working
     catch {
         # failed prepare is always due to an invalid request, right?
-        $c->response->status(400);
-        $c->response->content_type('text/plain');
-        $c->response->body('Bad Request');
         # Note we call finalize and then die here, which escapes
         # finalize being called in the enclosing block..
         # It in fact couldn't be called, as we don't return $c..
@@ -2429,8 +2473,20 @@ sub prepare {
         # breaking compat for people doing crazy things (we should set
         # the 400 and just return the ctx here IMO, letting finalize get called
         # above...
-        $c->finalize;
-        die $_;
+        if ( $c->_handle_http_exception($_) ) {
+            foreach my $err (@{$c->error}) {
+                $c->log->error($err);
+            }
+            $c->clear_errors;
+            $c->log->_flush if $c->log->can('_flush');
+            $_->can('rethrow') ? $_->rethrow : croak $_;
+        } else {
+            $c->response->status(400);
+            $c->response->content_type('text/plain');
+            $c->response->body('Bad Request');
+            $c->finalize;
+            die $_;
+        }
     };
 
     $c->log_request;
@@ -2812,7 +2868,7 @@ We try each possible role in turn (and throw an error if none load)
     MyApp::TraitFor::Request::Foo
     Catalyst::TraitFor::Request::Foo
 
-The namespace part 'TraitFor::Request' was choosen to assist in backwards
+The namespace part 'TraitFor::Request' was chosen to assist in backwards
 compatibility with L<CatalystX::RoleApplicator> which previously provided
 these features in a stand alone package.
   
@@ -2843,7 +2899,7 @@ We try each possible role in turn (and throw an error if none load)
     MyApp::TraitFor::Response::Foo
     Catalyst::TraitFor::Responset::Foo
 
-The namespace part 'TraitFor::Response' was choosen to assist in backwards
+The namespace part 'TraitFor::Response' was chosen to assist in backwards
 compatibility with L<CatalystX::RoleApplicator> which previously provided
 these features in a stand alone package.
 
@@ -3090,7 +3146,7 @@ sub locate_components {
     my $config = shift;
 
     my @paths   = qw( ::M ::Model ::V ::View ::C ::Controller );
-    my $extra   = delete $config->{ search_extra } || [];
+    my $extra   = $config->{ search_extra } || [];
 
     unshift @paths, @$extra;
 
@@ -3516,15 +3572,37 @@ sub setup_encoding {
 =head2 handle_unicode_encoding_exception
 
 Hook to let you customize how encoding errors are handled.  By default
-we just throw an exception.  Receives a hashref of debug information.
-Example:
+we just throw an exception and the default error page will pick it up.
+Receives a hashref of debug information.  Example of call:
 
     $c->handle_unicode_encoding_exception({
         param_value => $value,
         error_msg => $_,
-            encoding_step => 'params',
+        encoding_step => 'params',
         });
 
+You can override this for custom handling of unicode errors.  If you want a
+custom response here, one approach is to throw an HTTP style exception:
+
+    sub handle_unicode_encoding_exception {
+      my ($c, $params) = @_;
+      HTTP::Exception::BAD_REQUEST->throw(status_message=>$params->{error_msg});
+    }
+
+Alternatively you can 'catch' the error, stash it and write handling code later
+in your application:
+
+    sub handle_unicode_encoding_exception {
+      my ($c, $params) = @_;
+      $c->stash(BAD_UNICODE_DATA=>$params);
+      return 1;
+    }
+
+<B>NOTE:</b> Please keep in mind that once an error like this occurs, the request
+setup is aborted, which means the state of C<$c> and related context parts like
+the request and response may not be setup up correctly (since we never finished the
+setup.
+
 =cut
 
 sub handle_unicode_encoding_exception {
@@ -3564,16 +3642,17 @@ sub _handle_unicode_decoding {
 }
 
 sub _handle_param_unicode_decoding {
-    my ( $self, $value ) = @_;
+    my ( $self, $value, $check ) = @_;
     return unless defined $value; # not in love with just ignoring undefs - jnap
     return $value if blessed($value); #don't decode when the value is an object.
 
     my $enc = $self->encoding;
+    $check ||= $self->_encode_check;
     return try {
-      $enc->decode( $value, $self->_encode_check );
+      $enc->decode( $value, $check);
     }
     catch {
-        $self->handle_unicode_encoding_exception({
+        return $self->handle_unicode_encoding_exception({
             param_value => $value,
             error_msg => $_,
             encoding_step => 'params',
@@ -4031,7 +4110,7 @@ We try each possible role in turn (and throw an error if none load)
     MyApp::TraitFor::Stats::Foo
     Catalyst::TraitFor::Stats::Foo
 
-The namespace part 'TraitFor::Stats' was choosen to assist in backwards
+The namespace part 'TraitFor::Stats' was chosen to assist in backwards
 compatibility with L<CatalystX::RoleApplicator> which previously provided
 these features in a stand alone package.
 
@@ -4245,7 +4324,7 @@ backwardly compatible).
 
 C<skip_complex_post_part_handling>
 
-When creating body parameters from a POST, if we run into a multpart POST
+When creating body parameters from a POST, if we run into a multipart POST
 that does not contain uploads, but instead contains inlined complex data
 (very uncommon) we cannot reliably convert that into field => value pairs.  So
 instead we create an instance of L<Catalyst::Request::PartData>.  If this causes
@@ -4267,33 +4346,32 @@ parameter to true.
 C<do_not_decode_query>
 
 If true, then do not try to character decode any wide characters in your
-request URL query or keywords.  Most readings of the relevent specifications
+request URL query or keywords.  Most readings of the relevant specifications
 suggest these should be UTF-* encoded, which is the default that L<Catalyst>
-will use, hwoever if you are creating a lot of URLs manually or have external
+will use, however if you are creating a lot of URLs manually or have external
 evil clients, this might cause you trouble.  If you find the changes introduced
 in Catalyst version 5.90080+ break some of your query code, you may disable 
 the UTF-8 decoding globally using this configuration.
 
-This setting takes precedence over C<default_query_encoding> and
-C<decode_query_using_global_encoding>
+This setting takes precedence over C<default_query_encoding>
 
 =item *
 
-C<default_query_encoding>
-
-By default we decode query and keywords in your request URL using UTF-8, which
-is our reading of the relevent specifications.  This setting allows one to
-specify a fixed value for how to decode your query.  You might need this if
-you are doing a lot of custom encoding of your URLs and not using UTF-8.
+C<do_not_check_query_encoding>
 
-This setting take precedence over C<decode_query_using_global_encoding>.
+Catalyst versions 5.90080 - 5.90106 would decode query parts of an incoming
+request but would not raise an exception when the decoding failed due to
+incorrect unicode.  It now does, but if this change is giving you trouble
+you may disable it by setting this configuration to true.
 
 =item *
 
-C<decode_query_using_global_encoding>
+C<default_query_encoding>
 
-Setting this to true will default your query decoding to whatever your
-general global encoding is (the default is UTF-8).
+By default we decode query and keywords in your request URL using UTF-8, which
+is our reading of the relevant specifications.  This setting allows one to
+specify a fixed value for how to decode your query.  You might need this if
+you are doing a lot of custom encoding of your URLs and not using UTF-8.
 
 =item *
 
@@ -4319,19 +4397,19 @@ C<data_handlers> - See L<DATA HANDLERS>.
 
 C<stats_class_traits>
 
-An arrayref of L<Moose::Role>s that get componsed into your stats class.
+An arrayref of L<Moose::Role>s that get composed into your stats class.
 
 =item *
 
 C<request_class_traits>
 
-An arrayref of L<Moose::Role>s that get componsed into your request class.
+An arrayref of L<Moose::Role>s that get composed into your request class.
 
 =item *
 
 C<response_class_traits>
 
-An arrayref of L<Moose::Role>s that get componsed into your response class.
+An arrayref of L<Moose::Role>s that get composed into your response class.
 
 =item *
 
@@ -4810,6 +4888,8 @@ Caelum: Rafael Kitover <rkitover@io.com>
 
 chansen: Christian Hansen
 
+Chase Venters C<chase.venters@gmail.com>
+
 chicks: Christopher Hicks
 
 Chisel Wright C<pause@herlpacker.co.uk>