acceptable 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,231 @@
1
+ # encoding: binary
2
+
3
+ require 'rack/acceptable/const'
4
+
5
+ module Rack #:nodoc:
6
+ module Acceptable #:nodoc:
7
+ module Utils
8
+
9
+ #--
10
+ # http://tools.ietf.org/html/rfc2616#section-2.1
11
+ # http://tools.ietf.org/html/rfc2616#section-2.2
12
+ # http://tools.ietf.org/html/rfc2616#section-3.9
13
+ #++
14
+
15
+ QUALITY_PATTERN = '\s*(?:;\s*q=(0(?:\.\d{0,3})?|1(?:\.0{0,3})?))?'.freeze
16
+ QVALUE_REGEX = /\A(?:0(?:\.\d{0,3})?|1(?:\.0{0,3})?)\z/.freeze
17
+ QUALITY_REGEX = /\s*;\s*q\s*=([^;\s]*)/i.freeze
18
+
19
+ QVALUE_DEFAULT = 1.00
20
+ QVALUE = 'q'.freeze
21
+
22
+ # see benchmarks/simple/split_bench.rb
23
+ if RUBY_VERSION < '1.9.1'
24
+
25
+ PAIR_SPLITTER = /\=/.freeze
26
+ HYPHEN_SPLITTER = /-/
27
+ COMMA_SPLITTER = /,/
28
+ SEMICOLON_SPLITTER = /;/.freeze
29
+
30
+ else
31
+
32
+ PAIR_SPLITTER = '='.freeze
33
+ HYPHEN_SPLITTER = Const::HYPHEN
34
+ COMMA_SPLITTER = Const::COMMA
35
+ SEMICOLON_SPLITTER = Const::SEMICOLON
36
+
37
+ end
38
+
39
+ COMMA_WS_SPLITTER = /,\s*/.freeze
40
+ TOKEN_PATTERN = "[A-Za-z0-9#{Regexp.escape('!#$&%\'*+-.^_`|~')}]+".freeze
41
+
42
+ module_function
43
+
44
+ # ==== Parameters
45
+ # header<String>::
46
+ # The 'Accept' request-header, one of:
47
+ # * Accept
48
+ # * Accept-Charset
49
+ # * Accept-Encoding
50
+ # * Accept-Language
51
+ #
52
+ # ==== Returns
53
+ # Result of parsing. An Array with entries (as a +Strings+) and
54
+ # associated quality factors (qvalues). Default qvalue is 1.0.
55
+ #
56
+ # ==== Raises
57
+ # ArgumentError:: There's a malformed qvalue in header.
58
+ #
59
+ # ==== Notes
60
+ # * It checks *only* quality factors (full syntactical inspection of
61
+ # the HTTP header is *not* a task of simple qvalues extractor).
62
+ # * It does *not* perform additional operations (downcase etc),
63
+ # thereto a bunch of more specific parsers is provided.
64
+ #
65
+ def extract_qvalues(header)
66
+ header.split(COMMA_WS_SPLITTER).map! { |entry|
67
+ QUALITY_REGEX === entry
68
+ thing = $` || entry
69
+ if !(qvalue = $1)
70
+ [thing, QVALUE_DEFAULT]
71
+ elsif QVALUE_REGEX === qvalue
72
+ [thing, qvalue.to_f]
73
+ else
74
+ raise ArgumentError, "Malformed quality factor: #{qvalue.inspect}"
75
+ end
76
+ }
77
+ end
78
+
79
+ #:stopdoc:
80
+
81
+ def parse_header(header, regex)
82
+ header.split(COMMA_SPLITTER).map! do |entry|
83
+ raise unless regex === entry
84
+ [$1, ($2 || QVALUE_DEFAULT).to_f]
85
+ end
86
+ end
87
+
88
+ #:startdoc:
89
+
90
+ HTTP_ACCEPT_TOKEN_REGEX = /^\s*(#{Utils::TOKEN_PATTERN})#{Utils::QUALITY_PATTERN}\s*$/o.freeze
91
+
92
+ module_function
93
+
94
+ # ==== Parameters
95
+ # provides<Array>:: The Array of available Content-Codings. Could be empty.
96
+ # accepts<Array>:: The Array of acceptable Content-Codings. Could be empty.
97
+ #
98
+ # ==== Returns
99
+ # The best one of available Content-Codings (as a +String+) or +nil+.
100
+ #
101
+ # ==== Notes
102
+ # Available and acceptable Content-Codings are supposed to be *downcased*
103
+ # According to section 3.5 of RFC 2616, Content-Codings are *case-insensitive*.
104
+ #
105
+ def detect_best_encoding(provides, accepts)
106
+ return nil if provides.empty?
107
+
108
+ identity = provides.include?(Const::IDENTITY) # presence of 'identity' in available content-codings
109
+ identity_or_wildcard = true # absence of explicit 'identity;q=0' or '*;q=0'
110
+
111
+ # RFC 2616, sec. 14.3:
112
+ # If no Accept-Encoding field is present in a request, the server
113
+ # MAY assume that the client will accept any content coding. In this
114
+ # case, if "identity" is one of the available content-codings, then
115
+ # the server SHOULD use the "identity" content-coding, unless it has
116
+ # additional information that a different content-coding is meaningful
117
+ # to the client.
118
+
119
+ return Const::IDENTITY if identity && accepts.empty?
120
+ #return (identity ? Const::IDENTITY : provides.first) if accepts.empty?
121
+
122
+ # RFC 2616, sec. 14.3:
123
+ # The "identity" content-coding is always acceptable, unless
124
+ # specifically refused because the Accept-Encoding field includes
125
+ # "identity;q=0", or because the field includes "*;q=0" and does
126
+ # not explicitly include the "identity" content-coding. If the
127
+ # Accept-Encoding field-value is empty, then only the "identity"
128
+ # encoding is acceptable.
129
+
130
+ candidates = []
131
+ expansion = nil
132
+ i = 0
133
+
134
+ accepts.sort_by { |_,q| [-q,i+=1] }.each do |c,q|
135
+ if q == 0
136
+ identity_or_wildcard = false if c == Const::IDENTITY || c == Const::WILDCARD
137
+ elsif c == Const::WILDCARD
138
+ expansion ||= provides - accepts.map { |c| c.first }
139
+ candidates.concat expansion
140
+ else
141
+ candidates << c
142
+ end
143
+ end
144
+
145
+ (candidates & provides).first ||
146
+ (Const::IDENTITY if identity && identity_or_wildcard) ||
147
+ nil
148
+ end
149
+
150
+ # ==== Parameters
151
+ # provides<Array>:: The Array of available Charsets. Could be empty.
152
+ # accepts<Array>:: The Array of acceptable Charsets. Could be empty.
153
+ #
154
+ # ==== Returns
155
+ # The best one of available Charsets (as a +String+) or +nil+.
156
+ #
157
+ # ==== Notes
158
+ # Available and acceptable Charsets are supposed to be *downcased*.
159
+ # According to section 3.4 of RFC 2616, Charsets are *case-insensitive*.
160
+ #
161
+ def detect_best_charset(provides, accepts)
162
+ return nil if provides.empty?
163
+
164
+ # RFC 2616, sec 14.2:
165
+ # If no Accept-Charset header is present, the default is that any
166
+ # character set is acceptable. If an Accept-Charset header is present,
167
+ # and if the server cannot send a response which is acceptable
168
+ # according to the Accept-Charset header, then the server SHOULD send
169
+ # an error response with the 406 (not acceptable) status code, though
170
+ # the sending of an unacceptable response is also allowed.
171
+
172
+ return provides.first if accepts.empty?
173
+
174
+ expansion = nil
175
+ candidates = []
176
+ i = 0
177
+
178
+ accepts << [Const::ISO_8859_1, 1.0] unless accepts.assoc(Const::ISO_8859_1) || accepts.assoc(Const::WILDCARD)
179
+
180
+ accepts.sort_by { |_,q| [-q,i+=1] }.each do |c,q|
181
+
182
+ next if q == 0
183
+
184
+ if c == Const::WILDCARD
185
+
186
+ # RFC 2616, sec 14.2:
187
+ # The special value "*", if present in the Accept-Charset field,
188
+ # matches every character set (including ISO-8859-1) which is not
189
+ # mentioned elsewhere in the Accept-Charset field. If no "*" is present
190
+ # in an Accept-Charset field, then all character sets not explicitly
191
+ # mentioned get a quality value of 0, except for ISO-8859-1, which gets
192
+ # a quality value of 1 if not explicitly mentioned.
193
+
194
+ expansion ||= provides - accepts.map { |c,_| c }
195
+ candidates.concat expansion
196
+ else
197
+ candidates << c
198
+ end
199
+ end
200
+
201
+ (candidates & provides).first
202
+ end
203
+
204
+ #--
205
+ # RFC 2616, sec. 3.10:
206
+ # White space is not allowed within the tag and all tags are case-
207
+ # insensitive.
208
+ #
209
+ # RFC 4647, sec. 2.1
210
+ # Note that the ABNF [RFC4234] in [RFC2616] is incorrect, since it disallows the
211
+ # use of digits anywhere in the 'language-range' (see [RFC2616errata]).
212
+ #++
213
+
214
+ HTTP_ACCEPT_LANGUAGE_REGEX = /^\s*(\*|[a-z]{1,8}(?:-[a-z\d]{1,8}|-\*)*)#{Utils::QUALITY_PATTERN}\s*$/io.freeze
215
+
216
+ def normalize_header(header)
217
+ ret = header.strip
218
+ ret.gsub!(/\s*(?:,\s*)+/, Const::COMMA)
219
+ ret.gsub!(/^,|,$/, Const::EMPTY_STRING)
220
+ ret
221
+ end
222
+
223
+ def blank?(s)
224
+ s.empty? || s.strip.empty?
225
+ end
226
+
227
+ end
228
+ end
229
+ end
230
+
231
+ # EOF
@@ -0,0 +1,7 @@
1
+ module Rack #:nodoc:
2
+ module Acceptable #:nodoc:
3
+ VERSION = '0.2.1'
4
+ end
5
+ end
6
+
7
+ # EOF
@@ -0,0 +1,27 @@
1
+ require 'rubygems'
2
+ gem 'rack', '>=1.0.0'
3
+ require 'rack'
4
+
5
+ module Rack #:nodoc:
6
+ module Acceptable
7
+
8
+ # common
9
+ autoload :Const , 'rack/acceptable/const'
10
+ autoload :Utils , 'rack/acceptable/utils'
11
+ autoload :MIMETypes , 'rack/acceptable/mimetypes'
12
+ autoload :LanguageTag , 'rack/acceptable/language_tag'
13
+
14
+ # request and mixins
15
+ autoload :Headers , 'rack/acceptable/mixin/headers'
16
+ autoload :Media , 'rack/acceptable/mixin/media'
17
+ autoload :Request , 'rack/acceptable/request'
18
+
19
+ # middleware
20
+ autoload :Formats , 'rack/acceptable/middleware/formats'
21
+ autoload :Provides , 'rack/acceptable/middleware/provides'
22
+ autoload :FakeAccept , 'rack/acceptable/middleware/fake_accept'
23
+
24
+ end
25
+ end
26
+
27
+ # EOF
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: acceptable
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ - 1
9
+ version: 0.2.1
10
+ platform: ruby
11
+ authors:
12
+ - SSDany
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-04-16 00:00:00 +04:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rack
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 0
30
+ - 0
31
+ version: 1.0.0
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ description: HTTP Accept parsers for Rack.
35
+ email: inadsence@gmail.com
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files:
41
+ - README.rdoc
42
+ files:
43
+ - README.rdoc
44
+ - lib/rack/acceptable/const.rb
45
+ - lib/rack/acceptable/language_tag.rb
46
+ - lib/rack/acceptable/middleware/formats.rb
47
+ - lib/rack/acceptable/middleware/provides.rb
48
+ - lib/rack/acceptable/middleware/fake_accept.rb
49
+ - lib/rack/acceptable/mimetypes.rb
50
+ - lib/rack/acceptable/mixin/headers.rb
51
+ - lib/rack/acceptable/mixin/media.rb
52
+ - lib/rack/acceptable/request.rb
53
+ - lib/rack/acceptable/utils.rb
54
+ - lib/rack/acceptable/version.rb
55
+ - lib/rack/acceptable.rb
56
+ - lib/rack/acceptable/data/mime.types
57
+ has_rdoc: true
58
+ homepage: http://github.com/SSDany/rack-acceptable
59
+ licenses: []
60
+
61
+ post_install_message:
62
+ rdoc_options: []
63
+
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ requirements: []
81
+
82
+ rubyforge_project: acceptable
83
+ rubygems_version: 1.3.6
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: HTTP Accept parsers for Rack.
87
+ test_files: []
88
+