line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
3
|
|
|
3
|
|
1690484
|
use strict; |
|
3
|
|
|
|
|
9
|
|
|
3
|
|
|
|
|
79
|
|
2
|
3
|
|
|
3
|
|
17
|
use warnings; |
|
3
|
|
|
|
|
5
|
|
|
3
|
|
|
|
|
115
|
|
3
|
|
|
|
|
|
|
|
4
|
|
|
|
|
|
|
package Dancer2::Plugin::Negotiate; |
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
# ABSTRACT: Content negotiation plugin for Dancer2 |
7
|
|
|
|
|
|
|
|
8
|
3
|
|
|
3
|
|
2313
|
use Dancer2::Plugin; |
|
3
|
|
|
|
|
7476
|
|
|
3
|
|
|
|
|
22
|
|
9
|
|
|
|
|
|
|
|
10
|
3
|
|
|
3
|
|
15185
|
use HTTP::Negotiate; |
|
3
|
|
|
|
|
92
|
|
|
3
|
|
|
|
|
2086
|
|
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
our $VERSION = '0.002'; # VERSION |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
sub choose_variant { |
15
|
7
|
|
|
7
|
|
113713
|
my $dsl = shift; |
16
|
7
|
|
|
|
|
24
|
my $app = $dsl->app; |
17
|
7
|
|
|
|
|
18
|
my $variants = []; |
18
|
7
|
|
|
|
|
37
|
while ( my ( $variant, $options ) = ( shift, shift ) ) { |
19
|
24
|
100
|
66
|
|
|
136
|
last unless defined $variant and defined $options; |
20
|
|
|
|
|
|
|
push @$variants => [ |
21
|
|
|
|
|
|
|
$variant, $options->{Quality}, $options->{Type}, |
22
|
|
|
|
|
|
|
$options->{Encoding}, $options->{Charset}, $options->{Language}, |
23
|
|
|
|
|
|
|
$options->{Size} |
24
|
17
|
|
|
|
|
106
|
]; |
25
|
|
|
|
|
|
|
} |
26
|
7
|
|
|
|
|
60
|
HTTP::Negotiate::choose( $variants, $app->request->headers, ); |
27
|
|
|
|
|
|
|
} |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
sub apply_variant { |
30
|
5
|
|
|
5
|
|
112821
|
my ( $dsl, %variants ) = @_; |
31
|
5
|
|
|
|
|
23
|
my $app = $dsl->app; |
32
|
5
|
|
|
|
|
26
|
my $variant = scalar choose_variant(@_); |
33
|
5
|
50
|
|
|
|
1812
|
return unless defined $variant; |
34
|
5
|
|
|
|
|
7
|
my %options = %{ $variants{$variant} }; |
|
5
|
|
|
|
|
24
|
|
35
|
5
|
|
|
|
|
137
|
my $R = $app->response; |
36
|
5
|
100
|
|
|
|
78
|
$R->header( 'Content-Type' => $options{Type} ) if defined $options{Type}; |
37
|
|
|
|
|
|
|
$R->header( 'Content-Encoding' => $options{Encoding} ) |
38
|
5
|
50
|
|
|
|
1717
|
if defined $options{Encoding}; |
39
|
|
|
|
|
|
|
$R->header( 'Content-Charset' => $options{Charset} ) |
40
|
5
|
100
|
|
|
|
62
|
if defined $options{Charset}; |
41
|
|
|
|
|
|
|
$R->header( 'Content-Language' => $options{Language} ) |
42
|
5
|
100
|
|
|
|
239
|
if defined $options{Language}; |
43
|
5
|
|
|
|
|
557
|
$variant; |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
sub _langmap { |
47
|
0
|
|
50
|
0
|
|
0
|
my $grep = shift || sub { 1 }; |
|
3
|
|
|
3
|
|
16
|
|
48
|
3
|
|
50
|
|
|
15
|
my $langs = plugin_setting->{languages} || {}; |
49
|
|
|
|
|
|
|
return grep defined, map { |
50
|
3
|
50
|
|
|
|
550
|
my $opt = { |
|
6
|
50
|
|
|
|
38
|
|
51
|
|
|
|
|
|
|
Language => scalar( ref $_ eq 'HASH' ? ( keys %$_ )[0] : $_ ), |
52
|
|
|
|
|
|
|
Quality => scalar( ref $_ eq 'HASH' ? ( values %$_ )[0] : 1 ) |
53
|
|
|
|
|
|
|
}; |
54
|
6
|
|
|
|
|
15
|
my $id = lc $opt->{Language}; |
55
|
6
|
50
|
|
|
|
15
|
$grep->($opt) ? ( $id => $opt ) : undef; |
56
|
|
|
|
|
|
|
} @$langs; |
57
|
|
|
|
|
|
|
} |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
sub negotiate { |
60
|
3
|
|
|
3
|
|
124523
|
my ( $dsl, $tplname, @rest ) = @_; |
61
|
3
|
|
|
|
|
12
|
my $app = $dsl->app; |
62
|
3
|
|
|
|
|
13
|
my $engine = $app->engine('template'); |
63
|
|
|
|
|
|
|
my @langmap = _langmap( |
64
|
|
|
|
|
|
|
sub { |
65
|
6
|
|
|
6
|
|
14
|
my $lang = shift->{Language}; |
66
|
6
|
|
|
|
|
33
|
my $view = $engine->view_pathname( $tplname . '.' . $lang ); |
67
|
6
|
50
|
|
|
|
1223
|
defined $view and -e $view ? 1 : 0; |
|
|
50
|
|
|
|
|
|
68
|
|
|
|
|
|
|
} |
69
|
3
|
|
|
|
|
160
|
); |
70
|
3
|
|
|
|
|
24
|
my $lang = apply_variant( $dsl, 0, {}, @langmap ); |
71
|
3
|
100
|
|
|
|
18
|
$tplname .= '.' . $lang if $lang; |
72
|
3
|
50
|
|
|
|
7
|
if (wantarray) { |
73
|
3
|
|
|
|
|
22
|
return ( $tplname, @rest ); |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
else { |
76
|
0
|
|
|
|
|
|
return $tplname; |
77
|
|
|
|
|
|
|
} |
78
|
|
|
|
|
|
|
} |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
register |
81
|
|
|
|
|
|
|
choose_variant => \&choose_variant, |
82
|
|
|
|
|
|
|
{ is_global => 0 }; |
83
|
|
|
|
|
|
|
register |
84
|
|
|
|
|
|
|
apply_variant => \&apply_variant, |
85
|
|
|
|
|
|
|
{ is_global => 0 }; |
86
|
|
|
|
|
|
|
register |
87
|
|
|
|
|
|
|
negotiate => \&negotiate, |
88
|
|
|
|
|
|
|
{ is_global => 0 }; |
89
|
|
|
|
|
|
|
|
90
|
|
|
|
|
|
|
register_plugin; |
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
1; |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
__END__ |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
=pod |
97
|
|
|
|
|
|
|
|
98
|
|
|
|
|
|
|
=head1 NAME |
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
Dancer2::Plugin::Negotiate - Content negotiation plugin for Dancer2 |
101
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
=head1 VERSION |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
version 0.002 |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=head1 SYNOPSIS |
107
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
use Dancer2::Plugin::Negotiate; |
109
|
|
|
|
|
|
|
|
110
|
|
|
|
|
|
|
get '...' => sub { |
111
|
|
|
|
|
|
|
choose_variant( |
112
|
|
|
|
|
|
|
var1 => { |
113
|
|
|
|
|
|
|
Quality => 1.000, |
114
|
|
|
|
|
|
|
Type => 'text/html', |
115
|
|
|
|
|
|
|
Charset => 'iso-8859-1', |
116
|
|
|
|
|
|
|
Language => 'en', |
117
|
|
|
|
|
|
|
Size => 3000 |
118
|
|
|
|
|
|
|
}, |
119
|
|
|
|
|
|
|
var2 => { |
120
|
|
|
|
|
|
|
Quality => 0.950, |
121
|
|
|
|
|
|
|
Type => 'text/plain', |
122
|
|
|
|
|
|
|
Charset => 'us-ascii', |
123
|
|
|
|
|
|
|
Language => 'no', |
124
|
|
|
|
|
|
|
Size => 400 |
125
|
|
|
|
|
|
|
}, |
126
|
|
|
|
|
|
|
var3 => { |
127
|
|
|
|
|
|
|
Quality => 0.3, |
128
|
|
|
|
|
|
|
Type => 'image/gif', |
129
|
|
|
|
|
|
|
Size => 43555 |
130
|
|
|
|
|
|
|
} |
131
|
|
|
|
|
|
|
); # returns 'var1' or 'var2' or 'var3' or undef |
132
|
|
|
|
|
|
|
} |
133
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
=head1 DESCRIPTION |
135
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
This module is a wrapper for L<HTTP::Negotiate>. |
137
|
|
|
|
|
|
|
|
138
|
|
|
|
|
|
|
=head1 METHODS |
139
|
|
|
|
|
|
|
|
140
|
|
|
|
|
|
|
=head2 C<< choose_variant(%variants) >> |
141
|
|
|
|
|
|
|
|
142
|
|
|
|
|
|
|
C<%options> is a hash like this: |
143
|
|
|
|
|
|
|
|
144
|
|
|
|
|
|
|
%variants = ( |
145
|
|
|
|
|
|
|
$identifier => \%options |
146
|
|
|
|
|
|
|
) |
147
|
|
|
|
|
|
|
|
148
|
|
|
|
|
|
|
The key C<$identifier> is a string that will be returned by C<choose_variant()>. |
149
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
Valid keywords of hashref C<\%options>: |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=over 4 |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
=item Quality |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
A float point value between I<0.000> and I<1.000>, describing the source quality (defaults to 1) |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
=item Type |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
A MIME media type (with no charset attributes, but other attributes like I<version>) |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
=item Encoding |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
An encoding like I<gzip> or I<compress> |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
=item Charset |
167
|
|
|
|
|
|
|
|
168
|
|
|
|
|
|
|
An encoding like I<utf-8> or I<iso-8859-1> |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
=item Language |
171
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
A language tag conforming to RFC 3066 |
173
|
|
|
|
|
|
|
|
174
|
|
|
|
|
|
|
=item Size |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
Number of bytes used to represent |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
=back |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
Returns C<undef> if no variant matched. |
181
|
|
|
|
|
|
|
|
182
|
|
|
|
|
|
|
See L<HTTP::Negotiate> for more information. |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
=head2 C<< apply_variant(%options) >> |
185
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
This method behaves like C<choose_variant> but sets the according response headers if a variant matched. |
187
|
|
|
|
|
|
|
|
188
|
|
|
|
|
|
|
=head2 C<< negotiate($template_name) >> |
189
|
|
|
|
|
|
|
|
190
|
|
|
|
|
|
|
This method returns C<$template_name> with a suffixed language tag. The file needs to exist. This method behaves similiary to mod_negotiate of apache httpd. |
191
|
|
|
|
|
|
|
|
192
|
|
|
|
|
|
|
Language tags must be specified in plugin settings and ordered by priority: |
193
|
|
|
|
|
|
|
|
194
|
|
|
|
|
|
|
plugins: |
195
|
|
|
|
|
|
|
Negotiate: |
196
|
|
|
|
|
|
|
languages: |
197
|
|
|
|
|
|
|
- en |
198
|
|
|
|
|
|
|
- de |
199
|
|
|
|
|
|
|
- fr |
200
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
The result of this method can be propagated to C<template()> in order to render a localized version of the file. |
202
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
get '/index' => sub { |
204
|
|
|
|
|
|
|
return template negotiate 'index'; |
205
|
|
|
|
|
|
|
}; # renders index.de.tt or index.en.tt or index.fr.tt or index.tt |
206
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
Falls back to C<$template_name> if negotiaten fails. |
208
|
|
|
|
|
|
|
|
209
|
|
|
|
|
|
|
Hint: additional arguments applies to C<template()>: |
210
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
template negotiate index => { foo => 'bar' }; |
212
|
|
|
|
|
|
|
# is the same as |
213
|
|
|
|
|
|
|
template(negotiate('index'), { foo => 'bar' }); |
214
|
|
|
|
|
|
|
|
215
|
|
|
|
|
|
|
=head1 BUGS |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
Please report any bugs or feature requests on the bugtracker website |
218
|
|
|
|
|
|
|
https://github.com/zurborg/libdancer2-plugin-negotiate-perl/issues |
219
|
|
|
|
|
|
|
|
220
|
|
|
|
|
|
|
When submitting a bug or request, please include a test-file or a |
221
|
|
|
|
|
|
|
patch to an existing test-file that illustrates the bug or desired |
222
|
|
|
|
|
|
|
feature. |
223
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
=head1 AUTHOR |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
David Zurborg <zurborg@cpan.org> |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
=head1 COPYRIGHT AND LICENSE |
229
|
|
|
|
|
|
|
|
230
|
|
|
|
|
|
|
This software is Copyright (c) 2015 by David Zurborg. |
231
|
|
|
|
|
|
|
|
232
|
|
|
|
|
|
|
This is free software, licensed under: |
233
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
The ISC License |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
=cut |