xenon 0.0.1 → 0.0.2
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/.gitignore +20 -19
- data/.rspec +3 -0
- data/Guardfile +48 -0
- data/LICENSE +22 -0
- data/README.md +4 -1
- data/lib/xenon.rb +237 -2
- data/lib/xenon/errors.rb +4 -0
- data/lib/xenon/headers.rb +114 -0
- data/lib/xenon/headers/accept.rb +33 -0
- data/lib/xenon/headers/accept_charset.rb +59 -0
- data/lib/xenon/headers/accept_encoding.rb +63 -0
- data/lib/xenon/headers/accept_language.rb +59 -0
- data/lib/xenon/headers/authorization.rb +96 -0
- data/lib/xenon/headers/cache_control.rb +56 -0
- data/lib/xenon/headers/content_type.rb +23 -0
- data/lib/xenon/media_type.rb +140 -0
- data/lib/xenon/parsers/basic_rules.rb +55 -0
- data/lib/xenon/parsers/header_rules.rb +14 -0
- data/lib/xenon/parsers/media_type.rb +52 -0
- data/lib/xenon/quoted_string.rb +10 -0
- data/lib/xenon/routing.rb +133 -0
- data/lib/xenon/version.rb +1 -1
- data/spec/spec_helper.rb +91 -0
- data/spec/xenon/headers/accept_charset_spec.rb +31 -0
- data/spec/xenon/headers/accept_encoding_spec.rb +40 -0
- data/spec/xenon/headers/accept_language_spec.rb +33 -0
- data/spec/xenon/headers/accept_spec.rb +54 -0
- data/spec/xenon/headers/authorization_spec.rb +47 -0
- data/spec/xenon/headers/cache_control_spec.rb +64 -0
- data/spec/xenon/media_type_spec.rb +267 -0
- data/xenon.gemspec +6 -1
- metadata +70 -9
- data/LICENSE.txt +0 -22
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'xenon/headers/accept'
|
2
|
+
|
3
|
+
describe Xenon::Headers::Accept do
|
4
|
+
|
5
|
+
context '::parse' do
|
6
|
+
it 'can parse a basic accept range' do
|
7
|
+
header = Xenon::Headers::Accept.parse('text/plain; q=0.5')
|
8
|
+
expect(header.media_ranges.size).to eq(1)
|
9
|
+
expect(header.media_ranges[0].to_s).to eq('text/plain; q=0.5')
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'can parse the first example from RFC 7231 § 5.3.2 with the right precedence' do
|
13
|
+
header = Xenon::Headers::Accept.parse('text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c')
|
14
|
+
expect(header.media_ranges.size).to eq(4)
|
15
|
+
expect(header.media_ranges[0].to_s).to eq('text/html')
|
16
|
+
expect(header.media_ranges[1].to_s).to eq('text/x-c')
|
17
|
+
expect(header.media_ranges[2].to_s).to eq('text/x-dvi; q=0.8')
|
18
|
+
expect(header.media_ranges[3].to_s).to eq('text/plain; q=0.5')
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'can parse the second example from RFC 7231 § 5.3.2 with the right precedence' do
|
22
|
+
header = Xenon::Headers::Accept.parse('text/*, text/plain, text/plain;format=flowed, */*')
|
23
|
+
expect(header.media_ranges.size).to eq(4)
|
24
|
+
expect(header.media_ranges[0].to_s).to eq('text/plain; format=flowed')
|
25
|
+
expect(header.media_ranges[1].to_s).to eq('text/plain')
|
26
|
+
expect(header.media_ranges[2].to_s).to eq('text/*')
|
27
|
+
expect(header.media_ranges[3].to_s).to eq('*/*')
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'can parse the third example from RFC 7231 § 5.3.2 with the right precedence' do
|
31
|
+
header = Xenon::Headers::Accept.parse('text/*;q=0.3, text/html;q=0.7, text/html;level=1, text/html;level=2;q=0.4, */*;q=0.5')
|
32
|
+
expect(header.media_ranges.size).to eq(5)
|
33
|
+
expect(header.media_ranges[0].to_s).to eq('text/html; level=1')
|
34
|
+
expect(header.media_ranges[1].to_s).to eq('text/html; level=2; q=0.4')
|
35
|
+
expect(header.media_ranges[2].to_s).to eq('text/html; q=0.7')
|
36
|
+
expect(header.media_ranges[3].to_s).to eq('text/*; q=0.3')
|
37
|
+
expect(header.media_ranges[4].to_s).to eq('*/*; q=0.5')
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context '#merge' do
|
42
|
+
it 'can merge two headers with the right precedence' do
|
43
|
+
h1 = Xenon::Headers::Accept.parse('text/plain; q=0.5, text/html')
|
44
|
+
h2 = Xenon::Headers::Accept.parse('text/x-c, text/x-dvi; q=0.8')
|
45
|
+
header = h1.merge(h2)
|
46
|
+
expect(header.media_ranges.size).to eq(4)
|
47
|
+
expect(header.media_ranges[0].to_s).to eq('text/html')
|
48
|
+
expect(header.media_ranges[1].to_s).to eq('text/x-c')
|
49
|
+
expect(header.media_ranges[2].to_s).to eq('text/x-dvi; q=0.8')
|
50
|
+
expect(header.media_ranges[3].to_s).to eq('text/plain; q=0.5')
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'xenon/headers/authorization'
|
2
|
+
|
3
|
+
describe Xenon::Headers::Authorization do
|
4
|
+
|
5
|
+
context '::parse' do
|
6
|
+
it 'can parse Basic credentials' do
|
7
|
+
header = Xenon::Headers::Authorization.parse('Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==')
|
8
|
+
expect(header.credentials.class).to eq(Xenon::BasicCredentials)
|
9
|
+
expect(header.credentials.username).to eq('Aladdin')
|
10
|
+
expect(header.credentials.password).to eq('open sesame')
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'can parse Digest credentials' do
|
14
|
+
header = Xenon::Headers::Authorization.parse('Digest username="Mufasa"' +
|
15
|
+
', realm="testrealm@host.com"' +
|
16
|
+
', nonce="dcd98b7102dd2f0e8b11d0f600bfb0c093"' +
|
17
|
+
', uri="/dir/index.html"' +
|
18
|
+
', qop=auth' +
|
19
|
+
', nc=00000001' +
|
20
|
+
', cnonce="0a4f113b"' +
|
21
|
+
', response="6629fae49393a05397450978507c4ef1"' +
|
22
|
+
', opaque="5ccc069c403ebaf9f0171e9517f40e41"')
|
23
|
+
expect(header.credentials.class).to eq(Xenon::GenericCredentials)
|
24
|
+
expect(header.credentials.scheme).to eq('Digest')
|
25
|
+
expect(header.credentials.token).to eq(nil)
|
26
|
+
expect(header.credentials.params).to eq(
|
27
|
+
'username' => 'Mufasa',
|
28
|
+
'realm' => 'testrealm@host.com',
|
29
|
+
'nonce' => 'dcd98b7102dd2f0e8b11d0f600bfb0c093',
|
30
|
+
'uri' => '/dir/index.html',
|
31
|
+
'qop' => 'auth',
|
32
|
+
'nc' => '00000001',
|
33
|
+
'cnonce' => '0a4f113b',
|
34
|
+
'response' => '6629fae49393a05397450978507c4ef1',
|
35
|
+
'opaque' => '5ccc069c403ebaf9f0171e9517f40e41')
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'can parse Bearer credentials' do
|
39
|
+
header = Xenon::Headers::Authorization.parse('Bearer eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.')
|
40
|
+
expect(header.credentials.class).to eq(Xenon::GenericCredentials)
|
41
|
+
expect(header.credentials.scheme).to eq('Bearer')
|
42
|
+
expect(header.credentials.token).to eq('eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ.')
|
43
|
+
expect(header.credentials.params).to eq({})
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'xenon/headers/cache_control'
|
2
|
+
|
3
|
+
describe Xenon::Headers::CacheControl do
|
4
|
+
|
5
|
+
context '::parse' do
|
6
|
+
['max-age', 'max-stale', 'min-fresh', 's-maxage'].each do |dir|
|
7
|
+
it "can parse the #{dir} directive" do
|
8
|
+
header = Xenon::Headers::CacheControl.parse("#{dir}=5")
|
9
|
+
expect(header.directives.size).to eq(1)
|
10
|
+
expect(header.directives[0].to_s).to eq("#{dir}=5")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "can parse the #{dir} directive with a quoted value" do # should not be sent by clients but is permitted
|
14
|
+
header = Xenon::Headers::CacheControl.parse("#{dir}=\"5\"")
|
15
|
+
expect(header.directives.size).to eq(1)
|
16
|
+
expect(header.directives[0].to_s).to eq("#{dir}=5")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
['no-cache', 'no-store', 'no-transform', 'only-if-cached', 'must-revalidate', 'public', 'proxy-revalidate'].each do |dir|
|
21
|
+
it "can parse the #{dir} directive" do
|
22
|
+
header = Xenon::Headers::CacheControl.parse("#{dir}")
|
23
|
+
expect(header.directives.size).to eq(1)
|
24
|
+
expect(header.directives[0].to_s).to eq("#{dir}")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
it "can parse the private directive with no field names" do
|
29
|
+
header = Xenon::Headers::CacheControl.parse('private')
|
30
|
+
expect(header.directives.size).to eq(1)
|
31
|
+
expect(header.directives[0].to_s).to eq('private')
|
32
|
+
end
|
33
|
+
|
34
|
+
# TODO: private directive with field names
|
35
|
+
|
36
|
+
it 'can parse extension directives with quoted string values' do
|
37
|
+
header = Xenon::Headers::CacheControl.parse('ext="hello \"world\""')
|
38
|
+
expect(header.directives.size).to eq(1)
|
39
|
+
expect(header.directives[0].name).to eq('ext')
|
40
|
+
expect(header.directives[0].value).to eq("hello \"world\"")
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'can parse more complex directives' do
|
44
|
+
header = Xenon::Headers::CacheControl.parse('public, max-age=3600, must-revalidate')
|
45
|
+
expect(header.directives.size).to eq(3)
|
46
|
+
expect(header.directives[0].to_s).to eq('public')
|
47
|
+
expect(header.directives[1].to_s).to eq('max-age=3600')
|
48
|
+
expect(header.directives[2].to_s).to eq('must-revalidate')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context '#merge' do
|
53
|
+
it 'can merge two headers and maintain directive order' do
|
54
|
+
h1 = Xenon::Headers::CacheControl.parse('public, max-age=3600')
|
55
|
+
h2 = Xenon::Headers::CacheControl.parse('must-revalidate')
|
56
|
+
header = h1.merge(h2)
|
57
|
+
expect(header.directives.size).to eq(3)
|
58
|
+
expect(header.directives[0].to_s).to eq('public')
|
59
|
+
expect(header.directives[1].to_s).to eq('max-age=3600')
|
60
|
+
expect(header.directives[2].to_s).to eq('must-revalidate')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,267 @@
|
|
1
|
+
require 'xenon/media_type'
|
2
|
+
|
3
|
+
describe Xenon::MediaType do
|
4
|
+
|
5
|
+
context '::parse' do
|
6
|
+
it 'can parse basic media types' do
|
7
|
+
mt = Xenon::MediaType.parse('application/json')
|
8
|
+
expect(mt.type).to eq('application')
|
9
|
+
expect(mt.subtype).to eq('json')
|
10
|
+
end
|
11
|
+
it 'can parse media types with a subtype suffix' do
|
12
|
+
mt = Xenon::MediaType.parse('application/rss+xml')
|
13
|
+
expect(mt.type).to eq('application')
|
14
|
+
expect(mt.subtype).to eq('rss+xml')
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'can parse media types with parameters' do
|
18
|
+
mt = Xenon::MediaType.parse('text/plain; format=flowed; paged')
|
19
|
+
expect(mt.type).to eq('text')
|
20
|
+
expect(mt.subtype).to eq('plain')
|
21
|
+
expect(mt.params).to eq({ 'format' => 'flowed', 'paged' => nil })
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'strips whitespace around separators' do
|
25
|
+
mt = Xenon::MediaType.parse('text/plain ; format = flowed ; paged')
|
26
|
+
expect(mt.type).to eq('text')
|
27
|
+
expect(mt.subtype).to eq('plain')
|
28
|
+
expect(mt.params).to eq({ 'format' => 'flowed', 'paged' => nil })
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'raises an error when the media type contains wildcards' do
|
32
|
+
expect { Xenon::MediaType.parse('*/*') }.to raise_error(Xenon::ParseError)
|
33
|
+
expect { Xenon::MediaType.parse('application/*') }.to raise_error(Xenon::ParseError)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'raises an error when the media type is invalid' do
|
37
|
+
expect { Xenon::MediaType.parse('application') }.to raise_error(Xenon::ParseError)
|
38
|
+
expect { Xenon::MediaType.parse('application; foo=bar') }.to raise_error(Xenon::ParseError)
|
39
|
+
expect { Xenon::MediaType.parse('/json') }.to raise_error(Xenon::ParseError)
|
40
|
+
expect { Xenon::MediaType.parse('/json; foo=bar') }.to raise_error(Xenon::ParseError)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
%w(application audio image message multipart text video).each do |type|
|
45
|
+
context "#{type}?" do
|
46
|
+
it "returns true when the root type is '#{type}'" do
|
47
|
+
mt = Xenon::MediaType.new(type, 'dummy')
|
48
|
+
expect(mt.send("#{type}?")).to eq(true)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "returns false when the root type is not '#{type}'" do
|
52
|
+
mt = Xenon::MediaType.new('dummy', 'dummy')
|
53
|
+
expect(mt.send("#{type}?")).to eq(false)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
{ experimental?: 'x', personal?: 'prs', vendor?: 'vnd' }.each do |method, prefix|
|
59
|
+
context method do
|
60
|
+
it "returns true when the subtype starts with '#{prefix}.'" do
|
61
|
+
mt = Xenon::MediaType.new('application', "#{prefix}.dummy")
|
62
|
+
expect(mt.send(method)).to eq(true)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "returns false when the subtype does not start with '#{prefix}.'" do
|
66
|
+
mt = Xenon::MediaType.new('application', "dummy.dummy")
|
67
|
+
expect(mt.send(method)).to eq(false)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
%w(ber der fastinfoset json wbxml xml zip).each do |format|
|
73
|
+
context "#{format}?" do
|
74
|
+
it "returns true when the subtype is '#{format}'" do
|
75
|
+
mt = Xenon::MediaType.new('application', format)
|
76
|
+
expect(mt.send("#{format}?")).to eq(true)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "returns true when the subtype ends with '+#{format}'" do
|
80
|
+
mt = Xenon::MediaType.new('application', "dummy+#{format}")
|
81
|
+
expect(mt.send("#{format}?")).to eq(true)
|
82
|
+
end
|
83
|
+
|
84
|
+
it "returns false when the subtype is not '#{format}' and does not end with '+#{format}'" do
|
85
|
+
mt = Xenon::MediaType.new('dummy', 'dummy+dummy')
|
86
|
+
expect(mt.send("#{format}?")).to eq(false)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context '#to_s' do
|
92
|
+
it 'returns the string representation of a media type' do
|
93
|
+
mt = Xenon::MediaType.new('text', 'plain', 'format' => 'flowed', 'paged' => nil)
|
94
|
+
expect(mt.to_s).to eq('text/plain; format=flowed; paged')
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
describe Xenon::MediaRange do
|
101
|
+
|
102
|
+
context '::parse' do
|
103
|
+
it 'can parse basic media ranges' do
|
104
|
+
mt = Xenon::MediaRange.parse('application/json')
|
105
|
+
expect(mt.type).to eq('application')
|
106
|
+
expect(mt.subtype).to eq('json')
|
107
|
+
end
|
108
|
+
|
109
|
+
it 'can parse media ranges with parameters' do
|
110
|
+
mt = Xenon::MediaRange.parse('text/plain; format=flowed; paged')
|
111
|
+
expect(mt.type).to eq('text')
|
112
|
+
expect(mt.subtype).to eq('plain')
|
113
|
+
expect(mt.params).to eq({ 'format' => 'flowed', 'paged' => nil })
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'strips whitespace around separators' do
|
117
|
+
mt = Xenon::MediaRange.parse('text/plain ; format = flowed ; paged')
|
118
|
+
expect(mt.type).to eq('text')
|
119
|
+
expect(mt.subtype).to eq('plain')
|
120
|
+
expect(mt.params).to eq({ 'format' => 'flowed', 'paged' => nil })
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'can parse media ranges with subtype wildcards' do
|
124
|
+
mt = Xenon::MediaRange.parse('application/*')
|
125
|
+
expect(mt.type).to eq('application')
|
126
|
+
expect(mt.subtype).to eq('*')
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'can parse media ranges with type and subtype wildcards' do
|
130
|
+
mt = Xenon::MediaRange.parse('*/*')
|
131
|
+
expect(mt.type).to eq('*')
|
132
|
+
expect(mt.subtype).to eq('*')
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'extracts q from the parameters' do
|
136
|
+
mt = Xenon::MediaRange.parse('text/plain; q=0.8; format=flowed; paged')
|
137
|
+
expect(mt.type).to eq('text')
|
138
|
+
expect(mt.subtype).to eq('plain')
|
139
|
+
expect(mt.q).to eq(0.8)
|
140
|
+
expect(mt.params).to eq({ 'format' => 'flowed', 'paged' => nil })
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'uses the default value for q if the value is not numeric' do
|
144
|
+
mt = Xenon::MediaRange.parse('application/json; q=foo')
|
145
|
+
expect(mt.type).to eq('application')
|
146
|
+
expect(mt.subtype).to eq('json')
|
147
|
+
expect(mt.q).to eq(Xenon::MediaRange::DEFAULT_Q)
|
148
|
+
end
|
149
|
+
|
150
|
+
it 'raises an error when the media range is invalid' do
|
151
|
+
expect { Xenon::MediaRange.parse('application') }.to raise_error(Xenon::ParseError)
|
152
|
+
expect { Xenon::MediaRange.parse('application; foo=bar') }.to raise_error(Xenon::ParseError)
|
153
|
+
expect { Xenon::MediaRange.parse('*/json') }.to raise_error(Xenon::ParseError)
|
154
|
+
expect { Xenon::MediaRange.parse('/json') }.to raise_error(Xenon::ParseError)
|
155
|
+
expect { Xenon::MediaRange.parse('/json; foo=bar') }.to raise_error(Xenon::ParseError)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
context '#<=>' do
|
160
|
+
it 'considers a wildcard type less than a regular type' do
|
161
|
+
mr1 = Xenon::MediaRange.new('*', '*')
|
162
|
+
mr2 = Xenon::MediaRange.new('text', '*')
|
163
|
+
expect(mr1 <=> mr2).to eq(-1)
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'considers a wildcard subtype less than a regular subtype' do
|
167
|
+
mr1 = Xenon::MediaRange.new('application', '*')
|
168
|
+
mr2 = Xenon::MediaRange.new('text', 'plain')
|
169
|
+
expect(mr1 <=> mr2).to eq(-1)
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'considers media ranges with type and subtype equal' do
|
173
|
+
mr1 = Xenon::MediaRange.new('application', 'json')
|
174
|
+
mr2 = Xenon::MediaRange.new('text', 'plain')
|
175
|
+
expect(mr1 <=> mr2).to eq(0)
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'considers a media range with parameters greater than one without' do
|
179
|
+
mr1 = Xenon::MediaRange.new('text', 'plain', 'format' => 'flowed')
|
180
|
+
mr2 = Xenon::MediaRange.new('text', 'plain')
|
181
|
+
expect(mr1 <=> mr2).to eq(1)
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'does not consider the quality when one media range is more specific' do
|
185
|
+
mr1 = Xenon::MediaRange.new('application', '*', 'q' => '0.3')
|
186
|
+
mr2 = Xenon::MediaRange.new('*', '*', 'q' => '0.5')
|
187
|
+
expect(mr1 <=> mr2).to eq(1)
|
188
|
+
end
|
189
|
+
|
190
|
+
it 'considers the quality when media ranges are equally specific' do
|
191
|
+
mr1 = Xenon::MediaRange.new('application', 'json', 'q' => '0.8')
|
192
|
+
mr2 = Xenon::MediaRange.new('application', 'xml')
|
193
|
+
expect(mr1 <=> mr2).to eq(-1)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
%i(=~ ===).each do |name|
|
198
|
+
context "##{name}" do
|
199
|
+
it 'returns true when the type and subtype are wildcards' do
|
200
|
+
mr = Xenon::MediaRange.new('*', '*')
|
201
|
+
mt = Xenon::MediaType.new('application', 'json')
|
202
|
+
expect(mr.send(name, mt)).to eq(true)
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'returns true when the type matches and subtype is a wildcard' do
|
206
|
+
mr = Xenon::MediaRange.new('application', '*')
|
207
|
+
mt = Xenon::MediaType.new('application', 'json')
|
208
|
+
expect(mr.send(name, mt)).to eq(true)
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'returns true when the type and subtype match exactly' do
|
212
|
+
mr = Xenon::MediaRange.new('application', 'json')
|
213
|
+
mt = Xenon::MediaType.new('application', 'json')
|
214
|
+
expect(mr.send(name, mt)).to eq(true)
|
215
|
+
end
|
216
|
+
|
217
|
+
it 'returns true when the type, subtype and parameters match exactly' do
|
218
|
+
mr = Xenon::MediaRange.new('text', 'plain', 'format' => 'flowed')
|
219
|
+
mt = Xenon::MediaType.new('text', 'plain', 'format' => 'flowed', 'paged' => nil)
|
220
|
+
expect(mr.send(name, mt)).to eq(true)
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'returns true when the the media type has more specific parameters' do
|
224
|
+
mr = Xenon::MediaRange.new('text', 'plain')
|
225
|
+
mt = Xenon::MediaType.new('text', 'plain', 'format' => 'flowed', 'paged' => nil)
|
226
|
+
expect(mr.send(name, mt)).to eq(true)
|
227
|
+
end
|
228
|
+
|
229
|
+
it 'returns false when the type is different' do
|
230
|
+
mr = Xenon::MediaRange.new('text', 'json')
|
231
|
+
mt = Xenon::MediaType.new('application', 'json')
|
232
|
+
expect(mr.send(name, mt)).to eq(false)
|
233
|
+
end
|
234
|
+
|
235
|
+
it 'returns false when the type matches but subtype is different' do
|
236
|
+
mr = Xenon::MediaRange.new('application', 'xml')
|
237
|
+
mt = Xenon::MediaType.new('application', 'json')
|
238
|
+
expect(mr.send(name, mt)).to eq(false)
|
239
|
+
end
|
240
|
+
|
241
|
+
it 'returns false when the media range has more specific parameters' do
|
242
|
+
mr = Xenon::MediaRange.new('text', 'plain', 'format' => 'flowed', 'paged' => nil)
|
243
|
+
mt = Xenon::MediaType.new('text', 'plain')
|
244
|
+
expect(mr.send(name, mt)).to eq(false)
|
245
|
+
end
|
246
|
+
|
247
|
+
it 'returns false when the media range has a different parameter value' do
|
248
|
+
mr = Xenon::MediaRange.new('text', 'plain', 'format' => 'flowed')
|
249
|
+
mt = Xenon::MediaType.new('text', 'plain', 'format' => 'linear')
|
250
|
+
expect(mr.send(name, mt)).to eq(false)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
context '#to_s' do
|
256
|
+
it 'returns the string representation of a media range' do
|
257
|
+
mt = Xenon::MediaRange.new('text', 'plain', 'q' => 0.8, 'format' => 'flowed', 'paged' => nil)
|
258
|
+
expect(mt.to_s).to eq('text/plain; format=flowed; paged; q=0.8')
|
259
|
+
end
|
260
|
+
|
261
|
+
it 'omits the q parameter when it is 1.0' do
|
262
|
+
mt = Xenon::MediaRange.new('application', 'json', 'q' => 1.0)
|
263
|
+
expect(mt.to_s).to eq('application/json')
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
end
|
data/xenon.gemspec
CHANGED
@@ -18,7 +18,12 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
|
+
spec.required_ruby_version = '>= 2.2.0'
|
22
|
+
|
23
|
+
spec.add_runtime_dependency 'activesupport', '~> 4.0'
|
24
|
+
spec.add_runtime_dependency 'parslet', '~> 1.7'
|
25
|
+
|
21
26
|
spec.add_development_dependency 'bundler', '~> 1.6'
|
22
|
-
spec.add_development_dependency 'rake'
|
27
|
+
spec.add_development_dependency 'rake', '~> 10.0'
|
23
28
|
spec.add_development_dependency 'rspec', '~> 3.0'
|
24
29
|
end
|