File Coverage

blib/lib/HTTP/AcceptLanguage.pm
Criterion Covered Total %
statement 84 84 100.0
branch 40 40 100.0
condition 9 12 75.0
subroutine 10 10 100.0
pod 3 3 100.0
total 146 149 97.9


line stmt bran cond sub pod time code
1             package HTTP::AcceptLanguage;
2 6     6   227740 use strict;
  6         16  
  6         401  
3 6     6   31 use warnings;
  6         11  
  6         157  
4 6     6   140 use 5.008_005;
  6         22  
  6         3035  
5             our $VERSION = '0.02';
6              
7             our $MATCH_PRIORITY_0_01_STYLE;
8              
9             my $LANGUAGE_RANGE = qr/(?:[A-Za-z0-9]{1,8}(?:-[A-Za-z0-9]{1,8})*|\*)/;
10             my $QVALUE = qr/(?:0(?:\.[0-9]{0,3})?|1(?:\.0{0,3})?)/;
11              
12             sub new {
13 101     101 1 58034 my($class, $header) = @_;
14              
15 101         128 my @parsed_header;
16 101 100       244 if ($header) {
17 87         222 @parsed_header = $class->_parse($header);
18             }
19              
20             bless {
21 101         641 header => $header,
22             parsed_header => \@parsed_header,
23             }, $class;
24             }
25              
26             sub _parse {
27 87     87   130 my($class, $header) = @_;
28 87         432 $header =~ s/\s//g; #loose
29              
30 87         113 my @elements;
31             my %high_qualities;
32 87         369 for my $element (split /,+/, $header) {
33 167         1567 my($language, $quality) = $element =~ /\A($LANGUAGE_RANGE)(?:;q=($QVALUE))?\z/;
34 167 100       427 $quality = 1 unless defined $quality;
35 167 100 100     780 next unless $language && $quality > 0;
36              
37 153         308 my($primary) = split /-/, $language;
38 153         690 push @elements, {
39             language => $language,
40             language_primary_lc => lc($primary),
41             language_lc => lc($language),
42             quality => $quality,
43             };
44 153 100 100     458 if ((not exists $high_qualities{$language}) || $quality > $high_qualities{$language}) {
45 146         1701 $high_qualities{$language} = $quality;
46             }
47             }
48              
49             # RFC2616: The language quality factor assigned to a language-tag by the Accept-Language field is the quality value of the longest language- range in the field that matches the language-tag.
50             grep {
51 87         198 my $language = $_->{language};
  153         247  
52 153 100       908 $high_qualities{$language} ? (
    100          
53             $high_qualities{$language} == $_->{quality} ? delete $high_qualities{$language} : 0
54             ) : 0;
55             } @elements;
56             }
57              
58             sub languages {
59 19     19 1 85 my $self = shift;
60 19   33     53 $self->{languages} ||= do {
61 6     6   6709 use sort 'stable';
  6         3811  
  6         32  
62 19         20 my @languages = map { $_->{language} } sort { $b->{quality} <=> $a->{quality} } @{ $self->{parsed_header} };
  22         48  
  17         33  
  19         50  
63 19         53 \@languages;
64             };
65 19         18 @{ $self->{languages} };
  19         99  
66             }
67              
68             sub match {
69 84     84 1 623 my($self, @languages) = @_;
70 149 100       657 my @normlized_languages = map {
71 84         124 $_ ? ( +{
72             tag => $_,
73             tag_lc => lc($_),
74             } ) : ()
75             } @languages;
76 84 100       247 return undef unless scalar(@normlized_languages);
77              
78 72 100       89 unless (scalar(@{ $self->{parsed_header} })) {
  72         236  
79             # RFC2616: SHOULD assume that all languages are equally acceptable. If an Accept-Language header is present, then all languages which are assigned a quality factor greater than 0 are acceptable.
80 4         24 return $normlized_languages[0]->{tag};
81             }
82              
83 68   66     207 $self->{sorted_parsed_header} ||= do {
84 6     6   1135 use sort 'stable';
  6         11  
  6         25  
85 66         77 [ sort { $b->{quality} <=> $a->{quality} } @{ $self->{parsed_header} } ];
  55         264  
  66         280  
86             };
87              
88             # If language-quality has the same value, is a priority order of the $self->{sorted_parsed_header}.
89             # If you set $MATCH_PRIORITY_0_01_STYLE=1, takes is a priority order of the @languages
90 68 100       135 if ($MATCH_PRIORITY_0_01_STYLE) {
91 33         181 my %header_tags;
92             my %header_primary_tags;
93             my $detect_langguage = sub {
94 62 100   62   200 if (scalar(%header_tags)) {
95             # RFC give priority to full match.
96 29         43 for my $tag (@normlized_languages) {
97 40 100       146 return $tag->{tag} if $header_tags{$tag->{tag_lc}};
98             }
99 4         9 for my $tag (@normlized_languages) {
100 4 100       27 return $tag->{tag} if $header_primary_tags{$tag->{tag_lc}};
101             }
102             }
103 33         144 };
104 33         59 my $current_quality = 0;
105 33         59 for my $language (@{ $self->{sorted_parsed_header} }) {
  33         78  
106 54 100       130 if ($current_quality != $language->{quality}) {
107             # check of the last quality languages
108 39         69 my $ret = $detect_langguage->();
109 39 100       135 return $ret if $ret;
110              
111             # cleanup
112 33         50 $current_quality = $language->{quality};
113 33         57 %header_tags = ();
114 33         50 %header_primary_tags = ();
115             }
116              
117             # wildcard
118 48 100       145 return $normlized_languages[0]->{tag} if $language->{language} eq '*';
119              
120 44         89 $header_tags{$language->{language_lc}} = 1;
121 44         109 $header_primary_tags{$language->{language_primary_lc}} = 1;
122             }
123              
124 23         69 my $ret = $detect_langguage->();
125 23 100       758 return $ret if $ret;
126             } else {
127             # 0.02 or more
128 35         38 for my $language (@{ $self->{sorted_parsed_header} }) {
  35         79  
129             # wildcard
130 37 100       115 return $normlized_languages[0]->{tag} if $language->{language} eq '*';
131              
132             # RFC give priority to full match.
133 34         66 for my $tag (@normlized_languages) {
134 56 100       305 return $tag->{tag} if $language->{language_lc} eq $tag->{tag_lc};
135             }
136 10         21 for my $tag (@normlized_languages) {
137 14 100       75 return $tag->{tag} if $language->{language_primary_lc} eq $tag->{tag_lc};
138             }
139             }
140             }
141              
142 4         20 return undef; # not matched
143             }
144              
145             1;
146             __END__