addressable 2.8.7 → 2.8.9
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 +64 -46
- data/README.md +6 -6
- data/lib/addressable/idna/pure.rb +4235 -30
- data/lib/addressable/uri.rb +1 -1
- data/lib/addressable/version.rb +1 -1
- metadata +6 -46
- data/Gemfile +0 -31
- data/Rakefile +0 -37
- data/addressable.gemspec +0 -28
- data/data/unicode.data +0 -0
- data/spec/addressable/idna_spec.rb +0 -302
- data/spec/addressable/net_http_compat_spec.rb +0 -29
- data/spec/addressable/security_spec.rb +0 -58
- data/spec/addressable/template_spec.rb +0 -1264
- data/spec/addressable/uri_spec.rb +0 -6840
- data/spec/spec_helper.rb +0 -33
- data/tasks/clobber.rake +0 -4
- data/tasks/gem.rake +0 -95
- data/tasks/git.rake +0 -47
- data/tasks/metrics.rake +0 -24
- data/tasks/profile.rake +0 -72
- data/tasks/rspec.rake +0 -23
- data/tasks/yard.rake +0 -29
|
@@ -1,1264 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
# Copyright (C) Bob Aman
|
|
4
|
-
#
|
|
5
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
-
# you may not use this file except in compliance with the License.
|
|
7
|
-
# You may obtain a copy of the License at
|
|
8
|
-
#
|
|
9
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
-
#
|
|
11
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
-
# See the License for the specific language governing permissions and
|
|
15
|
-
# limitations under the License.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
require "spec_helper"
|
|
19
|
-
|
|
20
|
-
require "bigdecimal"
|
|
21
|
-
require "timeout"
|
|
22
|
-
require "addressable/template"
|
|
23
|
-
|
|
24
|
-
shared_examples_for 'expands' do |tests|
|
|
25
|
-
tests.each do |template, expansion|
|
|
26
|
-
exp = expansion.is_a?(Array) ? expansion.first : expansion
|
|
27
|
-
it "#{template} to #{exp}" do
|
|
28
|
-
tmpl = Addressable::Template.new(template).expand(subject)
|
|
29
|
-
expect(tmpl.to_str).to eq(expansion)
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
describe "eql?" do
|
|
35
|
-
let(:template) { Addressable::Template.new('https://www.example.com/{foo}') }
|
|
36
|
-
it 'is equal when the pattern matches' do
|
|
37
|
-
other_template = Addressable::Template.new('https://www.example.com/{foo}')
|
|
38
|
-
expect(template).to be_eql(other_template)
|
|
39
|
-
expect(other_template).to be_eql(template)
|
|
40
|
-
end
|
|
41
|
-
it 'is not equal when the pattern differs' do
|
|
42
|
-
other_template = Addressable::Template.new('https://www.example.com/{bar}')
|
|
43
|
-
expect(template).to_not be_eql(other_template)
|
|
44
|
-
expect(other_template).to_not be_eql(template)
|
|
45
|
-
end
|
|
46
|
-
it 'is not equal to non-templates' do
|
|
47
|
-
uri = 'https://www.example.com/foo/bar'
|
|
48
|
-
addressable_template = Addressable::Template.new uri
|
|
49
|
-
addressable_uri = Addressable::URI.parse uri
|
|
50
|
-
expect(addressable_template).to_not be_eql(addressable_uri)
|
|
51
|
-
expect(addressable_uri).to_not be_eql(addressable_template)
|
|
52
|
-
end
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
describe "==" do
|
|
56
|
-
let(:template) { Addressable::Template.new('https://www.example.com/{foo}') }
|
|
57
|
-
it 'is equal when the pattern matches' do
|
|
58
|
-
other_template = Addressable::Template.new('https://www.example.com/{foo}')
|
|
59
|
-
expect(template).to eq other_template
|
|
60
|
-
expect(other_template).to eq template
|
|
61
|
-
end
|
|
62
|
-
it 'is not equal when the pattern differs' do
|
|
63
|
-
other_template = Addressable::Template.new('https://www.example.com/{bar}')
|
|
64
|
-
expect(template).not_to eq other_template
|
|
65
|
-
expect(other_template).not_to eq template
|
|
66
|
-
end
|
|
67
|
-
it 'is not equal to non-templates' do
|
|
68
|
-
uri = 'https://www.example.com/foo/bar'
|
|
69
|
-
addressable_template = Addressable::Template.new uri
|
|
70
|
-
addressable_uri = Addressable::URI.parse uri
|
|
71
|
-
expect(addressable_template).not_to eq addressable_uri
|
|
72
|
-
expect(addressable_uri).not_to eq addressable_template
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
describe "#to_regexp" do
|
|
77
|
-
it "does not match the first line of multiline strings" do
|
|
78
|
-
uri = "https://www.example.com/bar"
|
|
79
|
-
template = Addressable::Template.new(uri)
|
|
80
|
-
expect(template.match(uri)).not_to be_nil
|
|
81
|
-
expect(template.match("#{uri}\ngarbage")).to be_nil
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
describe "Type conversion" do
|
|
86
|
-
subject {
|
|
87
|
-
{
|
|
88
|
-
:var => true,
|
|
89
|
-
:hello => 1234,
|
|
90
|
-
:nothing => nil,
|
|
91
|
-
:sym => :symbolic,
|
|
92
|
-
:decimal => BigDecimal('1')
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
it_behaves_like 'expands', {
|
|
97
|
-
'{var}' => 'true',
|
|
98
|
-
'{hello}' => '1234',
|
|
99
|
-
'{nothing}' => '',
|
|
100
|
-
'{sym}' => 'symbolic',
|
|
101
|
-
'{decimal}' => RUBY_VERSION < '2.4.0' ? '0.1E1' : '0.1e1'
|
|
102
|
-
}
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
describe "Level 1:" do
|
|
106
|
-
subject {
|
|
107
|
-
{:var => "value", :hello => "Hello World!"}
|
|
108
|
-
}
|
|
109
|
-
it_behaves_like 'expands', {
|
|
110
|
-
'{var}' => 'value',
|
|
111
|
-
'{hello}' => 'Hello%20World%21'
|
|
112
|
-
}
|
|
113
|
-
end
|
|
114
|
-
|
|
115
|
-
describe "Level 2" do
|
|
116
|
-
subject {
|
|
117
|
-
{
|
|
118
|
-
:var => "value",
|
|
119
|
-
:hello => "Hello World!",
|
|
120
|
-
:path => "/foo/bar"
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
context "Operator +:" do
|
|
124
|
-
it_behaves_like 'expands', {
|
|
125
|
-
'{+var}' => 'value',
|
|
126
|
-
'{+hello}' => 'Hello%20World!',
|
|
127
|
-
'{+path}/here' => '/foo/bar/here',
|
|
128
|
-
'here?ref={+path}' => 'here?ref=/foo/bar'
|
|
129
|
-
}
|
|
130
|
-
end
|
|
131
|
-
context "Operator #:" do
|
|
132
|
-
it_behaves_like 'expands', {
|
|
133
|
-
'X{#var}' => 'X#value',
|
|
134
|
-
'X{#hello}' => 'X#Hello%20World!'
|
|
135
|
-
}
|
|
136
|
-
end
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
describe "Level 3" do
|
|
140
|
-
subject {
|
|
141
|
-
{
|
|
142
|
-
:var => "value",
|
|
143
|
-
:hello => "Hello World!",
|
|
144
|
-
:empty => "",
|
|
145
|
-
:path => "/foo/bar",
|
|
146
|
-
:x => "1024",
|
|
147
|
-
:y => "768"
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
context "Operator nil (multiple vars):" do
|
|
151
|
-
it_behaves_like 'expands', {
|
|
152
|
-
'map?{x,y}' => 'map?1024,768',
|
|
153
|
-
'{x,hello,y}' => '1024,Hello%20World%21,768'
|
|
154
|
-
}
|
|
155
|
-
end
|
|
156
|
-
context "Operator + (multiple vars):" do
|
|
157
|
-
it_behaves_like 'expands', {
|
|
158
|
-
'{+x,hello,y}' => '1024,Hello%20World!,768',
|
|
159
|
-
'{+path,x}/here' => '/foo/bar,1024/here'
|
|
160
|
-
}
|
|
161
|
-
end
|
|
162
|
-
context "Operator # (multiple vars):" do
|
|
163
|
-
it_behaves_like 'expands', {
|
|
164
|
-
'{#x,hello,y}' => '#1024,Hello%20World!,768',
|
|
165
|
-
'{#path,x}/here' => '#/foo/bar,1024/here'
|
|
166
|
-
}
|
|
167
|
-
end
|
|
168
|
-
context "Operator ." do
|
|
169
|
-
it_behaves_like 'expands', {
|
|
170
|
-
'X{.var}' => 'X.value',
|
|
171
|
-
'X{.x,y}' => 'X.1024.768'
|
|
172
|
-
}
|
|
173
|
-
end
|
|
174
|
-
context "Operator /" do
|
|
175
|
-
it_behaves_like 'expands', {
|
|
176
|
-
'{/var}' => '/value',
|
|
177
|
-
'{/var,x}/here' => '/value/1024/here'
|
|
178
|
-
}
|
|
179
|
-
end
|
|
180
|
-
context "Operator ;" do
|
|
181
|
-
it_behaves_like 'expands', {
|
|
182
|
-
'{;x,y}' => ';x=1024;y=768',
|
|
183
|
-
'{;x,y,empty}' => ';x=1024;y=768;empty'
|
|
184
|
-
}
|
|
185
|
-
end
|
|
186
|
-
context "Operator ?" do
|
|
187
|
-
it_behaves_like 'expands', {
|
|
188
|
-
'{?x,y}' => '?x=1024&y=768',
|
|
189
|
-
'{?x,y,empty}' => '?x=1024&y=768&empty='
|
|
190
|
-
}
|
|
191
|
-
end
|
|
192
|
-
context "Operator &" do
|
|
193
|
-
it_behaves_like 'expands', {
|
|
194
|
-
'?fixed=yes{&x}' => '?fixed=yes&x=1024',
|
|
195
|
-
'{&x,y,empty}' => '&x=1024&y=768&empty='
|
|
196
|
-
}
|
|
197
|
-
end
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
describe "Level 4" do
|
|
201
|
-
subject {
|
|
202
|
-
{
|
|
203
|
-
:var => "value",
|
|
204
|
-
:hello => "Hello World!",
|
|
205
|
-
:path => "/foo/bar",
|
|
206
|
-
:semi => ";",
|
|
207
|
-
:list => %w(red green blue),
|
|
208
|
-
:keys => {"semi" => ';', "dot" => '.', :comma => ','}
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
context "Expansion with value modifiers" do
|
|
212
|
-
it_behaves_like 'expands', {
|
|
213
|
-
'{var:3}' => 'val',
|
|
214
|
-
'{var:30}' => 'value',
|
|
215
|
-
'{list}' => 'red,green,blue',
|
|
216
|
-
'{list*}' => 'red,green,blue',
|
|
217
|
-
'{keys}' => 'semi,%3B,dot,.,comma,%2C',
|
|
218
|
-
'{keys*}' => 'semi=%3B,dot=.,comma=%2C',
|
|
219
|
-
}
|
|
220
|
-
end
|
|
221
|
-
context "Operator + with value modifiers" do
|
|
222
|
-
it_behaves_like 'expands', {
|
|
223
|
-
'{+path:6}/here' => '/foo/b/here',
|
|
224
|
-
'{+list}' => 'red,green,blue',
|
|
225
|
-
'{+list*}' => 'red,green,blue',
|
|
226
|
-
'{+keys}' => 'semi,;,dot,.,comma,,',
|
|
227
|
-
'{+keys*}' => 'semi=;,dot=.,comma=,',
|
|
228
|
-
}
|
|
229
|
-
end
|
|
230
|
-
context "Operator # with value modifiers" do
|
|
231
|
-
it_behaves_like 'expands', {
|
|
232
|
-
'{#path:6}/here' => '#/foo/b/here',
|
|
233
|
-
'{#list}' => '#red,green,blue',
|
|
234
|
-
'{#list*}' => '#red,green,blue',
|
|
235
|
-
'{#keys}' => '#semi,;,dot,.,comma,,',
|
|
236
|
-
'{#keys*}' => '#semi=;,dot=.,comma=,',
|
|
237
|
-
}
|
|
238
|
-
end
|
|
239
|
-
context "Operator . with value modifiers" do
|
|
240
|
-
it_behaves_like 'expands', {
|
|
241
|
-
'X{.var:3}' => 'X.val',
|
|
242
|
-
'X{.list}' => 'X.red,green,blue',
|
|
243
|
-
'X{.list*}' => 'X.red.green.blue',
|
|
244
|
-
'X{.keys}' => 'X.semi,%3B,dot,.,comma,%2C',
|
|
245
|
-
'X{.keys*}' => 'X.semi=%3B.dot=..comma=%2C',
|
|
246
|
-
}
|
|
247
|
-
end
|
|
248
|
-
context "Operator / with value modifiers" do
|
|
249
|
-
it_behaves_like 'expands', {
|
|
250
|
-
'{/var:1,var}' => '/v/value',
|
|
251
|
-
'{/list}' => '/red,green,blue',
|
|
252
|
-
'{/list*}' => '/red/green/blue',
|
|
253
|
-
'{/list*,path:4}' => '/red/green/blue/%2Ffoo',
|
|
254
|
-
'{/keys}' => '/semi,%3B,dot,.,comma,%2C',
|
|
255
|
-
'{/keys*}' => '/semi=%3B/dot=./comma=%2C',
|
|
256
|
-
}
|
|
257
|
-
end
|
|
258
|
-
context "Operator ; with value modifiers" do
|
|
259
|
-
it_behaves_like 'expands', {
|
|
260
|
-
'{;hello:5}' => ';hello=Hello',
|
|
261
|
-
'{;list}' => ';list=red,green,blue',
|
|
262
|
-
'{;list*}' => ';list=red;list=green;list=blue',
|
|
263
|
-
'{;keys}' => ';keys=semi,%3B,dot,.,comma,%2C',
|
|
264
|
-
'{;keys*}' => ';semi=%3B;dot=.;comma=%2C',
|
|
265
|
-
}
|
|
266
|
-
end
|
|
267
|
-
context "Operator ? with value modifiers" do
|
|
268
|
-
it_behaves_like 'expands', {
|
|
269
|
-
'{?var:3}' => '?var=val',
|
|
270
|
-
'{?list}' => '?list=red,green,blue',
|
|
271
|
-
'{?list*}' => '?list=red&list=green&list=blue',
|
|
272
|
-
'{?keys}' => '?keys=semi,%3B,dot,.,comma,%2C',
|
|
273
|
-
'{?keys*}' => '?semi=%3B&dot=.&comma=%2C',
|
|
274
|
-
}
|
|
275
|
-
end
|
|
276
|
-
context "Operator & with value modifiers" do
|
|
277
|
-
it_behaves_like 'expands', {
|
|
278
|
-
'{&var:3}' => '&var=val',
|
|
279
|
-
'{&list}' => '&list=red,green,blue',
|
|
280
|
-
'{&list*}' => '&list=red&list=green&list=blue',
|
|
281
|
-
'{&keys}' => '&keys=semi,%3B,dot,.,comma,%2C',
|
|
282
|
-
'{&keys*}' => '&semi=%3B&dot=.&comma=%2C',
|
|
283
|
-
}
|
|
284
|
-
end
|
|
285
|
-
end
|
|
286
|
-
describe "Modifiers" do
|
|
287
|
-
subject {
|
|
288
|
-
{
|
|
289
|
-
:var => "value",
|
|
290
|
-
:semi => ";",
|
|
291
|
-
:year => [1965, 2000, 2012],
|
|
292
|
-
:dom => %w(example com)
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
context "length" do
|
|
296
|
-
it_behaves_like 'expands', {
|
|
297
|
-
'{var:3}' => 'val',
|
|
298
|
-
'{var:30}' => 'value',
|
|
299
|
-
'{var}' => 'value',
|
|
300
|
-
'{semi}' => '%3B',
|
|
301
|
-
'{semi:2}' => '%3B'
|
|
302
|
-
}
|
|
303
|
-
end
|
|
304
|
-
context "explode" do
|
|
305
|
-
it_behaves_like 'expands', {
|
|
306
|
-
'find{?year*}' => 'find?year=1965&year=2000&year=2012',
|
|
307
|
-
'www{.dom*}' => 'www.example.com',
|
|
308
|
-
}
|
|
309
|
-
end
|
|
310
|
-
end
|
|
311
|
-
describe "Expansion" do
|
|
312
|
-
subject {
|
|
313
|
-
{
|
|
314
|
-
:count => ["one", "two", "three"],
|
|
315
|
-
:dom => ["example", "com"],
|
|
316
|
-
:dub => "me/too",
|
|
317
|
-
:hello => "Hello World!",
|
|
318
|
-
:half => "50%",
|
|
319
|
-
:var => "value",
|
|
320
|
-
:who => "fred",
|
|
321
|
-
:base => "http://example.com/home/",
|
|
322
|
-
:path => "/foo/bar",
|
|
323
|
-
:list => ["red", "green", "blue"],
|
|
324
|
-
:keys => {"semi" => ";","dot" => ".",:comma => ","},
|
|
325
|
-
:v => "6",
|
|
326
|
-
:x => "1024",
|
|
327
|
-
:y => "768",
|
|
328
|
-
:empty => "",
|
|
329
|
-
:empty_keys => {},
|
|
330
|
-
:undef => nil
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
context "concatenation" do
|
|
334
|
-
it_behaves_like 'expands', {
|
|
335
|
-
'{count}' => 'one,two,three',
|
|
336
|
-
'{count*}' => 'one,two,three',
|
|
337
|
-
'{/count}' => '/one,two,three',
|
|
338
|
-
'{/count*}' => '/one/two/three',
|
|
339
|
-
'{;count}' => ';count=one,two,three',
|
|
340
|
-
'{;count*}' => ';count=one;count=two;count=three',
|
|
341
|
-
'{?count}' => '?count=one,two,three',
|
|
342
|
-
'{?count*}' => '?count=one&count=two&count=three',
|
|
343
|
-
'{&count*}' => '&count=one&count=two&count=three'
|
|
344
|
-
}
|
|
345
|
-
end
|
|
346
|
-
context "simple expansion" do
|
|
347
|
-
it_behaves_like 'expands', {
|
|
348
|
-
'{var}' => 'value',
|
|
349
|
-
'{hello}' => 'Hello%20World%21',
|
|
350
|
-
'{half}' => '50%25',
|
|
351
|
-
'O{empty}X' => 'OX',
|
|
352
|
-
'O{undef}X' => 'OX',
|
|
353
|
-
'{x,y}' => '1024,768',
|
|
354
|
-
'{x,hello,y}' => '1024,Hello%20World%21,768',
|
|
355
|
-
'?{x,empty}' => '?1024,',
|
|
356
|
-
'?{x,undef}' => '?1024',
|
|
357
|
-
'?{undef,y}' => '?768',
|
|
358
|
-
'{var:3}' => 'val',
|
|
359
|
-
'{var:30}' => 'value',
|
|
360
|
-
'{list}' => 'red,green,blue',
|
|
361
|
-
'{list*}' => 'red,green,blue',
|
|
362
|
-
'{keys}' => 'semi,%3B,dot,.,comma,%2C',
|
|
363
|
-
'{keys*}' => 'semi=%3B,dot=.,comma=%2C',
|
|
364
|
-
}
|
|
365
|
-
end
|
|
366
|
-
context "reserved expansion (+)" do
|
|
367
|
-
it_behaves_like 'expands', {
|
|
368
|
-
'{+var}' => 'value',
|
|
369
|
-
'{+hello}' => 'Hello%20World!',
|
|
370
|
-
'{+half}' => '50%25',
|
|
371
|
-
'{base}index' => 'http%3A%2F%2Fexample.com%2Fhome%2Findex',
|
|
372
|
-
'{+base}index' => 'http://example.com/home/index',
|
|
373
|
-
'O{+empty}X' => 'OX',
|
|
374
|
-
'O{+undef}X' => 'OX',
|
|
375
|
-
'{+path}/here' => '/foo/bar/here',
|
|
376
|
-
'here?ref={+path}' => 'here?ref=/foo/bar',
|
|
377
|
-
'up{+path}{var}/here' => 'up/foo/barvalue/here',
|
|
378
|
-
'{+x,hello,y}' => '1024,Hello%20World!,768',
|
|
379
|
-
'{+path,x}/here' => '/foo/bar,1024/here',
|
|
380
|
-
'{+path:6}/here' => '/foo/b/here',
|
|
381
|
-
'{+list}' => 'red,green,blue',
|
|
382
|
-
'{+list*}' => 'red,green,blue',
|
|
383
|
-
'{+keys}' => 'semi,;,dot,.,comma,,',
|
|
384
|
-
'{+keys*}' => 'semi=;,dot=.,comma=,',
|
|
385
|
-
}
|
|
386
|
-
end
|
|
387
|
-
context "fragment expansion (#)" do
|
|
388
|
-
it_behaves_like 'expands', {
|
|
389
|
-
'{#var}' => '#value',
|
|
390
|
-
'{#hello}' => '#Hello%20World!',
|
|
391
|
-
'{#half}' => '#50%25',
|
|
392
|
-
'foo{#empty}' => 'foo#',
|
|
393
|
-
'foo{#undef}' => 'foo',
|
|
394
|
-
'{#x,hello,y}' => '#1024,Hello%20World!,768',
|
|
395
|
-
'{#path,x}/here' => '#/foo/bar,1024/here',
|
|
396
|
-
'{#path:6}/here' => '#/foo/b/here',
|
|
397
|
-
'{#list}' => '#red,green,blue',
|
|
398
|
-
'{#list*}' => '#red,green,blue',
|
|
399
|
-
'{#keys}' => '#semi,;,dot,.,comma,,',
|
|
400
|
-
'{#keys*}' => '#semi=;,dot=.,comma=,',
|
|
401
|
-
}
|
|
402
|
-
end
|
|
403
|
-
context "label expansion (.)" do
|
|
404
|
-
it_behaves_like 'expands', {
|
|
405
|
-
'{.who}' => '.fred',
|
|
406
|
-
'{.who,who}' => '.fred.fred',
|
|
407
|
-
'{.half,who}' => '.50%25.fred',
|
|
408
|
-
'www{.dom*}' => 'www.example.com',
|
|
409
|
-
'X{.var}' => 'X.value',
|
|
410
|
-
'X{.empty}' => 'X.',
|
|
411
|
-
'X{.undef}' => 'X',
|
|
412
|
-
'X{.var:3}' => 'X.val',
|
|
413
|
-
'X{.list}' => 'X.red,green,blue',
|
|
414
|
-
'X{.list*}' => 'X.red.green.blue',
|
|
415
|
-
'X{.keys}' => 'X.semi,%3B,dot,.,comma,%2C',
|
|
416
|
-
'X{.keys*}' => 'X.semi=%3B.dot=..comma=%2C',
|
|
417
|
-
'X{.empty_keys}' => 'X',
|
|
418
|
-
'X{.empty_keys*}' => 'X'
|
|
419
|
-
}
|
|
420
|
-
end
|
|
421
|
-
context "path expansion (/)" do
|
|
422
|
-
it_behaves_like 'expands', {
|
|
423
|
-
'{/who}' => '/fred',
|
|
424
|
-
'{/who,who}' => '/fred/fred',
|
|
425
|
-
'{/half,who}' => '/50%25/fred',
|
|
426
|
-
'{/who,dub}' => '/fred/me%2Ftoo',
|
|
427
|
-
'{/var}' => '/value',
|
|
428
|
-
'{/var,empty}' => '/value/',
|
|
429
|
-
'{/var,undef}' => '/value',
|
|
430
|
-
'{/var,x}/here' => '/value/1024/here',
|
|
431
|
-
'{/var:1,var}' => '/v/value',
|
|
432
|
-
'{/list}' => '/red,green,blue',
|
|
433
|
-
'{/list*}' => '/red/green/blue',
|
|
434
|
-
'{/list*,path:4}' => '/red/green/blue/%2Ffoo',
|
|
435
|
-
'{/keys}' => '/semi,%3B,dot,.,comma,%2C',
|
|
436
|
-
'{/keys*}' => '/semi=%3B/dot=./comma=%2C',
|
|
437
|
-
}
|
|
438
|
-
end
|
|
439
|
-
context "path-style expansion (;)" do
|
|
440
|
-
it_behaves_like 'expands', {
|
|
441
|
-
'{;who}' => ';who=fred',
|
|
442
|
-
'{;half}' => ';half=50%25',
|
|
443
|
-
'{;empty}' => ';empty',
|
|
444
|
-
'{;v,empty,who}' => ';v=6;empty;who=fred',
|
|
445
|
-
'{;v,bar,who}' => ';v=6;who=fred',
|
|
446
|
-
'{;x,y}' => ';x=1024;y=768',
|
|
447
|
-
'{;x,y,empty}' => ';x=1024;y=768;empty',
|
|
448
|
-
'{;x,y,undef}' => ';x=1024;y=768',
|
|
449
|
-
'{;hello:5}' => ';hello=Hello',
|
|
450
|
-
'{;list}' => ';list=red,green,blue',
|
|
451
|
-
'{;list*}' => ';list=red;list=green;list=blue',
|
|
452
|
-
'{;keys}' => ';keys=semi,%3B,dot,.,comma,%2C',
|
|
453
|
-
'{;keys*}' => ';semi=%3B;dot=.;comma=%2C',
|
|
454
|
-
}
|
|
455
|
-
end
|
|
456
|
-
context "form query expansion (?)" do
|
|
457
|
-
it_behaves_like 'expands', {
|
|
458
|
-
'{?who}' => '?who=fred',
|
|
459
|
-
'{?half}' => '?half=50%25',
|
|
460
|
-
'{?x,y}' => '?x=1024&y=768',
|
|
461
|
-
'{?x,y,empty}' => '?x=1024&y=768&empty=',
|
|
462
|
-
'{?x,y,undef}' => '?x=1024&y=768',
|
|
463
|
-
'{?var:3}' => '?var=val',
|
|
464
|
-
'{?list}' => '?list=red,green,blue',
|
|
465
|
-
'{?list*}' => '?list=red&list=green&list=blue',
|
|
466
|
-
'{?keys}' => '?keys=semi,%3B,dot,.,comma,%2C',
|
|
467
|
-
'{?keys*}' => '?semi=%3B&dot=.&comma=%2C',
|
|
468
|
-
}
|
|
469
|
-
end
|
|
470
|
-
context "form query expansion (&)" do
|
|
471
|
-
it_behaves_like 'expands', {
|
|
472
|
-
'{&who}' => '&who=fred',
|
|
473
|
-
'{&half}' => '&half=50%25',
|
|
474
|
-
'?fixed=yes{&x}' => '?fixed=yes&x=1024',
|
|
475
|
-
'{&x,y,empty}' => '&x=1024&y=768&empty=',
|
|
476
|
-
'{&x,y,undef}' => '&x=1024&y=768',
|
|
477
|
-
'{&var:3}' => '&var=val',
|
|
478
|
-
'{&list}' => '&list=red,green,blue',
|
|
479
|
-
'{&list*}' => '&list=red&list=green&list=blue',
|
|
480
|
-
'{&keys}' => '&keys=semi,%3B,dot,.,comma,%2C',
|
|
481
|
-
'{&keys*}' => '&semi=%3B&dot=.&comma=%2C',
|
|
482
|
-
}
|
|
483
|
-
end
|
|
484
|
-
context "non-string key in match data" do
|
|
485
|
-
subject {Addressable::Template.new("http://example.com/{one}")}
|
|
486
|
-
|
|
487
|
-
it "raises TypeError" do
|
|
488
|
-
expect { subject.expand(Object.new => "1") }.to raise_error TypeError
|
|
489
|
-
end
|
|
490
|
-
end
|
|
491
|
-
end
|
|
492
|
-
|
|
493
|
-
class ExampleTwoProcessor
|
|
494
|
-
def self.restore(name, value)
|
|
495
|
-
return value.gsub(/-/, " ") if name == "query"
|
|
496
|
-
return value
|
|
497
|
-
end
|
|
498
|
-
|
|
499
|
-
def self.match(name)
|
|
500
|
-
return ".*?" if name == "first"
|
|
501
|
-
return ".*"
|
|
502
|
-
end
|
|
503
|
-
def self.validate(name, value)
|
|
504
|
-
return !!(value =~ /^[\w ]+$/) if name == "query"
|
|
505
|
-
return true
|
|
506
|
-
end
|
|
507
|
-
|
|
508
|
-
def self.transform(name, value)
|
|
509
|
-
return value.gsub(/ /, "+") if name == "query"
|
|
510
|
-
return value
|
|
511
|
-
end
|
|
512
|
-
end
|
|
513
|
-
|
|
514
|
-
class DumbProcessor
|
|
515
|
-
def self.match(name)
|
|
516
|
-
return ".*?" if name == "first"
|
|
517
|
-
end
|
|
518
|
-
end
|
|
519
|
-
|
|
520
|
-
describe Addressable::Template do
|
|
521
|
-
describe 'initialize' do
|
|
522
|
-
context 'with a non-string' do
|
|
523
|
-
it 'raises a TypeError' do
|
|
524
|
-
expect { Addressable::Template.new(nil) }.to raise_error(TypeError)
|
|
525
|
-
end
|
|
526
|
-
end
|
|
527
|
-
end
|
|
528
|
-
|
|
529
|
-
describe 'freeze' do
|
|
530
|
-
subject { Addressable::Template.new("http://example.com/{first}/{+second}/") }
|
|
531
|
-
it 'freezes the template' do
|
|
532
|
-
expect(subject.freeze).to be_frozen
|
|
533
|
-
end
|
|
534
|
-
end
|
|
535
|
-
|
|
536
|
-
describe "Matching" do
|
|
537
|
-
let(:uri){
|
|
538
|
-
Addressable::URI.parse(
|
|
539
|
-
"http://example.com/search/an-example-search-query/"
|
|
540
|
-
)
|
|
541
|
-
}
|
|
542
|
-
let(:uri2){
|
|
543
|
-
Addressable::URI.parse("http://example.com/a/b/c/")
|
|
544
|
-
}
|
|
545
|
-
let(:uri3){
|
|
546
|
-
Addressable::URI.parse("http://example.com/;a=1;b=2;c=3;first=foo")
|
|
547
|
-
}
|
|
548
|
-
let(:uri4){
|
|
549
|
-
Addressable::URI.parse("http://example.com/?a=1&b=2&c=3&first=foo")
|
|
550
|
-
}
|
|
551
|
-
let(:uri5){
|
|
552
|
-
"http://example.com/foo"
|
|
553
|
-
}
|
|
554
|
-
context "first uri with ExampleTwoProcessor" do
|
|
555
|
-
subject {
|
|
556
|
-
Addressable::Template.new(
|
|
557
|
-
"http://example.com/search/{query}/"
|
|
558
|
-
).match(uri, ExampleTwoProcessor)
|
|
559
|
-
}
|
|
560
|
-
its(:variables){ should == ["query"] }
|
|
561
|
-
its(:captures){ should == ["an example search query"] }
|
|
562
|
-
end
|
|
563
|
-
|
|
564
|
-
context "second uri with ExampleTwoProcessor" do
|
|
565
|
-
subject {
|
|
566
|
-
Addressable::Template.new(
|
|
567
|
-
"http://example.com/{first}/{+second}/"
|
|
568
|
-
).match(uri2, ExampleTwoProcessor)
|
|
569
|
-
}
|
|
570
|
-
its(:variables){ should == ["first", "second"] }
|
|
571
|
-
its(:captures){ should == ["a", "b/c"] }
|
|
572
|
-
end
|
|
573
|
-
|
|
574
|
-
context "second uri with DumbProcessor" do
|
|
575
|
-
subject {
|
|
576
|
-
Addressable::Template.new(
|
|
577
|
-
"http://example.com/{first}/{+second}/"
|
|
578
|
-
).match(uri2, DumbProcessor)
|
|
579
|
-
}
|
|
580
|
-
its(:variables){ should == ["first", "second"] }
|
|
581
|
-
its(:captures){ should == ["a", "b/c"] }
|
|
582
|
-
end
|
|
583
|
-
|
|
584
|
-
context "second uri" do
|
|
585
|
-
subject {
|
|
586
|
-
Addressable::Template.new(
|
|
587
|
-
"http://example.com/{first}{/second*}/"
|
|
588
|
-
).match(uri2)
|
|
589
|
-
}
|
|
590
|
-
its(:variables){ should == ["first", "second"] }
|
|
591
|
-
its(:captures){ should == ["a", ["b","c"]] }
|
|
592
|
-
end
|
|
593
|
-
context "third uri" do
|
|
594
|
-
subject {
|
|
595
|
-
Addressable::Template.new(
|
|
596
|
-
"http://example.com/{;hash*,first}"
|
|
597
|
-
).match(uri3)
|
|
598
|
-
}
|
|
599
|
-
its(:variables){ should == ["hash", "first"] }
|
|
600
|
-
its(:captures){ should == [
|
|
601
|
-
{"a" => "1", "b" => "2", "c" => "3", "first" => "foo"}, nil] }
|
|
602
|
-
end
|
|
603
|
-
# Note that this expansion is impossible to revert deterministically - the
|
|
604
|
-
# * operator means first could have been a key of hash or a separate key.
|
|
605
|
-
# Semantically, a separate key is more likely, but both are possible.
|
|
606
|
-
context "fourth uri" do
|
|
607
|
-
subject {
|
|
608
|
-
Addressable::Template.new(
|
|
609
|
-
"http://example.com/{?hash*,first}"
|
|
610
|
-
).match(uri4)
|
|
611
|
-
}
|
|
612
|
-
its(:variables){ should == ["hash", "first"] }
|
|
613
|
-
its(:captures){ should == [
|
|
614
|
-
{"a" => "1", "b" => "2", "c" => "3", "first"=> "foo"}, nil] }
|
|
615
|
-
end
|
|
616
|
-
context "fifth uri" do
|
|
617
|
-
subject {
|
|
618
|
-
Addressable::Template.new(
|
|
619
|
-
"http://example.com/{path}{?hash*,first}"
|
|
620
|
-
).match(uri5)
|
|
621
|
-
}
|
|
622
|
-
its(:variables){ should == ["path", "hash", "first"] }
|
|
623
|
-
its(:captures){ should == ["foo", nil, nil] }
|
|
624
|
-
end
|
|
625
|
-
end
|
|
626
|
-
|
|
627
|
-
describe 'match' do
|
|
628
|
-
subject { Addressable::Template.new('http://example.com/first/second/') }
|
|
629
|
-
context 'when the URI is the same as the template' do
|
|
630
|
-
it 'returns the match data itself with an empty mapping' do
|
|
631
|
-
uri = Addressable::URI.parse('http://example.com/first/second/')
|
|
632
|
-
match_data = subject.match(uri)
|
|
633
|
-
expect(match_data).to be_an Addressable::Template::MatchData
|
|
634
|
-
expect(match_data.uri).to eq(uri)
|
|
635
|
-
expect(match_data.template).to eq(subject)
|
|
636
|
-
expect(match_data.mapping).to be_empty
|
|
637
|
-
expect(match_data.inspect).to be_an String
|
|
638
|
-
end
|
|
639
|
-
end
|
|
640
|
-
end
|
|
641
|
-
|
|
642
|
-
describe "extract" do
|
|
643
|
-
let(:template) {
|
|
644
|
-
Addressable::Template.new(
|
|
645
|
-
"http://{host}{/segments*}/{?one,two,bogus}{#fragment}"
|
|
646
|
-
)
|
|
647
|
-
}
|
|
648
|
-
let(:uri){ "http://example.com/a/b/c/?one=1&two=2#foo" }
|
|
649
|
-
let(:uri2){ "http://example.com/a/b/c/#foo" }
|
|
650
|
-
it "should be able to extract with queries" do
|
|
651
|
-
expect(template.extract(uri)).to eq({
|
|
652
|
-
"host" => "example.com",
|
|
653
|
-
"segments" => %w(a b c),
|
|
654
|
-
"one" => "1",
|
|
655
|
-
"bogus" => nil,
|
|
656
|
-
"two" => "2",
|
|
657
|
-
"fragment" => "foo"
|
|
658
|
-
})
|
|
659
|
-
end
|
|
660
|
-
it "should be able to extract without queries" do
|
|
661
|
-
expect(template.extract(uri2)).to eq({
|
|
662
|
-
"host" => "example.com",
|
|
663
|
-
"segments" => %w(a b c),
|
|
664
|
-
"one" => nil,
|
|
665
|
-
"bogus" => nil,
|
|
666
|
-
"two" => nil,
|
|
667
|
-
"fragment" => "foo"
|
|
668
|
-
})
|
|
669
|
-
end
|
|
670
|
-
|
|
671
|
-
context "issue #137" do
|
|
672
|
-
subject { Addressable::Template.new('/path{?page,per_page}') }
|
|
673
|
-
|
|
674
|
-
it "can match empty" do
|
|
675
|
-
data = subject.extract("/path")
|
|
676
|
-
expect(data["page"]).to eq(nil)
|
|
677
|
-
expect(data["per_page"]).to eq(nil)
|
|
678
|
-
expect(data.keys.sort).to eq(['page', 'per_page'])
|
|
679
|
-
end
|
|
680
|
-
|
|
681
|
-
it "can match first var" do
|
|
682
|
-
data = subject.extract("/path?page=1")
|
|
683
|
-
expect(data["page"]).to eq("1")
|
|
684
|
-
expect(data["per_page"]).to eq(nil)
|
|
685
|
-
expect(data.keys.sort).to eq(['page', 'per_page'])
|
|
686
|
-
end
|
|
687
|
-
|
|
688
|
-
it "can match second var" do
|
|
689
|
-
data = subject.extract("/path?per_page=1")
|
|
690
|
-
expect(data["page"]).to eq(nil)
|
|
691
|
-
expect(data["per_page"]).to eq("1")
|
|
692
|
-
expect(data.keys.sort).to eq(['page', 'per_page'])
|
|
693
|
-
end
|
|
694
|
-
|
|
695
|
-
it "can match both vars" do
|
|
696
|
-
data = subject.extract("/path?page=2&per_page=1")
|
|
697
|
-
expect(data["page"]).to eq("2")
|
|
698
|
-
expect(data["per_page"]).to eq("1")
|
|
699
|
-
expect(data.keys.sort).to eq(['page', 'per_page'])
|
|
700
|
-
end
|
|
701
|
-
end
|
|
702
|
-
end
|
|
703
|
-
|
|
704
|
-
describe "Partial expand with symbols" do
|
|
705
|
-
context "partial_expand with two simple values" do
|
|
706
|
-
subject {
|
|
707
|
-
Addressable::Template.new("http://example.com/{one}/{two}/")
|
|
708
|
-
}
|
|
709
|
-
it "builds a new pattern" do
|
|
710
|
-
expect(subject.partial_expand(:one => "1").pattern).to eq(
|
|
711
|
-
"http://example.com/1/{two}/"
|
|
712
|
-
)
|
|
713
|
-
end
|
|
714
|
-
end
|
|
715
|
-
context "partial_expand query with missing param in middle" do
|
|
716
|
-
subject {
|
|
717
|
-
Addressable::Template.new("http://example.com/{?one,two,three}/")
|
|
718
|
-
}
|
|
719
|
-
it "builds a new pattern" do
|
|
720
|
-
expect(subject.partial_expand(:one => "1", :three => "3").pattern).to eq(
|
|
721
|
-
"http://example.com/?one=1{&two}&three=3/"
|
|
722
|
-
)
|
|
723
|
-
end
|
|
724
|
-
end
|
|
725
|
-
context "partial_expand form style query with missing param at beginning" do
|
|
726
|
-
subject {
|
|
727
|
-
Addressable::Template.new("http://example.com/{?one,two}/")
|
|
728
|
-
}
|
|
729
|
-
it "builds a new pattern" do
|
|
730
|
-
expect(subject.partial_expand(:two => "2").pattern).to eq(
|
|
731
|
-
"http://example.com/?two=2{&one}/"
|
|
732
|
-
)
|
|
733
|
-
end
|
|
734
|
-
end
|
|
735
|
-
context "issue #307 - partial_expand form query with nil params" do
|
|
736
|
-
subject do
|
|
737
|
-
Addressable::Template.new("http://example.com/{?one,two,three}/")
|
|
738
|
-
end
|
|
739
|
-
it "builds a new pattern with two=nil" do
|
|
740
|
-
expect(subject.partial_expand(two: nil).pattern).to eq(
|
|
741
|
-
"http://example.com/{?one}{&three}/"
|
|
742
|
-
)
|
|
743
|
-
end
|
|
744
|
-
it "builds a new pattern with one=nil and two=nil" do
|
|
745
|
-
expect(subject.partial_expand(one: nil, two: nil).pattern).to eq(
|
|
746
|
-
"http://example.com/{?three}/"
|
|
747
|
-
)
|
|
748
|
-
end
|
|
749
|
-
it "builds a new pattern with one=1 and two=nil" do
|
|
750
|
-
expect(subject.partial_expand(one: 1, two: nil).pattern).to eq(
|
|
751
|
-
"http://example.com/?one=1{&three}/"
|
|
752
|
-
)
|
|
753
|
-
end
|
|
754
|
-
it "builds a new pattern with one=nil and two=2" do
|
|
755
|
-
expect(subject.partial_expand(one: nil, two: 2).pattern).to eq(
|
|
756
|
-
"http://example.com/?two=2{&three}/"
|
|
757
|
-
)
|
|
758
|
-
end
|
|
759
|
-
it "builds a new pattern with one=nil" do
|
|
760
|
-
expect(subject.partial_expand(one: nil).pattern).to eq(
|
|
761
|
-
"http://example.com/{?two}{&three}/"
|
|
762
|
-
)
|
|
763
|
-
end
|
|
764
|
-
end
|
|
765
|
-
context "partial_expand with query string" do
|
|
766
|
-
subject {
|
|
767
|
-
Addressable::Template.new("http://example.com/{?two,one}/")
|
|
768
|
-
}
|
|
769
|
-
it "builds a new pattern" do
|
|
770
|
-
expect(subject.partial_expand(:one => "1").pattern).to eq(
|
|
771
|
-
"http://example.com/?one=1{&two}/"
|
|
772
|
-
)
|
|
773
|
-
end
|
|
774
|
-
end
|
|
775
|
-
context "partial_expand with path operator" do
|
|
776
|
-
subject {
|
|
777
|
-
Addressable::Template.new("http://example.com{/one,two}/")
|
|
778
|
-
}
|
|
779
|
-
it "builds a new pattern" do
|
|
780
|
-
expect(subject.partial_expand(:one => "1").pattern).to eq(
|
|
781
|
-
"http://example.com/1{/two}/"
|
|
782
|
-
)
|
|
783
|
-
end
|
|
784
|
-
end
|
|
785
|
-
context "partial expand with unicode values" do
|
|
786
|
-
subject do
|
|
787
|
-
Addressable::Template.new("http://example.com/{resource}/{query}/")
|
|
788
|
-
end
|
|
789
|
-
it "normalizes unicode by default" do
|
|
790
|
-
template = subject.partial_expand("query" => "Cafe\u0301")
|
|
791
|
-
expect(template.pattern).to eq(
|
|
792
|
-
"http://example.com/{resource}/Caf%C3%A9/"
|
|
793
|
-
)
|
|
794
|
-
end
|
|
795
|
-
|
|
796
|
-
it "normalizes as unicode even with wrong encoding specified" do
|
|
797
|
-
template = subject.partial_expand("query" => "Cafe\u0301".b)
|
|
798
|
-
expect(template.pattern).to eq(
|
|
799
|
-
"http://example.com/{resource}/Caf%C3%A9/"
|
|
800
|
-
)
|
|
801
|
-
end
|
|
802
|
-
|
|
803
|
-
it "raises on invalid unicode input" do
|
|
804
|
-
expect {
|
|
805
|
-
subject.partial_expand("query" => "M\xE9thode".b)
|
|
806
|
-
}.to raise_error(ArgumentError, "invalid byte sequence in UTF-8")
|
|
807
|
-
end
|
|
808
|
-
|
|
809
|
-
it "does not normalize unicode when byte semantics requested" do
|
|
810
|
-
template = subject.partial_expand({"query" => "Cafe\u0301"}, nil, false)
|
|
811
|
-
expect(template.pattern).to eq(
|
|
812
|
-
"http://example.com/{resource}/Cafe%CC%81/"
|
|
813
|
-
)
|
|
814
|
-
end
|
|
815
|
-
end
|
|
816
|
-
end
|
|
817
|
-
describe "Partial expand with strings" do
|
|
818
|
-
context "partial_expand with two simple values" do
|
|
819
|
-
subject {
|
|
820
|
-
Addressable::Template.new("http://example.com/{one}/{two}/")
|
|
821
|
-
}
|
|
822
|
-
it "builds a new pattern" do
|
|
823
|
-
expect(subject.partial_expand("one" => "1").pattern).to eq(
|
|
824
|
-
"http://example.com/1/{two}/"
|
|
825
|
-
)
|
|
826
|
-
end
|
|
827
|
-
end
|
|
828
|
-
context "partial_expand query with missing param in middle" do
|
|
829
|
-
subject {
|
|
830
|
-
Addressable::Template.new("http://example.com/{?one,two,three}/")
|
|
831
|
-
}
|
|
832
|
-
it "builds a new pattern" do
|
|
833
|
-
expect(subject.partial_expand("one" => "1", "three" => "3").pattern).to eq(
|
|
834
|
-
"http://example.com/?one=1{&two}&three=3/"
|
|
835
|
-
)
|
|
836
|
-
end
|
|
837
|
-
end
|
|
838
|
-
context "partial_expand with query string" do
|
|
839
|
-
subject {
|
|
840
|
-
Addressable::Template.new("http://example.com/{?two,one}/")
|
|
841
|
-
}
|
|
842
|
-
it "builds a new pattern" do
|
|
843
|
-
expect(subject.partial_expand("one" => "1").pattern).to eq(
|
|
844
|
-
"http://example.com/?one=1{&two}/"
|
|
845
|
-
)
|
|
846
|
-
end
|
|
847
|
-
end
|
|
848
|
-
context "partial_expand with path operator" do
|
|
849
|
-
subject {
|
|
850
|
-
Addressable::Template.new("http://example.com{/one,two}/")
|
|
851
|
-
}
|
|
852
|
-
it "builds a new pattern" do
|
|
853
|
-
expect(subject.partial_expand("one" => "1").pattern).to eq(
|
|
854
|
-
"http://example.com/1{/two}/"
|
|
855
|
-
)
|
|
856
|
-
end
|
|
857
|
-
end
|
|
858
|
-
end
|
|
859
|
-
describe "Expand" do
|
|
860
|
-
context "expand with unicode values" do
|
|
861
|
-
subject do
|
|
862
|
-
Addressable::Template.new("http://example.com/search/{query}/")
|
|
863
|
-
end
|
|
864
|
-
it "normalizes unicode by default" do
|
|
865
|
-
uri = subject.expand("query" => "Cafe\u0301").to_str
|
|
866
|
-
expect(uri).to eq("http://example.com/search/Caf%C3%A9/")
|
|
867
|
-
end
|
|
868
|
-
|
|
869
|
-
it "normalizes as unicode even with wrong encoding specified" do
|
|
870
|
-
uri = subject.expand("query" => "Cafe\u0301".b).to_str
|
|
871
|
-
expect(uri).to eq("http://example.com/search/Caf%C3%A9/")
|
|
872
|
-
end
|
|
873
|
-
|
|
874
|
-
it "raises on invalid unicode input" do
|
|
875
|
-
expect {
|
|
876
|
-
subject.expand("query" => "M\xE9thode".b).to_str
|
|
877
|
-
}.to raise_error(ArgumentError, "invalid byte sequence in UTF-8")
|
|
878
|
-
end
|
|
879
|
-
|
|
880
|
-
it "does not normalize unicode when byte semantics requested" do
|
|
881
|
-
uri = subject.expand({ "query" => "Cafe\u0301" }, nil, false).to_str
|
|
882
|
-
expect(uri).to eq("http://example.com/search/Cafe%CC%81/")
|
|
883
|
-
end
|
|
884
|
-
end
|
|
885
|
-
context "expand with a processor" do
|
|
886
|
-
subject {
|
|
887
|
-
Addressable::Template.new("http://example.com/search/{query}/")
|
|
888
|
-
}
|
|
889
|
-
it "processes spaces" do
|
|
890
|
-
expect(subject.expand({"query" => "an example search query"},
|
|
891
|
-
ExampleTwoProcessor).to_str).to eq(
|
|
892
|
-
"http://example.com/search/an+example+search+query/"
|
|
893
|
-
)
|
|
894
|
-
end
|
|
895
|
-
it "validates" do
|
|
896
|
-
expect{
|
|
897
|
-
subject.expand({"query" => "Bogus!"},
|
|
898
|
-
ExampleTwoProcessor).to_str
|
|
899
|
-
}.to raise_error(Addressable::Template::InvalidTemplateValueError)
|
|
900
|
-
end
|
|
901
|
-
end
|
|
902
|
-
context "partial_expand query with missing param in middle" do
|
|
903
|
-
subject {
|
|
904
|
-
Addressable::Template.new("http://example.com/{?one,two,three}/")
|
|
905
|
-
}
|
|
906
|
-
it "builds a new pattern" do
|
|
907
|
-
expect(subject.partial_expand("one" => "1", "three" => "3").pattern).to eq(
|
|
908
|
-
"http://example.com/?one=1{&two}&three=3/"
|
|
909
|
-
)
|
|
910
|
-
end
|
|
911
|
-
end
|
|
912
|
-
context "partial_expand with query string" do
|
|
913
|
-
subject {
|
|
914
|
-
Addressable::Template.new("http://example.com/{?two,one}/")
|
|
915
|
-
}
|
|
916
|
-
it "builds a new pattern" do
|
|
917
|
-
expect(subject.partial_expand("one" => "1").pattern).to eq(
|
|
918
|
-
"http://example.com/?one=1{&two}/"
|
|
919
|
-
)
|
|
920
|
-
end
|
|
921
|
-
end
|
|
922
|
-
context "partial_expand with path operator" do
|
|
923
|
-
subject {
|
|
924
|
-
Addressable::Template.new("http://example.com{/one,two}/")
|
|
925
|
-
}
|
|
926
|
-
it "builds a new pattern" do
|
|
927
|
-
expect(subject.partial_expand("one" => "1").pattern).to eq(
|
|
928
|
-
"http://example.com/1{/two}/"
|
|
929
|
-
)
|
|
930
|
-
end
|
|
931
|
-
end
|
|
932
|
-
end
|
|
933
|
-
context "Matching with operators" do
|
|
934
|
-
describe "Level 1:" do
|
|
935
|
-
subject { Addressable::Template.new("foo{foo}/{bar}baz") }
|
|
936
|
-
it "can match" do
|
|
937
|
-
data = subject.match("foofoo/bananabaz")
|
|
938
|
-
expect(data.mapping["foo"]).to eq("foo")
|
|
939
|
-
expect(data.mapping["bar"]).to eq("banana")
|
|
940
|
-
end
|
|
941
|
-
it "can fail" do
|
|
942
|
-
expect(subject.match("bar/foo")).to be_nil
|
|
943
|
-
expect(subject.match("foobaz")).to be_nil
|
|
944
|
-
end
|
|
945
|
-
it "can match empty" do
|
|
946
|
-
data = subject.match("foo/baz")
|
|
947
|
-
expect(data.mapping["foo"]).to eq(nil)
|
|
948
|
-
expect(data.mapping["bar"]).to eq(nil)
|
|
949
|
-
end
|
|
950
|
-
it "lists vars" do
|
|
951
|
-
expect(subject.variables).to eq(["foo", "bar"])
|
|
952
|
-
end
|
|
953
|
-
end
|
|
954
|
-
|
|
955
|
-
describe "Level 2:" do
|
|
956
|
-
subject { Addressable::Template.new("foo{+foo}{#bar}baz") }
|
|
957
|
-
it "can match" do
|
|
958
|
-
data = subject.match("foo/test/banana#bazbaz")
|
|
959
|
-
expect(data.mapping["foo"]).to eq("/test/banana")
|
|
960
|
-
expect(data.mapping["bar"]).to eq("baz")
|
|
961
|
-
end
|
|
962
|
-
it "can match empty level 2 #" do
|
|
963
|
-
data = subject.match("foo/test/bananabaz")
|
|
964
|
-
expect(data.mapping["foo"]).to eq("/test/banana")
|
|
965
|
-
expect(data.mapping["bar"]).to eq(nil)
|
|
966
|
-
data = subject.match("foo/test/banana#baz")
|
|
967
|
-
expect(data.mapping["foo"]).to eq("/test/banana")
|
|
968
|
-
expect(data.mapping["bar"]).to eq("")
|
|
969
|
-
end
|
|
970
|
-
it "can match empty level 2 +" do
|
|
971
|
-
data = subject.match("foobaz")
|
|
972
|
-
expect(data.mapping["foo"]).to eq(nil)
|
|
973
|
-
expect(data.mapping["bar"]).to eq(nil)
|
|
974
|
-
data = subject.match("foo#barbaz")
|
|
975
|
-
expect(data.mapping["foo"]).to eq(nil)
|
|
976
|
-
expect(data.mapping["bar"]).to eq("bar")
|
|
977
|
-
end
|
|
978
|
-
it "lists vars" do
|
|
979
|
-
expect(subject.variables).to eq(["foo", "bar"])
|
|
980
|
-
end
|
|
981
|
-
end
|
|
982
|
-
|
|
983
|
-
describe "Level 3:" do
|
|
984
|
-
context "no operator" do
|
|
985
|
-
subject { Addressable::Template.new("foo{foo,bar}baz") }
|
|
986
|
-
it "can match" do
|
|
987
|
-
data = subject.match("foofoo,barbaz")
|
|
988
|
-
expect(data.mapping["foo"]).to eq("foo")
|
|
989
|
-
expect(data.mapping["bar"]).to eq("bar")
|
|
990
|
-
end
|
|
991
|
-
it "lists vars" do
|
|
992
|
-
expect(subject.variables).to eq(["foo", "bar"])
|
|
993
|
-
end
|
|
994
|
-
end
|
|
995
|
-
context "+ operator" do
|
|
996
|
-
subject { Addressable::Template.new("foo{+foo,bar}baz") }
|
|
997
|
-
it "can match" do
|
|
998
|
-
data = subject.match("foofoo/bar,barbaz")
|
|
999
|
-
expect(data.mapping["bar"]).to eq("foo/bar,bar")
|
|
1000
|
-
expect(data.mapping["foo"]).to eq("")
|
|
1001
|
-
end
|
|
1002
|
-
it "lists vars" do
|
|
1003
|
-
expect(subject.variables).to eq(["foo", "bar"])
|
|
1004
|
-
end
|
|
1005
|
-
end
|
|
1006
|
-
context ". operator" do
|
|
1007
|
-
subject { Addressable::Template.new("foo{.foo,bar}baz") }
|
|
1008
|
-
it "can match" do
|
|
1009
|
-
data = subject.match("foo.foo.barbaz")
|
|
1010
|
-
expect(data.mapping["foo"]).to eq("foo")
|
|
1011
|
-
expect(data.mapping["bar"]).to eq("bar")
|
|
1012
|
-
end
|
|
1013
|
-
it "lists vars" do
|
|
1014
|
-
expect(subject.variables).to eq(["foo", "bar"])
|
|
1015
|
-
end
|
|
1016
|
-
end
|
|
1017
|
-
context "/ operator" do
|
|
1018
|
-
subject { Addressable::Template.new("foo{/foo,bar}baz") }
|
|
1019
|
-
it "can match" do
|
|
1020
|
-
data = subject.match("foo/foo/barbaz")
|
|
1021
|
-
expect(data.mapping["foo"]).to eq("foo")
|
|
1022
|
-
expect(data.mapping["bar"]).to eq("bar")
|
|
1023
|
-
end
|
|
1024
|
-
it "lists vars" do
|
|
1025
|
-
expect(subject.variables).to eq(["foo", "bar"])
|
|
1026
|
-
end
|
|
1027
|
-
end
|
|
1028
|
-
context "; operator" do
|
|
1029
|
-
subject { Addressable::Template.new("foo{;foo,bar,baz}baz") }
|
|
1030
|
-
it "can match" do
|
|
1031
|
-
data = subject.match("foo;foo=bar%20baz;bar=foo;bazbaz")
|
|
1032
|
-
expect(data.mapping["foo"]).to eq("bar baz")
|
|
1033
|
-
expect(data.mapping["bar"]).to eq("foo")
|
|
1034
|
-
expect(data.mapping["baz"]).to eq("")
|
|
1035
|
-
end
|
|
1036
|
-
it "lists vars" do
|
|
1037
|
-
expect(subject.variables).to eq(%w(foo bar baz))
|
|
1038
|
-
end
|
|
1039
|
-
end
|
|
1040
|
-
context "? operator" do
|
|
1041
|
-
context "test" do
|
|
1042
|
-
subject { Addressable::Template.new("foo{?foo,bar}baz") }
|
|
1043
|
-
it "can match" do
|
|
1044
|
-
data = subject.match("foo?foo=bar%20baz&bar=foobaz")
|
|
1045
|
-
expect(data.mapping["foo"]).to eq("bar baz")
|
|
1046
|
-
expect(data.mapping["bar"]).to eq("foo")
|
|
1047
|
-
end
|
|
1048
|
-
it "lists vars" do
|
|
1049
|
-
expect(subject.variables).to eq(%w(foo bar))
|
|
1050
|
-
end
|
|
1051
|
-
end
|
|
1052
|
-
|
|
1053
|
-
context "issue #137" do
|
|
1054
|
-
subject { Addressable::Template.new('/path{?page,per_page}') }
|
|
1055
|
-
|
|
1056
|
-
it "can match empty" do
|
|
1057
|
-
data = subject.match("/path")
|
|
1058
|
-
expect(data.mapping["page"]).to eq(nil)
|
|
1059
|
-
expect(data.mapping["per_page"]).to eq(nil)
|
|
1060
|
-
expect(data.mapping.keys.sort).to eq(['page', 'per_page'])
|
|
1061
|
-
end
|
|
1062
|
-
|
|
1063
|
-
it "can match first var" do
|
|
1064
|
-
data = subject.match("/path?page=1")
|
|
1065
|
-
expect(data.mapping["page"]).to eq("1")
|
|
1066
|
-
expect(data.mapping["per_page"]).to eq(nil)
|
|
1067
|
-
expect(data.mapping.keys.sort).to eq(['page', 'per_page'])
|
|
1068
|
-
end
|
|
1069
|
-
|
|
1070
|
-
it "can match second var" do
|
|
1071
|
-
data = subject.match("/path?per_page=1")
|
|
1072
|
-
expect(data.mapping["page"]).to eq(nil)
|
|
1073
|
-
expect(data.mapping["per_page"]).to eq("1")
|
|
1074
|
-
expect(data.mapping.keys.sort).to eq(['page', 'per_page'])
|
|
1075
|
-
end
|
|
1076
|
-
|
|
1077
|
-
it "can match both vars" do
|
|
1078
|
-
data = subject.match("/path?page=2&per_page=1")
|
|
1079
|
-
expect(data.mapping["page"]).to eq("2")
|
|
1080
|
-
expect(data.mapping["per_page"]).to eq("1")
|
|
1081
|
-
expect(data.mapping.keys.sort).to eq(['page', 'per_page'])
|
|
1082
|
-
end
|
|
1083
|
-
end
|
|
1084
|
-
|
|
1085
|
-
context "issue #71" do
|
|
1086
|
-
subject { Addressable::Template.new("http://cyberscore.dev/api/users{?username}") }
|
|
1087
|
-
it "can match" do
|
|
1088
|
-
data = subject.match("http://cyberscore.dev/api/users?username=foobaz")
|
|
1089
|
-
expect(data.mapping["username"]).to eq("foobaz")
|
|
1090
|
-
end
|
|
1091
|
-
it "lists vars" do
|
|
1092
|
-
expect(subject.variables).to eq(%w(username))
|
|
1093
|
-
expect(subject.keys).to eq(%w(username))
|
|
1094
|
-
end
|
|
1095
|
-
end
|
|
1096
|
-
end
|
|
1097
|
-
context "& operator" do
|
|
1098
|
-
subject { Addressable::Template.new("foo{&foo,bar}baz") }
|
|
1099
|
-
it "can match" do
|
|
1100
|
-
data = subject.match("foo&foo=bar%20baz&bar=foobaz")
|
|
1101
|
-
expect(data.mapping["foo"]).to eq("bar baz")
|
|
1102
|
-
expect(data.mapping["bar"]).to eq("foo")
|
|
1103
|
-
end
|
|
1104
|
-
it "lists vars" do
|
|
1105
|
-
expect(subject.variables).to eq(%w(foo bar))
|
|
1106
|
-
end
|
|
1107
|
-
end
|
|
1108
|
-
end
|
|
1109
|
-
end
|
|
1110
|
-
|
|
1111
|
-
context "support regexes:" do
|
|
1112
|
-
context "EXPRESSION" do
|
|
1113
|
-
subject { Addressable::Template::EXPRESSION }
|
|
1114
|
-
it "should be able to match an expression" do
|
|
1115
|
-
expect(subject).to match("{foo}")
|
|
1116
|
-
expect(subject).to match("{foo,9}")
|
|
1117
|
-
expect(subject).to match("{foo.bar,baz}")
|
|
1118
|
-
expect(subject).to match("{+foo.bar,baz}")
|
|
1119
|
-
expect(subject).to match("{foo,foo%20bar}")
|
|
1120
|
-
expect(subject).to match("{#foo:20,baz*}")
|
|
1121
|
-
expect(subject).to match("stuff{#foo:20,baz*}things")
|
|
1122
|
-
end
|
|
1123
|
-
it "should fail on non vars" do
|
|
1124
|
-
expect(subject).not_to match("!{foo")
|
|
1125
|
-
expect(subject).not_to match("{foo.bar.}")
|
|
1126
|
-
expect(subject).not_to match("!{}")
|
|
1127
|
-
end
|
|
1128
|
-
end
|
|
1129
|
-
context "VARNAME" do
|
|
1130
|
-
subject { Addressable::Template::VARNAME }
|
|
1131
|
-
it "should be able to match a variable" do
|
|
1132
|
-
expect(subject).to match("foo")
|
|
1133
|
-
expect(subject).to match("9")
|
|
1134
|
-
expect(subject).to match("foo.bar")
|
|
1135
|
-
expect(subject).to match("foo_bar")
|
|
1136
|
-
expect(subject).to match("foo_bar.baz")
|
|
1137
|
-
expect(subject).to match("foo%20bar")
|
|
1138
|
-
expect(subject).to match("foo%20bar.baz")
|
|
1139
|
-
end
|
|
1140
|
-
it "should fail on non vars" do
|
|
1141
|
-
expect(subject).not_to match("!foo")
|
|
1142
|
-
expect(subject).not_to match("foo.bar.")
|
|
1143
|
-
expect(subject).not_to match("foo%2%00bar")
|
|
1144
|
-
expect(subject).not_to match("foo_ba%r")
|
|
1145
|
-
expect(subject).not_to match("foo_bar*")
|
|
1146
|
-
expect(subject).not_to match("foo_bar:20")
|
|
1147
|
-
end
|
|
1148
|
-
|
|
1149
|
-
it 'should parse in a reasonable time' do
|
|
1150
|
-
expect do
|
|
1151
|
-
Timeout.timeout(0.1) do
|
|
1152
|
-
expect(subject).not_to match("0"*25 + "!")
|
|
1153
|
-
end
|
|
1154
|
-
end.not_to raise_error
|
|
1155
|
-
end
|
|
1156
|
-
end
|
|
1157
|
-
context "VARIABLE_LIST" do
|
|
1158
|
-
subject { Addressable::Template::VARIABLE_LIST }
|
|
1159
|
-
it "should be able to match a variable list" do
|
|
1160
|
-
expect(subject).to match("foo,bar")
|
|
1161
|
-
expect(subject).to match("foo")
|
|
1162
|
-
expect(subject).to match("foo,bar*,baz")
|
|
1163
|
-
expect(subject).to match("foo.bar,bar_baz*,baz:12")
|
|
1164
|
-
end
|
|
1165
|
-
it "should fail on non vars" do
|
|
1166
|
-
expect(subject).not_to match(",foo,bar*,baz")
|
|
1167
|
-
expect(subject).not_to match("foo,*bar,baz")
|
|
1168
|
-
expect(subject).not_to match("foo,,bar*,baz")
|
|
1169
|
-
end
|
|
1170
|
-
end
|
|
1171
|
-
context "VARSPEC" do
|
|
1172
|
-
subject { Addressable::Template::VARSPEC }
|
|
1173
|
-
it "should be able to match a variable with modifier" do
|
|
1174
|
-
expect(subject).to match("9:8")
|
|
1175
|
-
expect(subject).to match("foo.bar*")
|
|
1176
|
-
expect(subject).to match("foo_bar:12")
|
|
1177
|
-
expect(subject).to match("foo_bar.baz*")
|
|
1178
|
-
expect(subject).to match("foo%20bar:12")
|
|
1179
|
-
expect(subject).to match("foo%20bar.baz*")
|
|
1180
|
-
end
|
|
1181
|
-
it "should fail on non vars" do
|
|
1182
|
-
expect(subject).not_to match("!foo")
|
|
1183
|
-
expect(subject).not_to match("*foo")
|
|
1184
|
-
expect(subject).not_to match("fo*o")
|
|
1185
|
-
expect(subject).not_to match("fo:o")
|
|
1186
|
-
expect(subject).not_to match("foo:")
|
|
1187
|
-
end
|
|
1188
|
-
end
|
|
1189
|
-
end
|
|
1190
|
-
end
|
|
1191
|
-
|
|
1192
|
-
describe Addressable::Template::MatchData do
|
|
1193
|
-
let(:template) { Addressable::Template.new('{foo}/{bar}') }
|
|
1194
|
-
subject(:its) { template.match('ab/cd') }
|
|
1195
|
-
its(:uri) { should == Addressable::URI.parse('ab/cd') }
|
|
1196
|
-
its(:template) { should == template }
|
|
1197
|
-
its(:mapping) { should == { 'foo' => 'ab', 'bar' => 'cd' } }
|
|
1198
|
-
its(:variables) { should == ['foo', 'bar'] }
|
|
1199
|
-
its(:keys) { should == ['foo', 'bar'] }
|
|
1200
|
-
its(:names) { should == ['foo', 'bar'] }
|
|
1201
|
-
its(:values) { should == ['ab', 'cd'] }
|
|
1202
|
-
its(:captures) { should == ['ab', 'cd'] }
|
|
1203
|
-
its(:to_a) { should == ['ab/cd', 'ab', 'cd'] }
|
|
1204
|
-
its(:to_s) { should == 'ab/cd' }
|
|
1205
|
-
its(:string) { should == its.to_s }
|
|
1206
|
-
its(:pre_match) { should == "" }
|
|
1207
|
-
its(:post_match) { should == "" }
|
|
1208
|
-
|
|
1209
|
-
describe 'values_at' do
|
|
1210
|
-
it 'returns an array with the values' do
|
|
1211
|
-
expect(its.values_at(0, 2)).to eq(['ab/cd', 'cd'])
|
|
1212
|
-
end
|
|
1213
|
-
it 'allows mixing integer an string keys' do
|
|
1214
|
-
expect(its.values_at('foo', 1)).to eq(['ab', 'ab'])
|
|
1215
|
-
end
|
|
1216
|
-
it 'accepts unknown keys' do
|
|
1217
|
-
expect(its.values_at('baz', 'foo')).to eq([nil, 'ab'])
|
|
1218
|
-
end
|
|
1219
|
-
end
|
|
1220
|
-
|
|
1221
|
-
describe '[]' do
|
|
1222
|
-
context 'string key' do
|
|
1223
|
-
it 'returns the corresponding capture' do
|
|
1224
|
-
expect(its['foo']).to eq('ab')
|
|
1225
|
-
expect(its['bar']).to eq('cd')
|
|
1226
|
-
end
|
|
1227
|
-
it 'returns nil for unknown keys' do
|
|
1228
|
-
expect(its['baz']).to be_nil
|
|
1229
|
-
end
|
|
1230
|
-
end
|
|
1231
|
-
context 'symbol key' do
|
|
1232
|
-
it 'returns the corresponding capture' do
|
|
1233
|
-
expect(its[:foo]).to eq('ab')
|
|
1234
|
-
expect(its[:bar]).to eq('cd')
|
|
1235
|
-
end
|
|
1236
|
-
it 'returns nil for unknown keys' do
|
|
1237
|
-
expect(its[:baz]).to be_nil
|
|
1238
|
-
end
|
|
1239
|
-
end
|
|
1240
|
-
context 'integer key' do
|
|
1241
|
-
it 'returns the full URI for index 0' do
|
|
1242
|
-
expect(its[0]).to eq('ab/cd')
|
|
1243
|
-
end
|
|
1244
|
-
it 'returns the corresponding capture' do
|
|
1245
|
-
expect(its[1]).to eq('ab')
|
|
1246
|
-
expect(its[2]).to eq('cd')
|
|
1247
|
-
end
|
|
1248
|
-
it 'returns nil for unknown keys' do
|
|
1249
|
-
expect(its[3]).to be_nil
|
|
1250
|
-
end
|
|
1251
|
-
end
|
|
1252
|
-
context 'other key' do
|
|
1253
|
-
it 'raises an exception' do
|
|
1254
|
-
expect { its[Object.new] }.to raise_error(TypeError)
|
|
1255
|
-
end
|
|
1256
|
-
end
|
|
1257
|
-
context 'with length' do
|
|
1258
|
-
it 'returns an array starting at index with given length' do
|
|
1259
|
-
expect(its[0, 2]).to eq(['ab/cd', 'ab'])
|
|
1260
|
-
expect(its[2, 1]).to eq(['cd'])
|
|
1261
|
-
end
|
|
1262
|
-
end
|
|
1263
|
-
end
|
|
1264
|
-
end
|