acceptable 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+