Bump versions for release
[catagits/Catalyst-Action-REST.git] / lib / Catalyst / TraitFor / Request / REST.pm
CommitLineData
e623bdf2 1package Catalyst::TraitFor::Request::REST;
2use Moose::Role;
3use HTTP::Headers::Util qw(split_header_words);
4use namespace::autoclean;
5
3bb36dca 6our $VERSION = '0.82';
f465980c 7$VERSION = eval $VERSION;
8
e623bdf2 9has [qw/ data accept_only /] => ( is => 'rw' );
10
85aa4e18 11has accepted_content_types => (
12 is => 'ro',
13 isa => 'ArrayRef',
14 lazy => 1,
15 builder => '_build_accepted_content_types',
16 init_arg => undef,
17);
18
19has preferred_content_type => (
20 is => 'ro',
21 isa => 'Str',
22 lazy => 1,
23 builder => '_build_preferred_content_type',
24 init_arg => undef,
25);
26
27sub _build_accepted_content_types {
e623bdf2 28 my $self = shift;
29
e623bdf2 30 my %types;
31
32 # First, we use the content type in the HTTP Request. It wins all.
33 $types{ $self->content_type } = 3
34 if $self->content_type;
35
36 if ($self->method eq "GET" && $self->param('content-type')) {
37 $types{ $self->param('content-type') } = 2;
38 }
39
40 # Third, we parse the Accept header, and see if the client
41 # takes a format we understand.
42 #
43 # This is taken from chansen's Apache2::UploadProgress.
44 if ( $self->header('Accept') ) {
45 $self->accept_only(1) unless keys %types;
46
47 my $accept_header = $self->header('Accept');
48 my $counter = 0;
49
50 foreach my $pair ( split_header_words($accept_header) ) {
51 my ( $type, $qvalue ) = @{$pair}[ 0, 3 ];
52 next if $types{$type};
53
54 # cope with invalid (missing required q parameter) header like:
55 # application/json; charset="utf-8"
56 # http://tools.ietf.org/html/rfc2616#section-14.1
57 unless ( defined $pair->[2] && lc $pair->[2] eq 'q' ) {
58 $qvalue = undef;
59 }
60
61 unless ( defined $qvalue ) {
62 $qvalue = 1 - ( ++$counter / 1000 );
63 }
64
65 $types{$type} = sprintf( '%.3f', $qvalue );
66 }
67 }
68
85aa4e18 69 [ sort { $types{$b} <=> $types{$a} } keys %types ];
e623bdf2 70}
71
85aa4e18 72sub _build_preferred_content_type { $_[0]->accepted_content_types->[0] }
e623bdf2 73
74sub accepts {
75 my $self = shift;
76 my $type = shift;
77
78 return grep { $_ eq $type } @{ $self->accepted_content_types };
79}
80
811;
38e05ec4 82__END__
83
84=head1 NAME
85
86Catalyst::TraitFor::Request::REST - A role to apply to Catalyst::Request giving it REST methods and attributes.
87
88=head1 SYNOPSIS
89
90 if ( $c->request->accepts('application/json') ) {
91 ...
92 }
93
94 my $types = $c->request->accepted_content_types();
95
96=head1 DESCRIPTION
97
98This is a L<Moose::Role> applied to L<Catalyst::Request> that adds a few
d6ece98c 99methods to the request object to facilitate writing REST-y code.
38e05ec4 100Currently, these methods are all related to the content types accepted by
101the client.
102
103=head1 METHODS
104
105=over
106
107=item data
108
109If the request went through the Deserializer action, this method will
110return the deserialized data structure.
111
112=item accepted_content_types
113
114Returns an array reference of content types accepted by the
115client.
116
117The list of types is created by looking at the following sources:
118
119=over 8
120
121=item * Content-type header
122
123If this exists, this will always be the first type in the list.
124
125=item * content-type parameter
126
127If the request is a GET request and there is a "content-type"
128parameter in the query string, this will come before any types in the
129Accept header.
130
131=item * Accept header
132
133This will be parsed and the types found will be ordered by the
134relative quality specified for each type.
135
136=back
137
138If a type appears in more than one of these places, it is ordered based on
139where it is first found.
140
141=item preferred_content_type
142
143This returns the first content type found. It is shorthand for:
144
145 $request->accepted_content_types->[0]
146
147=item accepts($type)
148
149Given a content type, this returns true if the type is accepted.
150
151Note that this does not do any wildcard expansion of types.
152
153=back
154
155=head1 AUTHORS
156
157See L<Catalyst::Action::REST> for authors.
158
159=head1 LICENSE
160
161You may distribute this code under the same terms as Perl itself.
162
163=cut
164