File Coverage

blib/lib/HTTP/Headers/ActionPack/ContentNegotiation.pm
Criterion Covered Total %
statement 90 90 100.0
branch 41 48 85.4
condition 12 18 66.6
subroutine 20 20 100.0
pod 4 6 66.6
total 167 182 91.7


line stmt bran cond sub pod time code
1             package HTTP::Headers::ActionPack::ContentNegotiation;
2             BEGIN {
3 4     4   4274 $HTTP::Headers::ActionPack::ContentNegotiation::AUTHORITY = 'cpan:STEVAN';
4             }
5             {
6             $HTTP::Headers::ActionPack::ContentNegotiation::VERSION = '0.09';
7             }
8             # ABSTRACT: A class to handle content negotiation
9              
10 4     4   41 use strict;
  4         8  
  4         155  
11 4     4   25 use warnings;
  4         8  
  4         149  
12              
13 4     4   22 use Carp qw[ confess ];
  4         7  
  4         349  
14 4     4   21 use Scalar::Util qw[ blessed ];
  4         10  
  4         200  
15 4     4   19 use List::Util qw[ first ];
  4         13  
  4         5070  
16              
17             sub new {
18 4     4 0 52 my $class = shift;
19 4         9 my $action_pack = shift;
20              
21 4 50 33     77 (blessed $action_pack && $action_pack->isa('HTTP::Headers::ActionPack'))
22             || confess "You must supply an instance of HTTP::Headers::ActionPack";
23              
24 4         28 bless { action_pack => $action_pack } => $class;
25             }
26              
27 87     87 0 363 sub action_pack { (shift)->{'action_pack'} }
28              
29             sub choose_media_type {
30 20     20 1 894 my ($self, $provided, $header) = @_;
31 20 50       81 my $requested = blessed $header ? $header : $self->action_pack->create( 'MediaTypeList' => $header );
32 20         42 my $parsed_provided = [ map { $self->action_pack->create( 'MediaType' => $_ ) } @$provided ];
  38         78  
33              
34 20         27 my $chosen;
35 20         61 foreach my $request ( $requested->iterable ) {
36 32         52 my $requested_type = $request->[1];
37 32         62 $chosen = _media_match( $requested_type, $parsed_provided );
38 32 100       184 return $chosen if $chosen;
39             }
40              
41 3         52 return;
42             }
43              
44             sub choose_language {
45 13     13 1 723 my ($self, $provided, $header) = @_;
46              
47 13         39 return $self->_make_choice(
48             choices => $provided,
49             header => $header,
50             class => 'AcceptLanguage',
51             matcher => \&_language_match,
52             );
53             }
54              
55             sub choose_charset {
56 14     14 1 829 my ($self, $provided, $header) = @_;
57              
58             # NOTE:
59             # Making the default charset UTF-8, which
60             # is maybe sensible, I dunno.
61             # - SL
62 14         43 return $self->_make_choice(
63             choices => $provided,
64             header => $header,
65             class => 'AcceptCharset',
66             default => 'UTF-8',
67             matcher => \&_simple_match,
68             );
69             }
70              
71             sub choose_encoding {
72 5     5 1 516 my ($self, $provided, $header) = @_;
73              
74 5         16 return $self->_make_choice(
75             choices => $provided,
76             header => $header,
77             class => 'PriorityList',
78             default => 'identity',
79             matcher => \&_simple_match,
80             );
81             }
82              
83             sub _make_choice {
84 32     32   42 my $self = shift;
85 32         135 my %args = @_;
86              
87 32         85 my ($choices, $header, $class, $default, $matcher)
88             = @args{qw( choices header class default matcher )};
89              
90 32 100       103 return if @$choices == 0;
91 29 50       103 return if $header eq '';
92              
93 29 50       138 my $accepted = blessed $header ? $header : $self->action_pack->create( $class => $header );
94 29         99 my $star_priority = $accepted->priority_of( '*' );
95              
96 59         143 my @canonical = map {
97 29         56 my $c = $accepted->canonicalize_choice($_);
98 59 50       352 $c ? [ $_, $c ] : ()
99             } @$choices;
100              
101 29         38 my ($default_ok, $any_ok);
102              
103 29 100       59 if ($default) {
104 17         45 $default = $accepted->canonicalize_choice($default);
105 17         131 my $default_priority = $accepted->priority_of( $args{default} );
106              
107 17 100       49 if ( not defined $default_priority ) {
    100          
108 6 100 66     23 if ( defined $star_priority && $star_priority == 0.0 ) {
109 1         3 $default_ok = 0;
110             }
111             else {
112 5         6 $default_ok = 1;
113             }
114             }
115             elsif ( $default_priority == 0.0 ) {
116 3         5 $default_ok = 0;
117             }
118             else {
119 8         15 $default_ok = 1;
120             }
121             }
122              
123 29 100       155 if ( not defined $star_priority ) {
    100          
124 26         30 $any_ok = 0;
125             }
126             elsif ( $star_priority == 0.0 ) {
127 1         2 $any_ok = 0;
128             }
129             else {
130 2         5 $any_ok = 1;
131             }
132              
133 29         31 my $chosen;
134 29         162 for my $item ($accepted->iterable) {
135 44         77 my ($priority, $acceptable) = @$item;
136              
137 44 100       112 next if $priority == 0;
138              
139 40 100   67   209 if (my $match = first { $matcher->( $acceptable, $_->[1] ) } @canonical) {
  67         148  
140 20         41 $chosen = $match->[0];
141 20         40 last;
142             }
143             }
144              
145 29 100       333 return $chosen if $chosen;
146              
147 9 100       25 if ($any_ok) {
148             my $match = first {
149 2     2   7 my $priority = $accepted->priority_of( $_->[1] );
150 2 100 66     13 return 1 unless defined $priority && $priority == 0;
151 1         2 return 0;
152             }
153 1         4 @canonical;
154              
155 1 50       14 return $match->[0] if $match;
156             }
157              
158 8 100 100     42 if ( $default && $default_ok ) {
159 4     4   30 my $match = first { $matcher->( $default, $_->[1] ) } @canonical;
  4         10  
160 4 100       21 if ($match) {
161 2         8 my $priority = $accepted->priority_of( $match->[1] );
162 2 50 33     27 return $match->[0] unless defined $priority && $priority == 0;
163             }
164             }
165              
166 6         74 return;
167             }
168              
169             ## ....
170              
171             sub _media_match {
172 32     32   45 my ($requested, $provided) = @_;
173 32 100       94 return $provided->[0] if $requested->matches_all;
174 31     55   174 return first { $_->match( $requested ) } @$provided;
  55         189  
175             }
176              
177             sub _language_match {
178 27     27   38 my ($range, $tag) = @_;
179 27 100 100     373 ((lc $range) eq (lc $tag)) || $range eq "*" || $tag =~ /^$range\-/i;
180             }
181              
182             sub _simple_match {
183 44     44   155 return $_[0] eq $_[1];
184             }
185              
186             1;
187              
188             __END__