File Coverage

lib/Catalyst/Action/Serialize.pm
Criterion Covered Total %
statement 37 42 88.1
branch 15 28 53.5
condition 3 8 37.5
subroutine 5 5 100.0
pod 1 1 100.0
total 61 84 72.6


line stmt bran cond sub pod time code
1             package Catalyst::Action::Serialize;
2             $Catalyst::Action::Serialize::VERSION = '1.20';
3 12     12   125026 use Moose;
  12         27  
  12         101  
4 12     12   79003 use namespace::autoclean;
  12         29  
  12         133  
5              
6             extends 'Catalyst::Action::SerializeBase';
7 12     12   932 use Module::Pluggable::Object;
  12         25  
  12         352  
8 12     12   64 use MRO::Compat;
  12         21  
  12         7134  
9              
10             has _encoders => (
11             is => 'ro',
12             isa => 'HashRef',
13             default => sub { {} },
14             );
15              
16             sub execute {
17 24     24 1 34770 my $self = shift;
18 24         51 my ( $controller, $c ) = @_;
19              
20 24         122 $self->maybe::next::method(@_);
21              
22 24 50       1132 return 1 if $c->req->method eq 'HEAD';
23 24 100       2018 return 1 if $c->response->has_body;
24 8 50       368 return 1 if scalar @{ $c->error };
  8         27  
25 8 50       310 return 1 if $c->response->status =~ /^(?:204)$/;
26 8 50       1107 return 1 if defined $c->stash->{current_view};
27 8 50       589 return 1 if defined $c->stash->{current_view_instance};
28              
29             # on 3xx responses, serialize if there's something to
30             # serialize, no-op if not
31             my $stash_key = (
32             $controller->{'serialize'} ?
33             $controller->{'serialize'}->{'stash_key'} :
34 8   50     610 $controller->{'stash_key'}
35             ) || 'rest';
36 8 50 33     216 return 1 if $c->response->status =~ /^(?:3\d\d)$/ && ! defined $c->stash->{$stash_key};
37              
38 8         1003 my ( $sclass, $sarg, $content_type ) =
39             $self->_load_content_plugins( "Catalyst::Action::Serialize",
40             $controller, $c );
41 8 50       36 unless ( defined($sclass) ) {
42 0 0       0 if ( defined($content_type) ) {
43 0         0 $c->log->info("Could not find a serializer for $content_type");
44             } else {
45 0         0 $c->log->info(
46             "Could not find a serializer for an empty content-type");
47             }
48 0         0 return 1;
49             }
50             $c->log->debug(
51 8 0       44 "Serializing with $sclass" . ( $sarg ? " [$sarg]" : '' ) ) if $c->debug;
    50          
52              
53 8   33     363 $self->_encoders->{$sclass} ||= $sclass->new;
54 8         362 my $sobj = $self->_encoders->{$sclass};
55              
56 8         19 my $rc;
57 8         15 eval {
58 8 100       27 if ( defined($sarg) ) {
59 3         15 $rc = $sobj->execute( $controller, $c, $sarg );
60             } else {
61 5         21 $rc = $sobj->execute( $controller, $c );
62             }
63             };
64 8 100       443 if ($@) {
    50          
65 1         14 return $self->serialize_bad_request( $c, $content_type, $@ );
66             } elsif (!$rc) {
67 0         0 return $self->unsupported_media_type( $c, $content_type );
68             }
69              
70 7         25 return 1;
71             }
72              
73             __PACKAGE__->meta->make_immutable;
74              
75             1;
76              
77             =head1 NAME
78              
79             Catalyst::Action::Serialize - Serialize Data in a Response
80              
81             =head1 SYNOPSIS
82              
83             package Foo::Controller::Bar;
84              
85             __PACKAGE__->config(
86             'default' => 'text/x-yaml',
87             'stash_key' => 'rest',
88             'map' => {
89             'text/html' => [ 'View', 'TT', ],
90             'text/x-yaml' => 'YAML',
91             'text/x-data-dumper' => [ 'Data::Serializer', 'Data::Dumper' ],
92             }
93             );
94              
95             sub end :ActionClass('Serialize') {}
96              
97             =head1 DESCRIPTION
98              
99             This action will serialize the body of an HTTP Response. The serializer is
100             selected by introspecting the HTTP Requests content-type header.
101              
102             It requires that your Catalyst controller is properly configured to set up the
103             mapping between Content Type's and Serialization classes.
104              
105             The specifics of serializing each content-type is implemented as a plugin to
106             L<Catalyst::Action::Serialize>.
107              
108             Typically, you would use this ActionClass on your C<end> method. However,
109             nothing is stopping you from choosing specific methods to Serialize:
110              
111             sub foo :Local :ActionClass('Serialize') {
112             .. populate stash with data ..
113             }
114              
115             When you use this module, the request class will be changed to
116             L<Catalyst::Request::REST>.
117              
118             =head1 CONFIGURATION
119              
120             =head2 map
121              
122             Takes a hashref, mapping Content-Types to a given serializer plugin.
123              
124             =head2 default
125              
126             This is the 'fall-back' Content-Type if none of the requested or acceptable
127             types is found in the L</map>. It must be an entry in the L</map>.
128              
129             =head2 stash_key
130              
131             Specifies the key of the stash entry holding the data that is to be serialized.
132             So if the value is "rest", we will serialize the data under:
133              
134             $c->stash->{'rest'}
135              
136             =head2 content_type_stash_key
137              
138             Specifies the key of the stash entry that optionally holds an overriding
139             Content-Type. If set, and if the specified stash entry has a valid value,
140             then it takes priority over the requested content types.
141              
142             This can be useful if you want to dynamically force a particular content type,
143             perhaps for debugging.
144              
145             =head1 HELPFUL PEOPLE
146              
147             Daisuke Maki pointed out that early versions of this Action did not play
148             well with others, or generally behave in a way that was very consistent
149             with the rest of Catalyst.
150              
151             =head1 CUSTOM ERRORS
152              
153             For building custom error responses when serialization fails, you can create
154             an ActionRole (and use L<Catalyst::Controller::ActionRole> to apply it to the
155             C<end> action) which overrides C<unsupported_media_type> and/or C<serialize_bad_request>
156             methods.
157              
158             =head1 SEE ALSO
159              
160             You likely want to look at L<Catalyst::Controller::REST>, which implements
161             a sensible set of defaults for doing a REST controller.
162              
163             L<Catalyst::Action::Deserialize>, L<Catalyst::Action::REST>
164              
165             =head1 AUTHORS
166              
167             See L<Catalyst::Action::REST> for authors.
168              
169             =head1 LICENSE
170              
171             You may distribute this code under the same terms as Perl itself.
172              
173             =cut