Add content_type_stash_key to enable a stash entry to override the serialize content...
timbunce [Wed, 11 Mar 2009 16:46:02 +0000 (16:46 +0000)]
With docs and tests.

lib/Catalyst/Action/Serialize.pm
lib/Catalyst/Action/SerializeBase.pm
t/catalyst-action-serialize-accept.t

index c5f6b71..bdf0e73 100644 (file)
@@ -99,27 +99,30 @@ L<Catalyst::Request::REST>.
 
 =head1 CONFIGURATION
 
-=over 4
+=head2 map
 
-=item default
+Takes a hashref, mapping Content-Types to a given serializer plugin.
 
-The Content-Type of the default Serialization format.  This must be a
-Content-Type associated with a plugin in the "map" section below.  
+=head2 default
 
-This is used if a requested content-type is not recognized.
+This is the 'fall-back' Content-Type if none of the requested or acceptable
+types is found in the L</map>. It must be an entry in the L</map>.
 
-=item stash_key 
+=head2 stash_key 
 
-We will serialize the data that lives in this location in the stash.  So
-if the value is "rest", we will serialize the data under:
+Specifies the key of the stash entry holding the data that is to be serialized.
+So if the value is "rest", we will serialize the data under:
 
   $c->stash->{'rest'}
 
-=item map
+=head2 content_type_stash_key
 
-Takes a hashref, mapping Content-Types to a given plugin.
+Specifies the key of the stash entry that optionally holds an overriding
+Content-Type. If set, and if the specified stash entry has a valid value,
+then it takes priority over the requested content types.
 
-=back
+This can be useful if you want to dynamically force a particular content type,
+perhaps for debugging.
 
 =head1 HELPFUL PEOPLE
 
index b7a6765..ac4934f 100644 (file)
@@ -53,18 +53,27 @@ sub _load_content_plugins {
         $config = $controller->config;
     }
     $map = $config->{'map'};
-    # If we don't have a handler for our preferred content type, try
-    # the default
 
-    my ($content_type) = grep { $map->{$_} } @{$c->request->accepted_content_types};
-
-    unless ( defined $content_type ) {
-        if( exists $config->{'default'} ) {
-            $content_type = $config->{'default'} ;
-        } else {
-            return $self->_unsupported_media_type($c, $content_type);
-        }
+    # pick preferred content type
+    my @accepted_types; # priority order, best first
+    # give top priority to content type specified by stash, if any
+    my $content_type_stash_key = $config->{content_type_stash_key};
+    if ($content_type_stash_key
+        and my $stashed = $c->stash->{$content_type_stash_key}
+    ) {
+        # convert to array if not already a ref
+        $stashed = [ $stashed ] if not ref $stashed;
+        push @accepted_types, @$stashed;
     }
+    # then content types requested by caller
+    push @accepted_types, @{ $c->request->accepted_content_types };
+    # then the default
+    push @accepted_types, $config->{'default'} if $config->{'default'};
+    # pick the best match that we have a serializer mapping for
+    my ($content_type) = grep { $map->{$_} } @accepted_types;
+
+    return $self->_unsupported_media_type($c, $content_type)
+        if not $content_type;
 
     # carp about old text/x-json
     if ($content_type eq 'text/x-json') {
index 7778059..90a4fe4 100644 (file)
@@ -16,6 +16,7 @@ __PACKAGE__->config(
     serialize => {
         'default'   => 'text/x-yaml',
         'stash_key' => 'rest',
+        'content_type_stash_key' => 'serialize_content_type',
         'map'       => {
             'text/x-yaml'        => 'YAML',
             'application/json'   => 'JSON',
@@ -36,16 +37,18 @@ sub test :Local :ActionClass('Serialize') {
 
 sub test_second :Local :ActionClass('Serialize') {
     my ( $self, $c ) = @_;
+    $c->stash->{'serialize_content_type'} = $c->req->params->{'serialize_content_type'};
     $c->stash->{'rest'} = {
         lou => 'is my cat',
     };
 }
 
+
 package main;
 
 use strict;
 use warnings;
-use Test::More tests => 10;
+use Test::More tests => 16;
 use Data::Serializer;
 use FindBin;
 use Data::Dump qw(dump);
@@ -107,4 +110,26 @@ SKIP: {
        is( $res->header('Content-type'), 'text/x-yaml', '... with expected content-type')
 }
 
+# Make that using content_type_stash_key, an invalid value in the stash gets ignored
+{
+       my $req = $t->get(url => '/test_second?serialize_content_type=nonesuch');
+       $req->remove_header('Content-Type');
+       $req->header('Accept', '*/*');
+       my $res = request($req);
+       ok( $res->is_success, 'GET the serialized request succeeded' );
+       is( $res->content, $data, "Request returned proper data");
+       is( $res->header('Content-type'), 'text/x-yaml', '... with expected content-type')
+}
+
+# Make that using content_type_stash_key, a valid value in the stash gets priority
+{
+       my $req = $t->get(url => '/test_second?serialize_content_type=text/x-data-dumper');
+       $req->remove_header('Content-Type');
+       $req->header('Accept', '*/*');
+       my $res = request($req);
+       ok( $res->is_success, 'GET the serialized request succeeded' );
+       is( $res->content, "{'lou' => 'is my cat'}", "Request returned proper data");
+       is( $res->header('Content-type'), 'text/x-data-dumper', '... with expected content-type')
+}
+
 1;