accept_headers 0.0.4 → 0.0.5
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/Gemfile +1 -1
- data/README.md +27 -6
- data/accept_headers.gemspec +1 -1
- data/lib/accept_headers/acceptable.rb +19 -2
- data/lib/accept_headers/encoding.rb +11 -3
- data/lib/accept_headers/encoding/negotiator.rb +2 -2
- data/lib/accept_headers/language.rb +15 -5
- data/lib/accept_headers/language/negotiator.rb +1 -3
- data/lib/accept_headers/media_type.rb +10 -4
- data/lib/accept_headers/media_type/negotiator.rb +1 -2
- data/lib/accept_headers/negotiatable.rb +9 -5
- data/lib/accept_headers/version.rb +1 -1
- data/spec/encoding/negotiator_spec.rb +13 -0
- data/spec/encoding_spec.rb +68 -0
- data/spec/language_spec.rb +121 -2
- data/spec/media_type/negotiator_spec.rb +17 -5
- data/spec/media_type_spec.rb +112 -0
- data/spec/spec_helper.rb +6 -4
- data/spec/support/encodings/content-coding.csv +12 -0
- data/spec/support/{application.csv → media_types/application.csv} +0 -0
- data/spec/support/{audio.csv → media_types/audio.csv} +1 -1
- data/spec/support/{image.csv → media_types/image.csv} +0 -0
- data/spec/support/{message.csv → media_types/message.csv} +0 -0
- data/spec/support/{model.csv → media_types/model.csv} +0 -0
- data/spec/support/{multipart.csv → media_types/multipart.csv} +0 -0
- data/spec/support/{text.csv → media_types/text.csv} +0 -0
- data/spec/support/{video.csv → media_types/video.csv} +0 -0
- metadata +21 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4df1427bbf8cc170c8d3eb303c6978f4c03d4b3e
|
4
|
+
data.tar.gz: 7efc9d1f82cfd666507f72fae4b09e7020516970
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 339ce5ee8e97554e7d890179f6362e279d1933deec79f351c617ada281415d1f30e80f92080cb4eebe20e56a5436b2b5cd5ecc663940a65b77ee9697252dd165
|
7
|
+
data.tar.gz: 899d66e7efd0d9479adbc800de51d6abc3f3cb8d4c7950bf0ce1de344f7218757b5936d0d7814bbbdeb640daaa4faaf068b3a7fa39b879e1a4a6c0770040563c
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
## HEAD
|
2
|
+
|
3
|
+
## 0.0.5 / November 16, 2014
|
4
|
+
|
5
|
+
* Add `#accept?` and `#reject?` methods to all negotiators.
|
6
|
+
* Add `#accept?` method to all negotiators.
|
7
|
+
* Return nil if no matches on `#negotiate`.
|
8
|
+
* Fix matching logic in `MediaType`, `Encoding`, and `Language`.
|
9
|
+
* Test all IANA registered encodings against the parser.
|
10
|
+
* Fix `simplecov` loading.
|
11
|
+
* Update `audio.csv` media type file with typo fix.
|
12
|
+
* More specs.
|
13
|
+
|
1
14
|
## 0.0.4 / November 15, 2014
|
2
15
|
|
3
16
|
* Add MediaType#media_range which is the type/subtype as a string.
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -63,16 +63,22 @@ media_types.list
|
|
63
63
|
]
|
64
64
|
```
|
65
65
|
|
66
|
-
`#negotiate` takes a string of media types supported (by your API or route/controller) and returns the best match as a `MediaType`. This will first check the available list for any matching media types with a `q` of 0 and return `nil` if there is a match. Then it'll look to the highest `q` values and look for matches in descending `q` value order and return the first match
|
66
|
+
`#negotiate` takes a string of media types supported (by your API or route/controller) and returns the best match as a `MediaType`. This will first check the available list for any matching media types with a `q` of 0 and return `nil` if there is a match. Then it'll look to the highest `q` values and look for matches in descending `q` value order and return the first match (accounting for wildcards). Finally, if there are no matches, it returns `nil`.
|
67
67
|
|
68
68
|
```ruby
|
69
|
-
|
69
|
+
media_types.negotiate('text/html')
|
70
70
|
|
71
71
|
# Returns:
|
72
72
|
|
73
73
|
AcceptHeaders::MediaType.new('text', 'html', params: { 'level' => '1' })
|
74
74
|
```
|
75
75
|
|
76
|
+
`#accept?`:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
media_types.accept?('text/html') # true
|
80
|
+
```
|
81
|
+
|
76
82
|
### Accept-Encoding
|
77
83
|
|
78
84
|
`AcceptHeader::Charset::Encoding`:
|
@@ -86,7 +92,6 @@ encodings.list
|
|
86
92
|
|
87
93
|
[
|
88
94
|
AcceptHeaders::Encoding.new('gzip'),
|
89
|
-
AcceptHeaders::Encoding.new('identity'),
|
90
95
|
AcceptHeaders::Encoding.new('compress', q: 0.8),
|
91
96
|
AcceptHeaders::Encoding.new('deflate', q: 0.5)
|
92
97
|
]
|
@@ -95,11 +100,21 @@ encodings.list
|
|
95
100
|
`#negotiate`:
|
96
101
|
|
97
102
|
```ruby
|
98
|
-
encodings.negotiate('
|
103
|
+
encodings.negotiate('gzip')
|
99
104
|
|
100
105
|
# Returns:
|
101
106
|
|
102
|
-
AcceptHeaders::Encoding.new('
|
107
|
+
AcceptHeaders::Encoding.new('gzip')
|
108
|
+
```
|
109
|
+
|
110
|
+
`#accept?`:
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
encodings.accept?('gzip') # true
|
114
|
+
|
115
|
+
# Identity is accepted as long as it's not explicitly rejected 'identity;q=0'
|
116
|
+
|
117
|
+
encodings.accept?('identity') # true
|
103
118
|
```
|
104
119
|
|
105
120
|
### Accept-Language
|
@@ -130,7 +145,13 @@ languages.negotiate('en-us')
|
|
130
145
|
AcceptHeaders::Language.new('en', 'us')
|
131
146
|
```
|
132
147
|
|
133
|
-
|
148
|
+
`#accept?`:
|
149
|
+
|
150
|
+
```ruby
|
151
|
+
languages.accept?('en-gb') # true
|
152
|
+
```
|
153
|
+
|
154
|
+
## TODO
|
134
155
|
|
135
156
|
* Write rack middleware
|
136
157
|
* More edge case tests
|
data/accept_headers.gemspec
CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
|
|
9
9
|
spec.authors = ["Jack Chu"]
|
10
10
|
spec.email = ["kamuigt@gmail.com"]
|
11
11
|
spec.summary = %q{A ruby library that does content negotiation and parses and sorts http accept headers.}
|
12
|
-
spec.description = %q{
|
12
|
+
spec.description = %q{A ruby library that does content negotiation and parses and sorts http accept headers. Adheres to RFC 2616.}
|
13
13
|
spec.homepage = ""
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
@@ -5,8 +5,20 @@ module AcceptHeaders
|
|
5
5
|
|
6
6
|
attr_reader :q
|
7
7
|
|
8
|
-
def
|
9
|
-
|
8
|
+
def reject?(other)
|
9
|
+
if q != 0.0
|
10
|
+
false
|
11
|
+
else
|
12
|
+
match(other)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def accept?(other)
|
17
|
+
if q == 0.0
|
18
|
+
false
|
19
|
+
else
|
20
|
+
match(other)
|
21
|
+
end
|
10
22
|
end
|
11
23
|
|
12
24
|
def q=(value)
|
@@ -23,5 +35,10 @@ module AcceptHeaders
|
|
23
35
|
end
|
24
36
|
@q = q_float
|
25
37
|
end
|
38
|
+
|
39
|
+
private
|
40
|
+
def match(other)
|
41
|
+
raise NotImplementedError.new("#match is not implemented")
|
42
|
+
end
|
26
43
|
end
|
27
44
|
end
|
@@ -7,6 +7,8 @@ module AcceptHeaders
|
|
7
7
|
|
8
8
|
attr_reader :encoding
|
9
9
|
|
10
|
+
ENCODING_PATTERN = /^\s*(?<encoding>[\w!#$%^&*\-\+{}\\|'.`~]+)\s*$/
|
11
|
+
|
10
12
|
def initialize(encoding = '*', q: 1.0)
|
11
13
|
self.encoding = encoding
|
12
14
|
self.q = q
|
@@ -32,10 +34,16 @@ module AcceptHeaders
|
|
32
34
|
"#{encoding};q=#{qvalue}"
|
33
35
|
end
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
+
private
|
38
|
+
def match(encoding_string)
|
39
|
+
match_data = ENCODING_PATTERN.match(encoding_string)
|
40
|
+
if !match_data
|
41
|
+
false
|
42
|
+
elsif encoding == match_data[:encoding]
|
43
|
+
true
|
44
|
+
elsif match_data[:encoding] == 'identity'
|
37
45
|
true
|
38
|
-
elsif
|
46
|
+
elsif encoding == '*'
|
39
47
|
true
|
40
48
|
else
|
41
49
|
false
|
@@ -16,9 +16,9 @@ module AcceptHeaders
|
|
16
16
|
header.split(',').each do |entry|
|
17
17
|
encoding_arr = entry.split(';', 2)
|
18
18
|
next if encoding_arr[0].nil?
|
19
|
-
encoding =
|
19
|
+
encoding = Encoding::ENCODING_PATTERN.match(encoding_arr[0])
|
20
20
|
next if encoding.nil?
|
21
|
-
encodings << Encoding.new(encoding[:
|
21
|
+
encodings << Encoding.new(encoding[:encoding], q: parse_q(encoding_arr[1]))
|
22
22
|
end
|
23
23
|
encodings.sort! { |x,y| y <=> x }
|
24
24
|
end
|
@@ -7,6 +7,8 @@ module AcceptHeaders
|
|
7
7
|
|
8
8
|
attr_reader :primary_tag, :subtag, :params
|
9
9
|
|
10
|
+
LANGUAGE_TAG_PATTERN = /^\s*(?<primary_tag>[\w]{1,8}|\*)(?:\s*\-\s*(?<subtag>[\w]{1,8}|\*))?\s*$/
|
11
|
+
|
10
12
|
def initialize(primary_tag = '*', subtag = nil, q: 1.0)
|
11
13
|
self.primary_tag = primary_tag
|
12
14
|
self.subtag = subtag
|
@@ -53,15 +55,23 @@ module AcceptHeaders
|
|
53
55
|
end
|
54
56
|
|
55
57
|
def language_tag
|
56
|
-
|
58
|
+
if primary_tag == '*' && (subtag.nil? || subtag == '*')
|
59
|
+
'*'
|
60
|
+
else
|
61
|
+
"#{primary_tag}-#{subtag}"
|
62
|
+
end
|
57
63
|
end
|
58
64
|
|
59
|
-
|
60
|
-
|
65
|
+
private
|
66
|
+
def match(language_tag_string)
|
67
|
+
match_data = LANGUAGE_TAG_PATTERN.match(language_tag_string)
|
68
|
+
if !match_data
|
69
|
+
false
|
70
|
+
elsif primary_tag == match_data[:primary_tag] && subtag == match_data[:subtag]
|
61
71
|
true
|
62
|
-
elsif primary_tag ==
|
72
|
+
elsif primary_tag == match_data[:primary_tag] && subtag == '*'
|
63
73
|
true
|
64
|
-
elsif
|
74
|
+
elsif primary_tag == '*'
|
65
75
|
true
|
66
76
|
else
|
67
77
|
false
|
@@ -6,8 +6,6 @@ module AcceptHeaders
|
|
6
6
|
class Negotiator
|
7
7
|
include Negotiatable
|
8
8
|
|
9
|
-
LANGUAGE_PATTERN = /^\s*(?<primary_tag>[\w]{1,8}|\*)(?:\s*\-\s*(?<subtag>[\w]{1,8}|\*))?\s*$/
|
10
|
-
|
11
9
|
private
|
12
10
|
def parse(original_header)
|
13
11
|
header = original_header.dup
|
@@ -18,7 +16,7 @@ module AcceptHeaders
|
|
18
16
|
header.split(',').each do |entry|
|
19
17
|
language_arr = entry.split(';', 2)
|
20
18
|
next if language_arr[0].nil?
|
21
|
-
language_range =
|
19
|
+
language_range = Language::LANGUAGE_TAG_PATTERN.match(language_arr[0])
|
22
20
|
next if language_range.nil?
|
23
21
|
begin
|
24
22
|
languages << Language.new(
|
@@ -7,6 +7,8 @@ module AcceptHeaders
|
|
7
7
|
|
8
8
|
attr_reader :type, :subtype, :params
|
9
9
|
|
10
|
+
MEDIA_TYPE_PATTERN = /^\s*(?<type>[\w!#$%^&*\-\+{}\\|'.`~]+)(?:\s*\/\s*(?<subtype>[\w!#$%^&*\-\+{}\\|'.`~]+))?\s*$/
|
11
|
+
|
10
12
|
def initialize(type = '*', subtype = '*', q: 1.0, params: {})
|
11
13
|
self.type = type
|
12
14
|
self.subtype = subtype
|
@@ -74,12 +76,16 @@ module AcceptHeaders
|
|
74
76
|
"#{type}/#{subtype}"
|
75
77
|
end
|
76
78
|
|
77
|
-
|
78
|
-
|
79
|
+
private
|
80
|
+
def match(media_range_string)
|
81
|
+
match_data = MEDIA_TYPE_PATTERN.match(media_range_string)
|
82
|
+
if !match_data
|
83
|
+
false
|
84
|
+
elsif type == match_data[:type] && subtype == match_data[:subtype]
|
79
85
|
true
|
80
|
-
elsif type ==
|
86
|
+
elsif type == match_data[:type] && subtype == '*'
|
81
87
|
true
|
82
|
-
elsif
|
88
|
+
elsif type == '*' && subtype == '*'
|
83
89
|
true
|
84
90
|
else
|
85
91
|
false
|
@@ -7,7 +7,6 @@ module AcceptHeaders
|
|
7
7
|
include Negotiatable
|
8
8
|
|
9
9
|
private
|
10
|
-
MEDIA_TYPE_PATTERN = /^\s*(?<type>[\w!#$%^&*\-\+{}\\|'.`~]+)(?:\s*\/\s*(?<subtype>[\w!#$%^&*\-\+{}\\|'.`~]+))?\s*$/
|
11
10
|
PARAM_PATTERN = /(?<attribute>[\w!#$%^&*\-\+{}\\|'.`~]+)\s*\=\s*(?:\"(?<value>[^"]*)\"|\'(?<value>[^']*)\'|(?<value>[\w!#$%^&*\-\+{}\\|\'.`~]*))/
|
12
11
|
|
13
12
|
def parse(original_header)
|
@@ -19,7 +18,7 @@ module AcceptHeaders
|
|
19
18
|
header.split(',').each do |entry|
|
20
19
|
accept_media_range, accept_params = entry.split(';', 2)
|
21
20
|
next if accept_media_range.nil?
|
22
|
-
media_range = MEDIA_TYPE_PATTERN.match(accept_media_range)
|
21
|
+
media_range = MediaType::MEDIA_TYPE_PATTERN.match(accept_media_range)
|
23
22
|
next if media_range.nil?
|
24
23
|
begin
|
25
24
|
media_types << MediaType.new(
|
@@ -3,7 +3,6 @@ module AcceptHeaders
|
|
3
3
|
class Error < StandardError; end
|
4
4
|
class ParseError < Error; end
|
5
5
|
|
6
|
-
TOKEN_PATTERN = /^\s*(?<token>[\w!#$%^&*\-\+{}\\|'.`~]+)\s*$/
|
7
6
|
Q_PATTERN = /(?:\A|;)\s*(?<exists>qs*\=)\s*(?:(?<q>0\.\d{1,3}|[01])|(?:[^;]*))\s*(?:\z|;)/
|
8
7
|
|
9
8
|
attr_reader :list
|
@@ -12,24 +11,29 @@ module AcceptHeaders
|
|
12
11
|
@list = parse(header)
|
13
12
|
end
|
14
13
|
|
15
|
-
def negotiate(
|
16
|
-
supported = parse(supported_string)
|
14
|
+
def negotiate(supported)
|
17
15
|
return nil if @list.empty?
|
16
|
+
supported = [*supported]
|
18
17
|
rejects, acceptable = @list.partition { |m| m.q == 0.0 }
|
19
18
|
rejects.each do |reject|
|
20
19
|
supported.each do |support|
|
21
|
-
if
|
20
|
+
if reject.reject?(support)
|
22
21
|
return nil
|
23
22
|
end
|
24
23
|
end
|
25
24
|
end
|
26
25
|
acceptable.sort { |x,y| y <=> x }.each do |accepted|
|
27
26
|
supported.each do |support|
|
28
|
-
if
|
27
|
+
if accepted.accept?(support)
|
29
28
|
return accepted
|
30
29
|
end
|
31
30
|
end
|
32
31
|
end
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def accept?(other)
|
36
|
+
negotiate(other) ? true : false
|
33
37
|
end
|
34
38
|
|
35
39
|
private
|
@@ -22,6 +22,19 @@ module AcceptHeaders
|
|
22
22
|
]
|
23
23
|
end
|
24
24
|
|
25
|
+
it "supports all registered IANA encodings" do
|
26
|
+
require 'csv'
|
27
|
+
# https://www.iana.org/assignments/http-parameters/http-parameters.xml#content-coding
|
28
|
+
CSV.foreach("spec/support/encodings/content-coding.csv", headers: true) do |row|
|
29
|
+
encoding = row['Name']
|
30
|
+
|
31
|
+
if encoding
|
32
|
+
subject.new(encoding).list.size.must_equal 1
|
33
|
+
subject.new(encoding).list.first.encoding.must_equal encoding.downcase
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
25
38
|
it "sets encoding to * when the accept-encoding header is empty" do
|
26
39
|
subject.new('').list.must_equal [
|
27
40
|
Encoding.new('*')
|
data/spec/encoding_spec.rb
CHANGED
@@ -68,5 +68,73 @@ module AcceptHeaders
|
|
68
68
|
s = subject.new('gzip', q: 0.9).to_s
|
69
69
|
s.must_equal "gzip;q=0.9"
|
70
70
|
end
|
71
|
+
|
72
|
+
describe "#accept?" do
|
73
|
+
it "accepted if the encoding is the same" do
|
74
|
+
a = subject.new('gzip')
|
75
|
+
a.accept?('gzip').must_equal true
|
76
|
+
b = subject.new('gzip', q: 0.001)
|
77
|
+
b.accept?('gzip').must_equal true
|
78
|
+
end
|
79
|
+
|
80
|
+
it "accepted if the encoding is *" do
|
81
|
+
a = subject.new('*')
|
82
|
+
a.accept?('gzip').must_equal true
|
83
|
+
b = subject.new('*', q: 0.1)
|
84
|
+
b.accept?('gzip').must_equal true
|
85
|
+
end
|
86
|
+
|
87
|
+
it "not accepted if the encoding doesn't match" do
|
88
|
+
a = subject.new('gzip')
|
89
|
+
a.accept?('compress').must_equal false
|
90
|
+
b = subject.new('gzip', q: 0.4)
|
91
|
+
b.accept?('compress').must_equal false
|
92
|
+
end
|
93
|
+
|
94
|
+
it "not accepted if q is 0" do
|
95
|
+
a = subject.new('gzip', q: 0)
|
96
|
+
a.accept?('gzip').must_equal false
|
97
|
+
b = subject.new('*', q: 0)
|
98
|
+
b.accept?('gzip').must_equal false
|
99
|
+
end
|
100
|
+
|
101
|
+
# TODO: test *
|
102
|
+
# it "not accepted if..." do
|
103
|
+
# a = subject.new('gzip')
|
104
|
+
# a.accept?('*').must_equal true
|
105
|
+
# end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe "#reject?" do
|
109
|
+
describe "given q is 0" do
|
110
|
+
it "rejected if the encoding is the same" do
|
111
|
+
a = subject.new('gzip', q: 0)
|
112
|
+
a.reject?('gzip').must_equal true
|
113
|
+
end
|
114
|
+
|
115
|
+
it "rejected if the encoding is *" do
|
116
|
+
a = subject.new('*', q: 0)
|
117
|
+
a.reject?('gzip').must_equal true
|
118
|
+
end
|
119
|
+
|
120
|
+
it "not rejected if the encoding doesn't match" do
|
121
|
+
a = subject.new('gzip', q: 0)
|
122
|
+
a.reject?('compress').must_equal false
|
123
|
+
end
|
124
|
+
|
125
|
+
# TODO: test *
|
126
|
+
# it "not rejected if..." do
|
127
|
+
# a = subject.new('gzip', q: 0)
|
128
|
+
# a.reject?('*').must_equal true
|
129
|
+
# end
|
130
|
+
end
|
131
|
+
|
132
|
+
it "not rejected if q > 0" do
|
133
|
+
a = subject.new('gzip', q: 0.001)
|
134
|
+
a.reject?('gzip').must_equal false
|
135
|
+
b = subject.new('*', q: 0.9)
|
136
|
+
b.reject?('gzip').must_equal false
|
137
|
+
end
|
138
|
+
end
|
71
139
|
end
|
72
140
|
end
|
data/spec/language_spec.rb
CHANGED
@@ -87,8 +87,127 @@ module AcceptHeaders
|
|
87
87
|
s.must_equal "en-us;q=0.9"
|
88
88
|
end
|
89
89
|
|
90
|
-
|
91
|
-
|
90
|
+
describe "#language_tag" do
|
91
|
+
it "outputs the language tag" do
|
92
|
+
subject.new('en', 'us', q: 0.9).language_tag.must_equal "en-us"
|
93
|
+
end
|
94
|
+
|
95
|
+
it "if primary tag is * and subtag is * or nil, outputs *" do
|
96
|
+
subject.new('*', nil).language_tag.must_equal '*'
|
97
|
+
subject.new('*', '*').language_tag.must_equal '*'
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe "#accept?" do
|
102
|
+
it "accepted if the primary_tag and subtag are the same" do
|
103
|
+
a = subject.new('en', 'us')
|
104
|
+
a.accept?('en-us').must_equal true
|
105
|
+
b = subject.new('en', 'us', q: 0.001)
|
106
|
+
b.accept?('en-us').must_equal true
|
107
|
+
end
|
108
|
+
|
109
|
+
it "accepted if the primary_tag is the same and the other subtag is *" do
|
110
|
+
a = subject.new('en', '*')
|
111
|
+
a.accept?('en-us').must_equal true
|
112
|
+
b = subject.new('en', '*', q: 0.9)
|
113
|
+
b.accept?('en-us').must_equal true
|
114
|
+
end
|
115
|
+
|
116
|
+
it "accepted if the primary_tag and subtag are *" do
|
117
|
+
a = subject.new('*')
|
118
|
+
a.accept?('en-us').must_equal true
|
119
|
+
b = subject.new('*', q: 0.1)
|
120
|
+
b.accept?('en-us').must_equal true
|
121
|
+
end
|
122
|
+
|
123
|
+
it "not accepted if the primary_tag and subtag don't match" do
|
124
|
+
a = subject.new('en', 'us')
|
125
|
+
a.accept?('en-gb').must_equal false
|
126
|
+
b = subject.new('en', 'us', q: 0.2)
|
127
|
+
b.accept?('en-gb').must_equal false
|
128
|
+
end
|
129
|
+
|
130
|
+
it "not accepted if the primary_tag doesn't match" do
|
131
|
+
a = subject.new('en', 'us')
|
132
|
+
a.accept?('zh-us').must_equal false
|
133
|
+
b = subject.new('en', 'us', q: 0.4)
|
134
|
+
b.accept?('zh-us').must_equal false
|
135
|
+
end
|
136
|
+
|
137
|
+
it "not accepted if the subtag doesn't match" do
|
138
|
+
a = subject.new('en', 'us')
|
139
|
+
a.accept?('en-gb').must_equal false
|
140
|
+
b = subject.new('en', 'us', q: 0.6)
|
141
|
+
b.accept?('en-gb').must_equal false
|
142
|
+
end
|
143
|
+
|
144
|
+
it "not accepted if q is 0" do
|
145
|
+
a = subject.new('en', 'us', q: 0)
|
146
|
+
a.accept?('en-us').must_equal false
|
147
|
+
a.accept?('en-gb').must_equal false
|
148
|
+
a.accept?('zh-us').must_equal false
|
149
|
+
b = subject.new('en', '*', q: 0)
|
150
|
+
b.accept?('en-us').must_equal false
|
151
|
+
c = subject.new('*', q: 0)
|
152
|
+
c.accept?('en-us').must_equal false
|
153
|
+
end
|
154
|
+
|
155
|
+
# TODO: test *
|
156
|
+
it "not accepted if..." do
|
157
|
+
a = subject.new('en', 'us')
|
158
|
+
a.accept?('*').must_equal false
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe "#reject?" do
|
163
|
+
describe "given q is 0" do
|
164
|
+
it "rejected if the primary_tag and subtag are the same" do
|
165
|
+
a = subject.new('en', 'us', q: 0)
|
166
|
+
a.reject?('en-us').must_equal true
|
167
|
+
end
|
168
|
+
|
169
|
+
it "rejected if the primary_tag is the same and the other subtag is *" do
|
170
|
+
a = subject.new('en', '*', q: 0)
|
171
|
+
a.reject?('en-us').must_equal true
|
172
|
+
end
|
173
|
+
|
174
|
+
it "rejected if the primary_tag and subtag are *" do
|
175
|
+
a = subject.new('*', q: 0)
|
176
|
+
a.reject?('en-us').must_equal true
|
177
|
+
end
|
178
|
+
|
179
|
+
it "not rejected if the primary_tag and subtag don't match" do
|
180
|
+
a = subject.new('en', 'us', q: 0)
|
181
|
+
a.reject?('en-gb').must_equal false
|
182
|
+
end
|
183
|
+
|
184
|
+
it "not rejected if the primary_tag doesn't match" do
|
185
|
+
a = subject.new('en', 'us', q: 0)
|
186
|
+
a.reject?('zh-us').must_equal false
|
187
|
+
end
|
188
|
+
|
189
|
+
it "not rejected if the subtag doesn't match" do
|
190
|
+
a = subject.new('en', 'us', q: 0)
|
191
|
+
a.reject?('en-gb').must_equal false
|
192
|
+
end
|
193
|
+
|
194
|
+
# TODO: test *
|
195
|
+
it "not rejected if..." do
|
196
|
+
a = subject.new('en', 'us', q: 0)
|
197
|
+
a.reject?('*').must_equal false
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
it "not rejected if q > 0" do
|
202
|
+
a = subject.new('en', 'us', q: 0.001)
|
203
|
+
a.reject?('en-us').must_equal false
|
204
|
+
a.reject?('en-gb').must_equal false
|
205
|
+
a.reject?('zh-us').must_equal false
|
206
|
+
b = subject.new('en', '*', q: 0.9)
|
207
|
+
b.reject?('en-us').must_equal false
|
208
|
+
c = subject.new('*', q: 1)
|
209
|
+
c.reject?('en-us').must_equal false
|
210
|
+
end
|
92
211
|
end
|
93
212
|
end
|
94
213
|
end
|
@@ -40,12 +40,9 @@ module AcceptHeaders
|
|
40
40
|
require 'csv'
|
41
41
|
# https://www.iana.org/assignments/media-types/media-types.xhtml
|
42
42
|
%w[application audio image message model multipart text video].each do |filename|
|
43
|
-
CSV.foreach("spec/support/#{filename}.csv", headers: true) do |row|
|
43
|
+
CSV.foreach("spec/support/media_types/#{filename}.csv", headers: true) do |row|
|
44
44
|
media_type = row['Template']
|
45
45
|
|
46
|
-
# audio/amr-wb+ is a typo
|
47
|
-
media_type = 'audio/amr-wb+' if media_type == 'amr-wb+'
|
48
|
-
|
49
46
|
if media_type
|
50
47
|
subject.new(media_type).list.size.must_equal 1
|
51
48
|
subject.new(media_type).list.first.media_range.must_equal media_type.downcase
|
@@ -99,12 +96,27 @@ module AcceptHeaders
|
|
99
96
|
end
|
100
97
|
end
|
101
98
|
|
102
|
-
describe "negotiate
|
99
|
+
describe "#negotiate" do
|
103
100
|
it "returns a best matching media type" do
|
104
101
|
n = subject.new("text/*, text/html, text/html;level=1, */*")
|
105
102
|
n.negotiate("text/html").must_equal MediaType.new('text', 'html', params: { 'level' => '1' })
|
106
103
|
end
|
107
104
|
end
|
105
|
+
|
106
|
+
describe "#accept?" do
|
107
|
+
it "returns whether specific media type is accepted" do
|
108
|
+
n = subject.new("video/*, text/html, text/html;level=1;q:0.8")
|
109
|
+
n.accept?("text/html").must_equal true
|
110
|
+
n.accept?("application/json").must_equal false
|
111
|
+
n.accept?("video/ogg").must_equal true
|
112
|
+
end
|
113
|
+
|
114
|
+
it "returns false if accepted but q=0" do
|
115
|
+
n = subject.new("video/*, text/html;q=0")
|
116
|
+
n.accept?("text/html").must_equal false
|
117
|
+
n.accept?("video/ogg").must_equal true
|
118
|
+
end
|
119
|
+
end
|
108
120
|
end
|
109
121
|
end
|
110
122
|
end
|
data/spec/media_type_spec.rb
CHANGED
@@ -99,5 +99,117 @@ module AcceptHeaders
|
|
99
99
|
it "outputs the media range" do
|
100
100
|
subject.new('text', 'html', params: { 'level' => '1' }).media_range.must_equal "text/html"
|
101
101
|
end
|
102
|
+
|
103
|
+
describe "#accept?" do
|
104
|
+
it "accepted if the type and subtype are the same" do
|
105
|
+
a = subject.new('text', 'html')
|
106
|
+
a.accept?('text/html').must_equal true
|
107
|
+
b = subject.new('text', 'html', q: 0.001)
|
108
|
+
b.accept?('text/html').must_equal true
|
109
|
+
end
|
110
|
+
|
111
|
+
it "accepted if the type is the same and the other subtype is *" do
|
112
|
+
a = subject.new('text', '*')
|
113
|
+
a.accept?('text/html').must_equal true
|
114
|
+
b = subject.new('text', '*', q: 0.9)
|
115
|
+
b.accept?('text/html').must_equal true
|
116
|
+
end
|
117
|
+
|
118
|
+
it "accepted if the type and subtype are *" do
|
119
|
+
a = subject.new('*')
|
120
|
+
a.accept?('text/html').must_equal true
|
121
|
+
b = subject.new('*', q: 0.1)
|
122
|
+
b.accept?('text/html').must_equal true
|
123
|
+
end
|
124
|
+
|
125
|
+
it "not accepted if the type and subtype don't match" do
|
126
|
+
a = subject.new('text', 'html')
|
127
|
+
a.accept?('application/json').must_equal false
|
128
|
+
b = subject.new('text', 'html', q: 0.2)
|
129
|
+
b.accept?('application/json').must_equal false
|
130
|
+
end
|
131
|
+
|
132
|
+
it "not accepted if the type doesn't match" do
|
133
|
+
a = subject.new('text', 'plain')
|
134
|
+
a.accept?('application/plain').must_equal false
|
135
|
+
b = subject.new('text', 'plain', q: 0.4)
|
136
|
+
b.accept?('application/json').must_equal false
|
137
|
+
end
|
138
|
+
|
139
|
+
it "not accepted if the subtype doesn't match" do
|
140
|
+
a = subject.new('text', 'html')
|
141
|
+
a.accept?('text/plain').must_equal false
|
142
|
+
b = subject.new('text', 'html', q: 0.6)
|
143
|
+
b.accept?('text/plain').must_equal false
|
144
|
+
end
|
145
|
+
|
146
|
+
it "not accepted if q is 0" do
|
147
|
+
a = subject.new('text', 'html', q: 0)
|
148
|
+
a.accept?('text/html').must_equal false
|
149
|
+
a.accept?('text/plain').must_equal false
|
150
|
+
a.accept?('application/plain').must_equal false
|
151
|
+
b = subject.new('text', '*', q: 0)
|
152
|
+
b.accept?('text/html').must_equal false
|
153
|
+
c = subject.new('*', q: 0)
|
154
|
+
c.accept?('text/html').must_equal false
|
155
|
+
end
|
156
|
+
|
157
|
+
# TODO: test *
|
158
|
+
it "not accepted if..." do
|
159
|
+
a = subject.new('text', 'plain')
|
160
|
+
a.accept?('*').must_equal false
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe "#reject?" do
|
165
|
+
describe "given q is 0" do
|
166
|
+
it "rejected if the type and subtype are the same" do
|
167
|
+
a = subject.new('text', 'html', q: 0)
|
168
|
+
a.reject?('text/html').must_equal true
|
169
|
+
end
|
170
|
+
|
171
|
+
it "rejected if the type is the same and the other subtype is *" do
|
172
|
+
a = subject.new('text', '*', q: 0)
|
173
|
+
a.reject?('text/html').must_equal true
|
174
|
+
end
|
175
|
+
|
176
|
+
it "rejected if the type and subtype are *" do
|
177
|
+
a = subject.new('*', q: 0)
|
178
|
+
a.reject?('text/html').must_equal true
|
179
|
+
end
|
180
|
+
|
181
|
+
it "not rejected if the type and subtype don't match" do
|
182
|
+
a = subject.new('text', 'html', q: 0)
|
183
|
+
a.reject?('application/json').must_equal false
|
184
|
+
end
|
185
|
+
|
186
|
+
it "not rejected if the type doesn't match" do
|
187
|
+
a = subject.new('text', 'plain', q: 0)
|
188
|
+
a.reject?('application/plain').must_equal false
|
189
|
+
end
|
190
|
+
|
191
|
+
it "not rejected if the subtype doesn't match" do
|
192
|
+
a = subject.new('text', 'html', q: 0)
|
193
|
+
a.reject?('text/plain').must_equal false
|
194
|
+
end
|
195
|
+
|
196
|
+
# TODO: test *
|
197
|
+
it "not rejected if..." do
|
198
|
+
a = subject.new('text', 'plain', q: 0)
|
199
|
+
a.reject?('*').must_equal false
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
it "not rejected if q > 0" do
|
204
|
+
a = subject.new('text', 'html', q: 0.001)
|
205
|
+
a.reject?('text/html').must_equal false
|
206
|
+
a.reject?('text/plain').must_equal false
|
207
|
+
a.reject?('application/plain').must_equal false
|
208
|
+
b = subject.new('text', '*', q: 0.9)
|
209
|
+
b.reject?('text/html').must_equal false
|
210
|
+
c = subject.new('*', q: 1)
|
211
|
+
c.reject?('text/html').must_equal false
|
212
|
+
end
|
213
|
+
end
|
102
214
|
end
|
103
215
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,9 +1,5 @@
|
|
1
|
-
require "minitest/autorun"
|
2
|
-
require "minitest/focus"
|
3
1
|
require "codeclimate-test-reporter"
|
4
2
|
require "simplecov"
|
5
|
-
require "awesome_print"
|
6
|
-
require "pry"
|
7
3
|
|
8
4
|
CodeClimate::TestReporter.configure do |config|
|
9
5
|
config.logger.level = Logger::WARN
|
@@ -14,6 +10,12 @@ SimpleCov.start do
|
|
14
10
|
SimpleCov::Formatter::HTMLFormatter,
|
15
11
|
CodeClimate::TestReporter::Formatter
|
16
12
|
]
|
13
|
+
add_filter 'spec/'
|
17
14
|
end
|
18
15
|
|
16
|
+
require "minitest/autorun"
|
17
|
+
require "minitest/focus"
|
18
|
+
require "awesome_print"
|
19
|
+
require "pry"
|
20
|
+
|
19
21
|
require_relative "../lib/accept_headers"
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Name,Description,Reference,Notes
|
2
|
+
compress,"UNIX ""compress"" data format [Welch, T., ""A Technique
|
3
|
+
for High Performance Data Compression"", IEEE Computer 17(6), June 1984.]",[RFC7230],Section 4.2.1
|
4
|
+
deflate,"""deflate"" compressed data ([RFC1951])
|
5
|
+
inside the ""zlib"" data format ([RFC1950])",[RFC7230],Section 4.2.2
|
6
|
+
exi,W3C Efficient XML Interchange,"[J. Schneider and T. Kamiya, Eds. ""Efficient XML Interchange (EXI) Format 1.0"", W3C
|
7
|
+
Working Draft, 19 Sep 2008]",
|
8
|
+
gzip,GZIP file format [RFC1952],[RFC7230],Section 4.2.3
|
9
|
+
identity,"Reserved (synonym for ""no encoding"" in Accept-Encoding)",[RFC7231],Section 5.3.4
|
10
|
+
pack200-gzip,Network Transfer Format for Java Archives,[JSR 200: Network Transfer Format for Java][Kumar_Srinivasan][John_Rose],
|
11
|
+
x-compress,Deprecated (alias for compress),[RFC7230],Section 4.2.1
|
12
|
+
x-gzip,Deprecated (alias for gzip),[RFC7230],Section 4.2.3
|
File without changes
|
@@ -6,7 +6,7 @@ Name,Template,Reference
|
|
6
6
|
ac3,audio/ac3,[RFC4184]
|
7
7
|
AMR,audio/AMR,[RFC4867]
|
8
8
|
AMR-WB,audio/AMR-WB,[RFC4867]
|
9
|
-
amr-wb+,amr-wb+,[RFC4352]
|
9
|
+
amr-wb+,audio/amr-wb+,[RFC4352]
|
10
10
|
aptx,audio/aptx,[RFC7310]
|
11
11
|
asc,audio/asc,[RFC6295]
|
12
12
|
ATRAC-ADVANCED-LOSSLESS,audio/ATRAC-ADVANCED-LOSSLESS,[RFC5584]
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: accept_headers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jack Chu
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-11-
|
11
|
+
date: 2014-11-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -80,7 +80,7 @@ dependencies:
|
|
80
80
|
- - '>='
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
|
-
description:
|
83
|
+
description: A ruby library that does content negotiation and parses and sorts http
|
84
84
|
accept headers. Adheres to RFC 2616.
|
85
85
|
email:
|
86
86
|
- kamuigt@gmail.com
|
@@ -115,14 +115,15 @@ files:
|
|
115
115
|
- spec/media_type/negotiator_spec.rb
|
116
116
|
- spec/media_type_spec.rb
|
117
117
|
- spec/spec_helper.rb
|
118
|
-
- spec/support/
|
119
|
-
- spec/support/
|
120
|
-
- spec/support/
|
121
|
-
- spec/support/
|
122
|
-
- spec/support/
|
123
|
-
- spec/support/
|
124
|
-
- spec/support/
|
125
|
-
- spec/support/
|
118
|
+
- spec/support/encodings/content-coding.csv
|
119
|
+
- spec/support/media_types/application.csv
|
120
|
+
- spec/support/media_types/audio.csv
|
121
|
+
- spec/support/media_types/image.csv
|
122
|
+
- spec/support/media_types/message.csv
|
123
|
+
- spec/support/media_types/model.csv
|
124
|
+
- spec/support/media_types/multipart.csv
|
125
|
+
- spec/support/media_types/text.csv
|
126
|
+
- spec/support/media_types/video.csv
|
126
127
|
- wercker.yml
|
127
128
|
homepage: ''
|
128
129
|
licenses:
|
@@ -157,11 +158,12 @@ test_files:
|
|
157
158
|
- spec/media_type/negotiator_spec.rb
|
158
159
|
- spec/media_type_spec.rb
|
159
160
|
- spec/spec_helper.rb
|
160
|
-
- spec/support/
|
161
|
-
- spec/support/
|
162
|
-
- spec/support/
|
163
|
-
- spec/support/
|
164
|
-
- spec/support/
|
165
|
-
- spec/support/
|
166
|
-
- spec/support/
|
167
|
-
- spec/support/
|
161
|
+
- spec/support/encodings/content-coding.csv
|
162
|
+
- spec/support/media_types/application.csv
|
163
|
+
- spec/support/media_types/audio.csv
|
164
|
+
- spec/support/media_types/image.csv
|
165
|
+
- spec/support/media_types/message.csv
|
166
|
+
- spec/support/media_types/model.csv
|
167
|
+
- spec/support/media_types/multipart.csv
|
168
|
+
- spec/support/media_types/text.csv
|
169
|
+
- spec/support/media_types/video.csv
|