Change all classes to Moose
[catagits/Catalyst-Action-REST.git] / lib / Catalyst / Request / REST.pm
CommitLineData
256c894f 1package Catalyst::Request::REST;
2
930013e6 3use Moose;
4use namespace::autoclean;
2998eff6 5use Scalar::Util qw/blessed/;
256c894f 6
930013e6 7extends qw/Catalyst::Request Class::Accessor::Fast/;
37694e6c 8
9use Catalyst::Utils;
9a76221e 10use HTTP::Headers::Util qw(split_header_words);
256c894f 11
5132f5e4 12sub _insert_self_into {
37694e6c 13 my ($class, $app_class ) = @_;
27beb190 14 # the fallback to $app_class is for the (rare and deprecated) case when
15 # people are defining actions in MyApp.pm instead of in a controller.
2998eff6 16 my $app = (blessed($app_class) && $app_class->can('_application'))
17 ? $app_class->_application : Catalyst::Utils::class2appclass( $app_class ) || $app_class;
37694e6c 18
5132f5e4 19 my $req_class = $app->request_class;
20 return if $req_class->isa($class);
21 if ($req_class eq 'Catalyst::Request') {
22 $app->request_class($class);
23 } else {
24 die "$app has a custom request class $req_class, "
25 . "which is not a $class; see Catalyst::Request::REST";
26 }
27}
256c894f 28
bd8fc54e 29__PACKAGE__->mk_accessors(qw(data accept_only));
30
31sub accepted_content_types {
32 my $self = shift;
33
34 return $self->{content_types} if $self->{content_types};
35
36 my %types;
37
38 # First, we use the content type in the HTTP Request. It wins all.
39 $types{ $self->content_type } = 3
40 if $self->content_type;
41
42 if ($self->method eq "GET" && $self->param('content-type')) {
43 $types{ $self->param('content-type') } = 2;
44 }
45
46 # Third, we parse the Accept header, and see if the client
47 # takes a format we understand.
48 #
49 # This is taken from chansen's Apache2::UploadProgress.
50 if ( $self->header('Accept') ) {
51 $self->accept_only(1) unless keys %types;
52
53 my $accept_header = $self->header('Accept');
54 my $counter = 0;
55
56 foreach my $pair ( split_header_words($accept_header) ) {
57 my ( $type, $qvalue ) = @{$pair}[ 0, 3 ];
58 next if $types{$type};
59
60 # cope with invalid (missing required q parameter) header like:
61 # application/json; charset="utf-8"
62 # http://tools.ietf.org/html/rfc2616#section-14.1
63 unless ( defined $pair->[2] && lc $pair->[2] eq 'q' ) {
64 $qvalue = undef;
65 }
66
67 unless ( defined $qvalue ) {
68 $qvalue = 1 - ( ++$counter / 1000 );
69 }
70
71 $types{$type} = sprintf( '%.3f', $qvalue );
72 }
73 }
74
75 return $self->{content_types} =
76 [ sort { $types{$b} <=> $types{$a} } keys %types ];
77}
78
79sub preferred_content_type { $_[0]->accepted_content_types->[0] }
80
81sub accepts {
82 my $self = shift;
83 my $type = shift;
84
85 return grep { $_ eq $type } @{ $self->accepted_content_types };
86}
87
9a76221e 88=head1 NAME
89
90Catalyst::Request::REST - A REST-y subclass of Catalyst::Request
91
92=head1 SYNOPSIS
93
d6fb033c 94 if ( $c->request->accepts('application/json') ) {
9a76221e 95 ...
96 }
97
98 my $types = $c->request->accepted_content_types();
99
100=head1 DESCRIPTION
101
102This is a subclass of C<Catalyst::Request> that adds a few methods to
103the request object to faciliate writing REST-y code. Currently, these
104methods are all related to the content types accepted by the client.
105
5132f5e4 106Note that if you have a custom request class in your application, and it does
107not inherit from C<Catalyst::Request::REST>, your application will fail with an
108error indicating a conflict the first time it tries to use
109C<Catalyst::Request::REST>'s functionality. To fix this error, make sure your
110custom request class inherits from C<Catalyst::Request::REST>.
9a76221e 111
112=head1 METHODS
113
bd8fc54e 114=over
9a76221e 115
bd8fc54e 116=item data
9a76221e 117
bd8fc54e 118If the request went through the Deserializer action, this method will
119return the deserialized data structure.
fec6d454 120
9a76221e 121=item accepted_content_types
122
123Returns an array reference of content types accepted by the
124client.
125
126The list of types is created by looking at the following sources:
127
128=over 8
129
130=item * Content-type header
131
132If this exists, this will always be the first type in the list.
133
134=item * content-type parameter
135
136If the request is a GET request and there is a "content-type"
137parameter in the query string, this will come before any types in the
138Accept header.
139
140=item * Accept header
141
142This will be parsed and the types found will be ordered by the
143relative quality specified for each type.
144
145=back
146
147If a type appears in more than one of these places, it is ordered based on
148where it is first found.
149
9a76221e 150=item preferred_content_type
151
152This returns the first content type found. It is shorthand for:
153
154 $request->accepted_content_types->[0]
155
9a76221e 156=item accepts($type)
157
158Given a content type, this returns true if the type is accepted.
159
160Note that this does not do any wildcard expansion of types.
161
fec6d454 162=back
163
6e6e0bf9 164=head1 AUTHORS
9a76221e 165
6e6e0bf9 166See L<Catalyst::Action::REST> for authors.
fec6d454 167
9a76221e 168=head1 LICENSE
169
170You may distribute this code under the same terms as Perl itself.
171
172=cut
173
1741;