File Coverage

blib/lib/Catalyst/ActionRole/ConsumesContent.pm
Criterion Covered Total %
statement 18 18 100.0
branch 6 6 100.0
condition n/a
subroutine 4 4 100.0
pod 1 1 100.0
total 29 29 100.0


line stmt bran cond sub pod time code
1             package Catalyst::ActionRole::ConsumesContent;
2              
3 4     4   3189 use Moose::Role;
  4         20  
  4         36  
4              
5             requires 'match', 'match_captures', 'list_extra_info';
6              
7             has allowed_content_types => (
8             is=>'ro',
9             required=>1,
10             lazy=>1,
11             isa=>'ArrayRef',
12             builder=>'_build_allowed_content_types');
13              
14             has normalized => (
15             is=>'ro',
16             required=>1,
17             lazy=>1,
18             isa=>'HashRef',
19             builder=>'_build_normalized');
20              
21              
22             sub _build_normalized {
23             return +{
24 13     13   656 JSON => 'application/json',
25             JS => 'application/javascript',
26             PERL => 'application/perl',
27             HTML => 'text/html',
28             XML => 'text/XML',
29             Plain => 'text/plain',
30             UrlEncoded => 'application/x-www-form-urlencoded',
31             Multipart => 'multipart/form-data',
32             HTMLForm => ['application/x-www-form-urlencoded','multipart/form-data'],
33             };
34             }
35              
36             sub _build_allowed_content_types {
37 13     13   24 my $self = shift;
38 13         29 my @proto = map {split ',', $_ } @{$self->attributes->{Consumes}};
  14         71  
  13         375  
39             my @converted = map {
40 13 100       35 if(my $normalized = $self->normalized->{$_}) {
  15         512  
41 8 100       43 ref $normalized ? @$normalized : ($normalized);
42             } else {
43 7         24 $_;
44             }
45             } @proto;
46              
47 13         473 return \@converted;
48             }
49              
50             around ['match','match_captures'] => sub {
51             my ($orig, $self, $ctx, @args) = @_;
52             if(my $content_type = $ctx->req->content_type) {
53             return 0 unless $self->can_consume($content_type);
54             }
55             return $self->$orig($ctx, @args);
56             };
57              
58             sub can_consume {
59 57     57 1 132 my ($self, $request_content_type) = @_;
60 63         250 my @matches = grep { lc($_) eq lc($request_content_type) }
61 57         98 @{$self->allowed_content_types};
  57         2039  
62 57 100       355 return @matches ? 1:0;
63             }
64              
65             around 'list_extra_info' => sub {
66             my ($orig, $self, @args) = @_;
67             return {
68             %{ $self->$orig(@args) },
69             CONSUMES => $self->allowed_content_types,
70             };
71             };
72              
73             1;
74              
75             =head1 NAME
76              
77             Catalyst::ActionRole::ConsumesContent - Match on HTTP Request Content-Type
78              
79             =head1 SYNOPSIS
80              
81             package MyApp::Web::Controller::MyController;
82              
83             use base 'Catalyst::Controller';
84              
85             sub start : POST Chained('/') CaptureArg(0) { ... }
86              
87             sub is_json : Chained('start') Consumes('application/json') { ... }
88             sub is_urlencoded : Chained('start') Consumes('application/x-www-form-urlencoded') { ... }
89             sub is_multipart : Chained('start') Consumes('multipart/form-data') { ... }
90              
91             ## Alternatively, for common types...
92              
93             sub is_json : Chained('start') Consume(JSON) { ... }
94             sub is_urlencoded : Chained('start') Consumes(UrlEncoded) { ... }
95             sub is_multipart : Chained('start') Consumes(Multipart) { ... }
96              
97             ## Or allow more than one type
98              
99             sub is_more_than_one
100             : Chained('start')
101             : Consumes('application/x-www-form-urlencoded')
102             : Consumes('multipart/form-data')
103             {
104             ## ...
105             }
106              
107             1;
108              
109             =head1 DESCRIPTION
110              
111             This is an action role that lets your L<Catalyst::Action> match on the content
112             type of the incoming request.
113              
114             Generally when there's a PUT or POST request, there's a request content body
115             with a matching MIME content type. Commonly this will be one of the types
116             used with classic HTML forms ('application/x-www-form-urlencoded' for example)
117             but there's nothing stopping you specifying any valid content type.
118              
119             For matching purposes, we match strings but the casing is insensitive.
120              
121             =head1 REQUIRES
122              
123             This role requires the following methods in the consuming class.
124              
125             =head2 match
126              
127             =head2 match_captures
128              
129             Returns 1 if the action matches the existing request and zero if not.
130              
131             =head1 METHODS
132              
133             This role defines the following methods
134              
135             =head2 match
136              
137             =head2 match_captures
138              
139             Around method modifier that return 1 if the request content type matches one of the
140             allowed content types (see L</http_methods>) and zero otherwise.
141              
142             =head2 allowed_content_types
143              
144             An array of strings that are the allowed content types for matching this action.
145              
146             =head2 can_consume
147              
148             Boolean. Does the current request match content type with what this actionrole
149             can consume?
150              
151             =head2 list_extra_info
152              
153             Add the accepted content type to the debug screen.
154              
155             =head1 AUTHORS
156              
157             Catalyst Contributors, see Catalyst.pm
158              
159             =head1 COPYRIGHT
160              
161             This library is free software. You can redistribute it and/or modify it under
162             the same terms as Perl itself.
163              
164             =cut