simplify some code
[catagits/Catalyst-Runtime.git] / lib / Catalyst.pm
index 738149b..8e487f5 100644 (file)
@@ -120,7 +120,7 @@ __PACKAGE__->mk_classdata($_)
   for qw/components arguments dispatcher engine log dispatcher_class
   engine_loader context_class request_class response_class stats_class
   setup_finished _psgi_app loading_psgi_file run_options _psgi_middleware
-  _data_handlers _encoding _encode_check/;
+  _data_handlers _encoding _encode_check finalized_default_middleware/;
 
 __PACKAGE__->dispatcher_class('Catalyst::Dispatcher');
 __PACKAGE__->request_class('Catalyst::Request');
@@ -129,7 +129,7 @@ __PACKAGE__->stats_class('Catalyst::Stats');
 __PACKAGE__->_encode_check(Encode::FB_CROAK | Encode::LEAVE_SRC);
 
 # Remember to update this in Catalyst::Runtime as well!
-our $VERSION = '5.90079_006';
+our $VERSION = '5.90089_001';
 $VERSION = eval $VERSION if $VERSION =~ /_/; # numify for warning-free dev releases
 
 sub import {
@@ -592,7 +592,7 @@ sub last_error { my ($err, @errs) = @{shift->error}; return $err }
 =head2 shift_errors
 
 shifts the most recently added error off the error stack and returns if.  Returns
-nothing if there are nomore errors.
+nothing if there are no more errors.
 
 =cut
 
@@ -1049,7 +1049,11 @@ Clears the encoding for the current context
 
 =head2 encoding
 
-Sets or gets the application encoding.
+Sets or gets the application encoding.  Setting encoding takes either an
+Encoding object or a string that we try to resolve via L<Encode::find_encoding>.
+
+You would expect to get the encoding object back if you attempt to set it.  If
+there is a failure you will get undef returned and an error message in the log.
 
 =cut
 
@@ -1060,7 +1064,7 @@ sub clear_encoding {
     if(blessed $c) {
         $c->encoding(undef);
     } else {
-        $c->debug->error("You can't clear encoding on the application");
+        $c->log->error("You can't clear encoding on the application");
     }
 }
 
@@ -1069,6 +1073,13 @@ sub encoding {
     my $encoding;
 
     if ( scalar @_ ) {
+
+        # Don't let one change this once we are too far into the response
+        if(blessed $c && $c->res->finalized_headers) {
+          Carp::croak("You may not change the encoding once the headers are finalized");
+          return;
+        }
+
         # Let it be set to undef
         if (my $wanted = shift)  {
             $encoding = Encode::find_encoding($wanted)
@@ -1436,6 +1447,10 @@ In general the scheme of the generated URI object will follow the incoming reque
 however if your targeted action or action chain has the Scheme attribute it will
 use that instead.
 
+Also, if the targeted Action or Action chain declares Args/CaptureArgs that have
+type constraints, we will require that your proposed URL verify on those declared
+constraints.
+
 =cut
 
 sub uri_for {
@@ -1454,60 +1469,59 @@ sub uri_for {
 
     carp "uri_for called with undef argument" if grep { ! defined $_ } @args;
 
-    my @encoded_args = ();
-    foreach my $arg (@args) {
-      if(ref($arg)||'' eq 'ARRAY') {
-        push @encoded_args, [map {
-          my $encoded = encode_utf8 $_;
-          $encoded =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;
-         $encoded;
-        } @$arg];
-      } else {
-        push @encoded_args, do {
-          my $encoded = encode_utf8 $arg;
-          $encoded =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;
-          $encoded;
-        }
-      }
-    }
-
     my $target_action = $path->$_isa('Catalyst::Action') ? $path : undef;
     if ( $path->$_isa('Catalyst::Action') ) { # action object
-        s|/|%2F|g for @encoded_args;
+        s|/|%2F|g for @args;
         my $captures = [ map { s|/|%2F|g; $_; }
-                        ( scalar @encoded_args && ref $encoded_args[0] eq 'ARRAY'
-                         ? @{ shift(@encoded_args) }
+                        ( scalar @args && ref $args[0] eq 'ARRAY'
+                         ? @{ shift(@args) }
                          : ()) ];
 
         my $action = $path;
+        my $expanded_action = $c->dispatcher->expand_action( $action );
+        my $num_captures = $expanded_action->number_of_captures;
+
         # ->uri_for( $action, \@captures_and_args, \%query_values? )
-        if( !@encoded_args && $action->number_of_args ) {
-            my $expanded_action = $c->dispatcher->expand_action( $action );
-            my $num_captures = $expanded_action->number_of_captures;
-            unshift @encoded_args, splice @$captures, $num_captures;
+        if( !@args && $action->number_of_args ) {
+          unshift @args, splice @$captures, $num_captures;
         }
 
-       $path = $c->dispatcher->uri_for_action($action, $captures);
+        if($num_captures) {
+          unless($expanded_action->match_captures($c, $captures)) {
+            carp "captures [@{$captures}] do not match the type constraints in actionchain ending with '$action'";
+            return;
+          }
+        }
+
+        $path = $c->dispatcher->uri_for_action($action, $captures);
         if (not defined $path) {
             $c->log->debug(qq/Can't find uri_for action '$action' @$captures/)
                 if $c->debug;
             return undef;
         }
         $path = '/' if $path eq '';
+
+        # At this point @encoded_args is the remaining Args (all captures removed).
+        if($expanded_action->has_args_constraints) {
+          unless($expanded_action->match_args($c,\@args)) {
+             carp "args [@args] do not match the type constraints in action '$expanded_action'";
+             return;
+          }
+        }
     }
 
-    unshift(@encoded_args, $path);
+    unshift(@args, $path);
 
     unless (defined $path && $path =~ s!^/!!) { # in-place strip
         my $namespace = $c->namespace;
         if (defined $path) { # cheesy hack to handle path '../foo'
-           $namespace =~ s{(?:^|/)[^/]+$}{} while $encoded_args[0] =~ s{^\.\./}{};
+           $namespace =~ s{(?:^|/)[^/]+$}{} while $args[0] =~ s{^\.\./}{};
         }
-        unshift(@encoded_args, $namespace || '');
+        unshift(@args, $namespace || '');
     }
 
     # join args with '/', or a blank string
-    my $args = join('/', grep { defined($_) } @encoded_args);
+    my $args = join('/', grep { defined($_) } @args);
     $args =~ s/\?/%3F/g; # STUPID STUPID SPECIAL CASE
     $args =~ s!^/+!!;
 
@@ -1556,6 +1570,11 @@ sub uri_for {
       } @keys);
     }
 
+    $base = encode_utf8 $base;
+    $base =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;
+    $args = encode_utf8 $args;
+    $args =~ s/([^$URI::uric])/$URI::Escape::escapes{$1}/go;
+    
     my $res = bless(\"${base}${args}${query}", $class);
     $res;
 }
@@ -2105,7 +2124,7 @@ sub finalize_headers {
 =head2 $c->finalize_encoding
 
 Make sure your body is encoded properly IF you set an encoding.  By
-default the encoding is UTF-8 but you can disable it by explictly setting the
+default the encoding is UTF-8 but you can disable it by explicitly setting the
 encoding configuration value to undef.
 
 We can only encode when the body is a scalar.  Methods for encoding via the
@@ -3038,7 +3057,7 @@ $ENV{SCRIPT_NAME}.
 Please B<NOTE> that if you do use C<using_frontend_proxy> the middleware is now
 adding via C<registered_middleware> rather than this method.
 
-If you are using Lighttp or IIS6 you may wish to apply these middlewares.  In
+If you are using Lighttpd or IIS6 you may wish to apply these middlewares.  In
 general this is no longer a common case but we have this here for backward
 compatibility.
 
@@ -3107,7 +3126,7 @@ with a generated server script (via L<Catalyst::Devel> and C<catalyst.pl>) we do
 not attempt to return a valid L<PSGI> application using any existing C<${myapp}.psgi>
 scripts in your $HOME directory.
 
-B<NOTE> C<apply_default_middlewares> was orginally created when the first PSGI
+B<NOTE> C<apply_default_middlewares> was originally created when the first PSGI
 port was done for v5.90000.  These are middlewares that are added to achieve
 backward compatibility with older applications.  If you start your application
 using one of the supplied server scripts (generated with L<Catalyst::Devel> and
@@ -3222,6 +3241,7 @@ sub _handle_unicode_decoding {
 sub _handle_param_unicode_decoding {
     my ( $self, $value ) = @_;
     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;
     return try {
@@ -3522,8 +3542,8 @@ sub setup_middleware {
       @middleware_definitions = reverse(@_);
     } else {
       @middleware_definitions = reverse(@{$class->config->{'psgi_middleware'}||[]})
-        unless $class->config->{__configured_from_psgi_middleware};
-      $class->config->{__configured_from_psgi_middleware} = 1; # Only do this once, just in case some people call setup over and over...
+        unless $class->finalized_default_middleware;
+      $class->finalized_default_middleware(1); # Only do this once, just in case some people call setup over and over...
     }
 
     my @middleware = ();
@@ -3621,7 +3641,7 @@ sub default_data_handlers {
           return eval { 
             local $/;
             $slurped = $fh->getline;
-            $parser->can("decode_json")->($slurped);
+            $parser->can("decode_json")->($slurped); # decode_json does utf8 decoding for us
           } || Catalyst::Exception->throw(sprintf "Error Parsing POST '%s', Error: %s", (defined($slurped) ? $slurped : 'undef') ,$@);
         },
     };
@@ -3871,6 +3891,72 @@ backwardly compatible).
 
 =item *
 
+C<skip_complex_post_part_handling>
+
+When creating body parameters from a POST, if we run into a multpart 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
+issue for you, you can disable this by setting C<skip_complex_post_part_handling>
+to true (default is false).  
+
+=item *
+
+C<skip_body_param_unicode_decoding>
+
+Generally we decode incoming POST params based on your declared encoding (the
+default for this is to decode UTF-8).  If this is causing you trouble and you
+do not wish to turn all encoding support off (with the C<encoding> configuration
+parameter) you may disable this step atomically by setting this configuration
+parameter to true.
+
+=item *
+
+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
+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
+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>
+
+=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.
+
+This setting take precedence over C<decode_query_using_global_encoding>.
+
+=item *
+
+C<decode_query_using_global_encoding>
+
+Setting this to true will default your query decoding to whatever your
+general global encoding is (the default is UTF-8).
+
+=item *
+
+C<use_chained_args_0_special_case>
+
+In older versions of Catalyst, when more than one action matched the same path
+AND all those matching actions declared Args(0), we'd break the tie by choosing
+the first action defined.  We now normalized how Args(0) works so that it
+follows the same rule as Args(N), which is to say when we need to break a tie
+we choose the LAST action defined.  If this breaks your code and you don't
+have time to update to follow the new normalized approach, you may set this
+value to true and it will globally revert to the original chaining behavior.
+
+=item *
+
 C<psgi_middleware> - See L<PSGI MIDDLEWARE>.
 
 =item *
@@ -4207,7 +4293,7 @@ manually as well such as to conform to the JSON specification.
 
 NOTE: We also examine the value of $c->response->content_encoding.  If
 you set this (like for example 'gzip', and manually gzipping the body)
-we assume that you have done all the neccessary encoding yourself, since
+we assume that you have done all the necessary encoding yourself, since
 we cannot encode the gzipped contents.  If you use a plugin like
 L<Catalyst::Plugin::Compress> you need to update to a modern version in order
 to have this function correctly  with the new UTF8 encoding code, or you
@@ -4305,6 +4391,8 @@ acme: Leon Brocard <leon@astray.com>
 
 abraxxa: Alexander Hartmaier <abraxxa@cpan.org>
 
+andrewalker: AndrĂ© Walker <andre@cpan.org>
+
 Andrew Bramble
 
 Andrew Ford E<lt>A.Ford@ford-mason.co.ukE<gt>