addressable 2.8.0 → 2.9.0

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