addressable 2.3.2 → 2.8.7

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,3 +1,24 @@
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"
1
22
  require "addressable/template"
2
23
 
3
24
  shared_examples_for 'expands' do |tests|
@@ -5,17 +26,84 @@ shared_examples_for 'expands' do |tests|
5
26
  exp = expansion.is_a?(Array) ? expansion.first : expansion
6
27
  it "#{template} to #{exp}" do
7
28
  tmpl = Addressable::Template.new(template).expand(subject)
8
- if expansion.is_a?(Array)
9
- expansion.any?{|i| i == tmpl.to_str}.should be_true
10
- else
11
- tmpl.to_str.should == expansion
12
- end
29
+ expect(tmpl.to_str).to eq(expansion)
13
30
  end
14
31
  end
15
32
  end
16
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
+
17
105
  describe "Level 1:" do
18
- subject{
106
+ subject {
19
107
  {:var => "value", :hello => "Hello World!"}
20
108
  }
21
109
  it_behaves_like 'expands', {
@@ -25,7 +113,7 @@ describe "Level 1:" do
25
113
  end
26
114
 
27
115
  describe "Level 2" do
28
- subject{
116
+ subject {
29
117
  {
30
118
  :var => "value",
31
119
  :hello => "Hello World!",
@@ -49,7 +137,7 @@ describe "Level 2" do
49
137
  end
50
138
 
51
139
  describe "Level 3" do
52
- subject{
140
+ subject {
53
141
  {
54
142
  :var => "value",
55
143
  :hello => "Hello World!",
@@ -110,14 +198,14 @@ describe "Level 3" do
110
198
  end
111
199
 
112
200
  describe "Level 4" do
113
- subject{
201
+ subject {
114
202
  {
115
203
  :var => "value",
116
204
  :hello => "Hello World!",
117
205
  :path => "/foo/bar",
118
206
  :semi => ";",
119
207
  :list => %w(red green blue),
120
- :keys => {"semi" => ';', "dot" => '.', "comma" => ','}
208
+ :keys => {"semi" => ';', "dot" => '.', :comma => ','}
121
209
  }
122
210
  }
123
211
  context "Expansion with value modifiers" do
@@ -126,22 +214,8 @@ describe "Level 4" do
126
214
  '{var:30}' => 'value',
127
215
  '{list}' => 'red,green,blue',
128
216
  '{list*}' => 'red,green,blue',
129
- '{keys}' => [
130
- 'semi,%3B,dot,.,comma,%2C',
131
- 'dot,.,semi,%3B,comma,%2C',
132
- 'comma,%2C,semi,%3B,dot,.',
133
- 'semi,%3B,comma,%2C,dot,.',
134
- 'dot,.,comma,%2C,semi,%3B',
135
- 'comma,%2C,dot,.,semi,%3B'
136
- ],
137
- '{keys*}' => [
138
- 'semi=%3B,dot=.,comma=%2C',
139
- 'dot=.,semi=%3B,comma=%2C',
140
- 'comma=%2C,semi=%3B,dot=.',
141
- 'semi=%3B,comma=%2C,dot=.',
142
- 'dot=.,comma=%2C,semi=%3B',
143
- 'comma=%2C,dot=.,semi=%3B'
144
- ]
217
+ '{keys}' => 'semi,%3B,dot,.,comma,%2C',
218
+ '{keys*}' => 'semi=%3B,dot=.,comma=%2C',
145
219
  }
146
220
  end
147
221
  context "Operator + with value modifiers" do
@@ -149,22 +223,8 @@ describe "Level 4" do
149
223
  '{+path:6}/here' => '/foo/b/here',
150
224
  '{+list}' => 'red,green,blue',
151
225
  '{+list*}' => 'red,green,blue',
152
- '{+keys}' => [
153
- 'semi,;,dot,.,comma,,',
154
- 'dot,.,semi,;,comma,,',
155
- 'comma,,,semi,;,dot,.',
156
- 'semi,;,comma,,,dot,.',
157
- 'dot,.,comma,,,semi,;',
158
- 'comma,,,dot,.,semi,;'
159
- ],
160
- '{+keys*}' => [
161
- 'semi=;,dot=.,comma=,',
162
- 'dot=.,semi=;,comma=,',
163
- 'comma=,,semi=;,dot=.',
164
- 'semi=;,comma=,,dot=.',
165
- 'dot=.,comma=,,semi=;',
166
- 'comma=,,dot=.,semi=;'
167
- ]
226
+ '{+keys}' => 'semi,;,dot,.,comma,,',
227
+ '{+keys*}' => 'semi=;,dot=.,comma=,',
168
228
  }
169
229
  end
170
230
  context "Operator # with value modifiers" do
@@ -172,22 +232,8 @@ describe "Level 4" do
172
232
  '{#path:6}/here' => '#/foo/b/here',
173
233
  '{#list}' => '#red,green,blue',
174
234
  '{#list*}' => '#red,green,blue',
175
- '{#keys}' => [
176
- '#semi,;,dot,.,comma,,',
177
- '#dot,.,semi,;,comma,,',
178
- '#comma,,,semi,;,dot,.',
179
- '#semi,;,comma,,,dot,.',
180
- '#dot,.,comma,,,semi,;',
181
- '#comma,,,dot,.,semi,;'
182
- ],
183
- '{#keys*}' => [
184
- '#semi=;,dot=.,comma=,',
185
- '#dot=.,semi=;,comma=,',
186
- '#comma=,,semi=;,dot=.',
187
- '#semi=;,comma=,,dot=.',
188
- '#dot=.,comma=,,semi=;',
189
- '#comma=,,dot=.,semi=;'
190
- ]
235
+ '{#keys}' => '#semi,;,dot,.,comma,,',
236
+ '{#keys*}' => '#semi=;,dot=.,comma=,',
191
237
  }
192
238
  end
193
239
  context "Operator . with value modifiers" do
@@ -195,22 +241,8 @@ describe "Level 4" do
195
241
  'X{.var:3}' => 'X.val',
196
242
  'X{.list}' => 'X.red,green,blue',
197
243
  'X{.list*}' => 'X.red.green.blue',
198
- 'X{.keys}' => [
199
- 'X.semi,%3B,dot,.,comma,%2C',
200
- 'X.dot,.,semi,%3B,comma,%2C',
201
- 'X.comma,%2C,semi,%3B,dot,.',
202
- 'X.semi,%3B,comma,%2C,dot,.',
203
- 'X.dot,.,comma,%2C,semi,%3B',
204
- 'X.comma,%2C,dot,.,semi,%3B'
205
- ],
206
- 'X{.keys*}' => [
207
- 'X.semi=%3B.dot=..comma=%2C',
208
- 'X.dot=..semi=%3B.comma=%2C',
209
- 'X.comma=%2C.semi=%3B.dot=.',
210
- 'X.semi=%3B.comma=%2C.dot=.',
211
- 'X.dot=..comma=%2C.semi=%3B',
212
- 'X.comma=%2C.dot=..semi=%3B'
213
- ]
244
+ 'X{.keys}' => 'X.semi,%3B,dot,.,comma,%2C',
245
+ 'X{.keys*}' => 'X.semi=%3B.dot=..comma=%2C',
214
246
  }
215
247
  end
216
248
  context "Operator / with value modifiers" do
@@ -219,22 +251,8 @@ describe "Level 4" do
219
251
  '{/list}' => '/red,green,blue',
220
252
  '{/list*}' => '/red/green/blue',
221
253
  '{/list*,path:4}' => '/red/green/blue/%2Ffoo',
222
- '{/keys}' => [
223
- '/semi,%3B,dot,.,comma,%2C',
224
- '/dot,.,semi,%3B,comma,%2C',
225
- '/comma,%2C,semi,%3B,dot,.',
226
- '/semi,%3B,comma,%2C,dot,.',
227
- '/dot,.,comma,%2C,semi,%3B',
228
- '/comma,%2C,dot,.,semi,%3B'
229
- ],
230
- '{/keys*}' => [
231
- '/semi=%3B/dot=./comma=%2C',
232
- '/dot=./semi=%3B/comma=%2C',
233
- '/comma=%2C/semi=%3B/dot=.',
234
- '/semi=%3B/comma=%2C/dot=.',
235
- '/dot=./comma=%2C/semi=%3B',
236
- '/comma=%2C/dot=./semi=%3B'
237
- ]
254
+ '{/keys}' => '/semi,%3B,dot,.,comma,%2C',
255
+ '{/keys*}' => '/semi=%3B/dot=./comma=%2C',
238
256
  }
239
257
  end
240
258
  context "Operator ; with value modifiers" do
@@ -242,22 +260,8 @@ describe "Level 4" do
242
260
  '{;hello:5}' => ';hello=Hello',
243
261
  '{;list}' => ';list=red,green,blue',
244
262
  '{;list*}' => ';list=red;list=green;list=blue',
245
- '{;keys}' => [
246
- ';keys=semi,%3B,dot,.,comma,%2C',
247
- ';keys=dot,.,semi,%3B,comma,%2C',
248
- ';keys=comma,%2C,semi,%3B,dot,.',
249
- ';keys=semi,%3B,comma,%2C,dot,.',
250
- ';keys=dot,.,comma,%2C,semi,%3B',
251
- ';keys=comma,%2C,dot,.,semi,%3B'
252
- ],
253
- '{;keys*}' => [
254
- ';semi=%3B;dot=.;comma=%2C',
255
- ';dot=.;semi=%3B;comma=%2C',
256
- ';comma=%2C;semi=%3B;dot=.',
257
- ';semi=%3B;comma=%2C;dot=.',
258
- ';dot=.;comma=%2C;semi=%3B',
259
- ';comma=%2C;dot=.;semi=%3B'
260
- ]
263
+ '{;keys}' => ';keys=semi,%3B,dot,.,comma,%2C',
264
+ '{;keys*}' => ';semi=%3B;dot=.;comma=%2C',
261
265
  }
262
266
  end
263
267
  context "Operator ? with value modifiers" do
@@ -265,22 +269,8 @@ describe "Level 4" do
265
269
  '{?var:3}' => '?var=val',
266
270
  '{?list}' => '?list=red,green,blue',
267
271
  '{?list*}' => '?list=red&list=green&list=blue',
268
- '{?keys}' => [
269
- '?keys=semi,%3B,dot,.,comma,%2C',
270
- '?keys=dot,.,semi,%3B,comma,%2C',
271
- '?keys=comma,%2C,semi,%3B,dot,.',
272
- '?keys=semi,%3B,comma,%2C,dot,.',
273
- '?keys=dot,.,comma,%2C,semi,%3B',
274
- '?keys=comma,%2C,dot,.,semi,%3B'
275
- ],
276
- '{?keys*}' => [
277
- '?semi=%3B&dot=.&comma=%2C',
278
- '?dot=.&semi=%3B&comma=%2C',
279
- '?comma=%2C&semi=%3B&dot=.',
280
- '?semi=%3B&comma=%2C&dot=.',
281
- '?dot=.&comma=%2C&semi=%3B',
282
- '?comma=%2C&dot=.&semi=%3B'
283
- ]
272
+ '{?keys}' => '?keys=semi,%3B,dot,.,comma,%2C',
273
+ '{?keys*}' => '?semi=%3B&dot=.&comma=%2C',
284
274
  }
285
275
  end
286
276
  context "Operator & with value modifiers" do
@@ -288,31 +278,17 @@ describe "Level 4" do
288
278
  '{&var:3}' => '&var=val',
289
279
  '{&list}' => '&list=red,green,blue',
290
280
  '{&list*}' => '&list=red&list=green&list=blue',
291
- '{&keys}' => [
292
- '&keys=semi,%3B,dot,.,comma,%2C',
293
- '&keys=dot,.,semi,%3B,comma,%2C',
294
- '&keys=comma,%2C,semi,%3B,dot,.',
295
- '&keys=semi,%3B,comma,%2C,dot,.',
296
- '&keys=dot,.,comma,%2C,semi,%3B',
297
- '&keys=comma,%2C,dot,.,semi,%3B'
298
- ],
299
- '{&keys*}' => [
300
- '&semi=%3B&dot=.&comma=%2C',
301
- '&dot=.&semi=%3B&comma=%2C',
302
- '&comma=%2C&semi=%3B&dot=.',
303
- '&semi=%3B&comma=%2C&dot=.',
304
- '&dot=.&comma=%2C&semi=%3B',
305
- '&comma=%2C&dot=.&semi=%3B'
306
- ]
281
+ '{&keys}' => '&keys=semi,%3B,dot,.,comma,%2C',
282
+ '{&keys*}' => '&semi=%3B&dot=.&comma=%2C',
307
283
  }
308
284
  end
309
285
  end
310
286
  describe "Modifiers" do
311
- subject{
287
+ subject {
312
288
  {
313
289
  :var => "value",
314
290
  :semi => ";",
315
- :year => %w(1965 2000 2012),
291
+ :year => [1965, 2000, 2012],
316
292
  :dom => %w(example com)
317
293
  }
318
294
  }
@@ -333,7 +309,7 @@ describe "Modifiers" do
333
309
  end
334
310
  end
335
311
  describe "Expansion" do
336
- subject{
312
+ subject {
337
313
  {
338
314
  :count => ["one", "two", "three"],
339
315
  :dom => ["example", "com"],
@@ -345,7 +321,7 @@ describe "Expansion" do
345
321
  :base => "http://example.com/home/",
346
322
  :path => "/foo/bar",
347
323
  :list => ["red", "green", "blue"],
348
- :keys => {"semi" => ";","dot" => ".","comma" => ","},
324
+ :keys => {"semi" => ";","dot" => ".",:comma => ","},
349
325
  :v => "6",
350
326
  :x => "1024",
351
327
  :y => "768",
@@ -383,22 +359,8 @@ describe "Expansion" do
383
359
  '{var:30}' => 'value',
384
360
  '{list}' => 'red,green,blue',
385
361
  '{list*}' => 'red,green,blue',
386
- '{keys}' => [
387
- 'semi,%3B,dot,.,comma,%2C',
388
- 'dot,.,semi,%3B,comma,%2C',
389
- 'comma,%2C,semi,%3B,dot,.',
390
- 'semi,%3B,comma,%2C,dot,.',
391
- 'dot,.,comma,%2C,semi,%3B',
392
- 'comma,%2C,dot,.,semi,%3B'
393
- ],
394
- '{keys*}' => [
395
- 'semi=%3B,dot=.,comma=%2C',
396
- 'dot=.,semi=%3B,comma=%2C',
397
- 'comma=%2C,semi=%3B,dot=.',
398
- 'semi=%3B,comma=%2C,dot=.',
399
- 'dot=.,comma=%2C,semi=%3B',
400
- 'comma=%2C,dot=.,semi=%3B'
401
- ]
362
+ '{keys}' => 'semi,%3B,dot,.,comma,%2C',
363
+ '{keys*}' => 'semi=%3B,dot=.,comma=%2C',
402
364
  }
403
365
  end
404
366
  context "reserved expansion (+)" do
@@ -418,22 +380,8 @@ describe "Expansion" do
418
380
  '{+path:6}/here' => '/foo/b/here',
419
381
  '{+list}' => 'red,green,blue',
420
382
  '{+list*}' => 'red,green,blue',
421
- '{+keys}' => [
422
- 'semi,;,dot,.,comma,,',
423
- 'dot,.,semi,;,comma,,',
424
- 'comma,,,semi,;,dot,.',
425
- 'semi,;,comma,,,dot,.',
426
- 'dot,.,comma,,,semi,;',
427
- 'comma,,,dot,.,semi,;'
428
- ],
429
- '{+keys*}' => [
430
- 'semi=;,dot=.,comma=,',
431
- 'dot=.,semi=;,comma=,',
432
- 'comma=,,semi=;,dot=.',
433
- 'semi=;,comma=,,dot=.',
434
- 'dot=.,comma=,,semi=;',
435
- 'comma=,,dot=.,semi=;'
436
- ]
383
+ '{+keys}' => 'semi,;,dot,.,comma,,',
384
+ '{+keys*}' => 'semi=;,dot=.,comma=,',
437
385
  }
438
386
  end
439
387
  context "fragment expansion (#)" do
@@ -448,22 +396,8 @@ describe "Expansion" do
448
396
  '{#path:6}/here' => '#/foo/b/here',
449
397
  '{#list}' => '#red,green,blue',
450
398
  '{#list*}' => '#red,green,blue',
451
- '{#keys}' => [
452
- '#semi,;,dot,.,comma,,',
453
- '#dot,.,semi,;,comma,,',
454
- '#comma,,,semi,;,dot,.',
455
- '#semi,;,comma,,,dot,.',
456
- '#dot,.,comma,,,semi,;',
457
- '#comma,,,dot,.,semi,;'
458
- ],
459
- '{#keys*}' => [
460
- '#semi=;,dot=.,comma=,',
461
- '#dot=.,semi=;,comma=,',
462
- '#comma=,,semi=;,dot=.',
463
- '#semi=;,comma=,,dot=.',
464
- '#dot=.,comma=,,semi=;',
465
- '#comma=,,dot=.,semi=;'
466
- ]
399
+ '{#keys}' => '#semi,;,dot,.,comma,,',
400
+ '{#keys*}' => '#semi=;,dot=.,comma=,',
467
401
  }
468
402
  end
469
403
  context "label expansion (.)" do
@@ -478,22 +412,8 @@ describe "Expansion" do
478
412
  'X{.var:3}' => 'X.val',
479
413
  'X{.list}' => 'X.red,green,blue',
480
414
  'X{.list*}' => 'X.red.green.blue',
481
- 'X{.keys}' => [
482
- 'X.semi,%3B,dot,.,comma,%2C',
483
- 'X.dot,.,semi,%3B,comma,%2C',
484
- 'X.comma,%2C,semi,%3B,dot,.',
485
- 'X.semi,%3B,comma,%2C,dot,.',
486
- 'X.dot,.,comma,%2C,semi,%3B',
487
- 'X.comma,%2C,dot,.,semi,%3B'
488
- ],
489
- 'X{.keys*}' => [
490
- 'X.semi=%3B.dot=..comma=%2C',
491
- 'X.dot=..semi=%3B.comma=%2C',
492
- 'X.comma=%2C.semi=%3B.dot=.',
493
- 'X.semi=%3B.comma=%2C.dot=.',
494
- 'X.dot=..comma=%2C.semi=%3B',
495
- 'X.comma=%2C.dot=..semi=%3B'
496
- ],
415
+ 'X{.keys}' => 'X.semi,%3B,dot,.,comma,%2C',
416
+ 'X{.keys*}' => 'X.semi=%3B.dot=..comma=%2C',
497
417
  'X{.empty_keys}' => 'X',
498
418
  'X{.empty_keys*}' => 'X'
499
419
  }
@@ -512,22 +432,8 @@ describe "Expansion" do
512
432
  '{/list}' => '/red,green,blue',
513
433
  '{/list*}' => '/red/green/blue',
514
434
  '{/list*,path:4}' => '/red/green/blue/%2Ffoo',
515
- '{/keys}' => [
516
- '/semi,%3B,dot,.,comma,%2C',
517
- '/dot,.,semi,%3B,comma,%2C',
518
- '/comma,%2C,semi,%3B,dot,.',
519
- '/semi,%3B,comma,%2C,dot,.',
520
- '/dot,.,comma,%2C,semi,%3B',
521
- '/comma,%2C,dot,.,semi,%3B'
522
- ],
523
- '{/keys*}' => [
524
- '/semi=%3B/dot=./comma=%2C',
525
- '/dot=./semi=%3B/comma=%2C',
526
- '/comma=%2C/semi=%3B/dot=.',
527
- '/semi=%3B/comma=%2C/dot=.',
528
- '/dot=./comma=%2C/semi=%3B',
529
- '/comma=%2C/dot=./semi=%3B'
530
- ]
435
+ '{/keys}' => '/semi,%3B,dot,.,comma,%2C',
436
+ '{/keys*}' => '/semi=%3B/dot=./comma=%2C',
531
437
  }
532
438
  end
533
439
  context "path-style expansion (;)" do
@@ -543,22 +449,8 @@ describe "Expansion" do
543
449
  '{;hello:5}' => ';hello=Hello',
544
450
  '{;list}' => ';list=red,green,blue',
545
451
  '{;list*}' => ';list=red;list=green;list=blue',
546
- '{;keys}' => [
547
- ';keys=semi,%3B,dot,.,comma,%2C',
548
- ';keys=dot,.,semi,%3B,comma,%2C',
549
- ';keys=comma,%2C,semi,%3B,dot,.',
550
- ';keys=semi,%3B,comma,%2C,dot,.',
551
- ';keys=dot,.,comma,%2C,semi,%3B',
552
- ';keys=comma,%2C,dot,.,semi,%3B'
553
- ],
554
- '{;keys*}' => [
555
- ';semi=%3B;dot=.;comma=%2C',
556
- ';dot=.;semi=%3B;comma=%2C',
557
- ';comma=%2C;semi=%3B;dot=.',
558
- ';semi=%3B;comma=%2C;dot=.',
559
- ';dot=.;comma=%2C;semi=%3B',
560
- ';comma=%2C;dot=.;semi=%3B'
561
- ]
452
+ '{;keys}' => ';keys=semi,%3B,dot,.,comma,%2C',
453
+ '{;keys*}' => ';semi=%3B;dot=.;comma=%2C',
562
454
  }
563
455
  end
564
456
  context "form query expansion (?)" do
@@ -571,22 +463,8 @@ describe "Expansion" do
571
463
  '{?var:3}' => '?var=val',
572
464
  '{?list}' => '?list=red,green,blue',
573
465
  '{?list*}' => '?list=red&list=green&list=blue',
574
- '{?keys}' => [
575
- '?keys=semi,%3B,dot,.,comma,%2C',
576
- '?keys=dot,.,semi,%3B,comma,%2C',
577
- '?keys=comma,%2C,semi,%3B,dot,.',
578
- '?keys=semi,%3B,comma,%2C,dot,.',
579
- '?keys=dot,.,comma,%2C,semi,%3B',
580
- '?keys=comma,%2C,dot,.,semi,%3B'
581
- ],
582
- '{?keys*}' => [
583
- '?semi=%3B&dot=.&comma=%2C',
584
- '?dot=.&semi=%3B&comma=%2C',
585
- '?comma=%2C&semi=%3B&dot=.',
586
- '?semi=%3B&comma=%2C&dot=.',
587
- '?dot=.&comma=%2C&semi=%3B',
588
- '?comma=%2C&dot=.&semi=%3B'
589
- ]
466
+ '{?keys}' => '?keys=semi,%3B,dot,.,comma,%2C',
467
+ '{?keys*}' => '?semi=%3B&dot=.&comma=%2C',
590
468
  }
591
469
  end
592
470
  context "form query expansion (&)" do
@@ -599,24 +477,17 @@ describe "Expansion" do
599
477
  '{&var:3}' => '&var=val',
600
478
  '{&list}' => '&list=red,green,blue',
601
479
  '{&list*}' => '&list=red&list=green&list=blue',
602
- '{&keys}' => [
603
- '&keys=semi,%3B,dot,.,comma,%2C',
604
- '&keys=dot,.,semi,%3B,comma,%2C',
605
- '&keys=comma,%2C,semi,%3B,dot,.',
606
- '&keys=semi,%3B,comma,%2C,dot,.',
607
- '&keys=dot,.,comma,%2C,semi,%3B',
608
- '&keys=comma,%2C,dot,.,semi,%3B'
609
- ],
610
- '{&keys*}' => [
611
- '&semi=%3B&dot=.&comma=%2C',
612
- '&dot=.&semi=%3B&comma=%2C',
613
- '&comma=%2C&semi=%3B&dot=.',
614
- '&semi=%3B&comma=%2C&dot=.',
615
- '&dot=.&comma=%2C&semi=%3B',
616
- '&comma=%2C&dot=.&semi=%3B'
617
- ]
480
+ '{&keys}' => '&keys=semi,%3B,dot,.,comma,%2C',
481
+ '{&keys*}' => '&semi=%3B&dot=.&comma=%2C',
618
482
  }
619
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
620
491
  end
621
492
 
622
493
  class ExampleTwoProcessor
@@ -640,8 +511,28 @@ class ExampleTwoProcessor
640
511
  end
641
512
  end
642
513
 
514
+ class DumbProcessor
515
+ def self.match(name)
516
+ return ".*?" if name == "first"
517
+ end
518
+ end
643
519
 
644
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
+
645
536
  describe "Matching" do
646
537
  let(:uri){
647
538
  Addressable::URI.parse(
@@ -657,55 +548,97 @@ describe Addressable::Template do
657
548
  let(:uri4){
658
549
  Addressable::URI.parse("http://example.com/?a=1&b=2&c=3&first=foo")
659
550
  }
551
+ let(:uri5){
552
+ "http://example.com/foo"
553
+ }
660
554
  context "first uri with ExampleTwoProcessor" do
661
- subject{
662
- match = Addressable::Template.new(
555
+ subject {
556
+ Addressable::Template.new(
663
557
  "http://example.com/search/{query}/"
664
558
  ).match(uri, ExampleTwoProcessor)
665
559
  }
666
- its(:variables){ should == ["query"]}
667
- its(:captures){ should == ["an example search query"]}
560
+ its(:variables){ should == ["query"] }
561
+ its(:captures){ should == ["an example search query"] }
668
562
  end
669
563
 
670
564
  context "second uri with ExampleTwoProcessor" do
671
- subject{
672
- match = Addressable::Template.new(
565
+ subject {
566
+ Addressable::Template.new(
673
567
  "http://example.com/{first}/{+second}/"
674
568
  ).match(uri2, ExampleTwoProcessor)
675
569
  }
676
- its(:variables){ should == ["first", "second"]}
570
+ its(:variables){ should == ["first", "second"] }
677
571
  its(:captures){ should == ["a", "b/c"] }
678
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
+
679
584
  context "second uri" do
680
- subject{
681
- match = Addressable::Template.new(
585
+ subject {
586
+ Addressable::Template.new(
682
587
  "http://example.com/{first}{/second*}/"
683
588
  ).match(uri2)
684
589
  }
685
- its(:variables){ should == ["first", "second"]}
590
+ its(:variables){ should == ["first", "second"] }
686
591
  its(:captures){ should == ["a", ["b","c"]] }
687
592
  end
688
593
  context "third uri" do
689
- subject{
690
- match = Addressable::Template.new(
594
+ subject {
595
+ Addressable::Template.new(
691
596
  "http://example.com/{;hash*,first}"
692
597
  ).match(uri3)
693
598
  }
694
- its(:variables){ should == ["hash", "first"]}
599
+ its(:variables){ should == ["hash", "first"] }
695
600
  its(:captures){ should == [
696
601
  {"a" => "1", "b" => "2", "c" => "3", "first" => "foo"}, nil] }
697
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.
698
606
  context "fourth uri" do
699
- subject{
700
- match = Addressable::Template.new(
607
+ subject {
608
+ Addressable::Template.new(
701
609
  "http://example.com/{?hash*,first}"
702
610
  ).match(uri4)
703
611
  }
704
- its(:variables){ should == ["hash", "first"]}
612
+ its(:variables){ should == ["hash", "first"] }
705
613
  its(:captures){ should == [
706
614
  {"a" => "1", "b" => "2", "c" => "3", "first"=> "foo"}, nil] }
707
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
708
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
+
709
642
  describe "extract" do
710
643
  let(:template) {
711
644
  Addressable::Template.new(
@@ -713,97 +646,287 @@ describe Addressable::Template do
713
646
  )
714
647
  }
715
648
  let(:uri){ "http://example.com/a/b/c/?one=1&two=2#foo" }
716
- it "should be able to extract" do
717
- template.extract(uri).should == {
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({
718
652
  "host" => "example.com",
719
653
  "segments" => %w(a b c),
720
654
  "one" => "1",
721
655
  "bogus" => nil,
722
656
  "two" => "2",
723
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}/")
724
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
725
815
  end
726
816
  end
727
- describe "Partial expand" do
817
+ describe "Partial expand with strings" do
728
818
  context "partial_expand with two simple values" do
729
- subject{
819
+ subject {
730
820
  Addressable::Template.new("http://example.com/{one}/{two}/")
731
821
  }
732
822
  it "builds a new pattern" do
733
- subject.partial_expand("one" => "1").pattern.should ==
823
+ expect(subject.partial_expand("one" => "1").pattern).to eq(
734
824
  "http://example.com/1/{two}/"
825
+ )
735
826
  end
736
827
  end
737
828
  context "partial_expand query with missing param in middle" do
738
- subject{
829
+ subject {
739
830
  Addressable::Template.new("http://example.com/{?one,two,three}/")
740
831
  }
741
832
  it "builds a new pattern" do
742
- subject.partial_expand("one" => "1", "three" => "3").pattern.should ==
833
+ expect(subject.partial_expand("one" => "1", "three" => "3").pattern).to eq(
743
834
  "http://example.com/?one=1{&two}&three=3/"
835
+ )
744
836
  end
745
837
  end
746
838
  context "partial_expand with query string" do
747
- subject{
839
+ subject {
748
840
  Addressable::Template.new("http://example.com/{?two,one}/")
749
841
  }
750
842
  it "builds a new pattern" do
751
- subject.partial_expand("one" => "1").pattern.should ==
752
- "http://example.com/{?two}&one=1/"
843
+ expect(subject.partial_expand("one" => "1").pattern).to eq(
844
+ "http://example.com/?one=1{&two}/"
845
+ )
753
846
  end
754
847
  end
755
848
  context "partial_expand with path operator" do
756
- subject{
849
+ subject {
757
850
  Addressable::Template.new("http://example.com{/one,two}/")
758
851
  }
759
852
  it "builds a new pattern" do
760
- subject.partial_expand("one" => "1").pattern.should ==
853
+ expect(subject.partial_expand("one" => "1").pattern).to eq(
761
854
  "http://example.com/1{/two}/"
855
+ )
762
856
  end
763
857
  end
764
858
  end
765
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
766
885
  context "expand with a processor" do
767
- subject{
886
+ subject {
768
887
  Addressable::Template.new("http://example.com/search/{query}/")
769
888
  }
770
889
  it "processes spaces" do
771
- subject.expand({"query" => "an example search query"},
772
- ExampleTwoProcessor).to_str.should ==
890
+ expect(subject.expand({"query" => "an example search query"},
891
+ ExampleTwoProcessor).to_str).to eq(
773
892
  "http://example.com/search/an+example+search+query/"
893
+ )
774
894
  end
775
895
  it "validates" do
776
- lambda{
896
+ expect{
777
897
  subject.expand({"query" => "Bogus!"},
778
898
  ExampleTwoProcessor).to_str
779
- }.should raise_error(Addressable::Template::InvalidTemplateValueError)
899
+ }.to raise_error(Addressable::Template::InvalidTemplateValueError)
780
900
  end
781
901
  end
782
902
  context "partial_expand query with missing param in middle" do
783
- subject{
903
+ subject {
784
904
  Addressable::Template.new("http://example.com/{?one,two,three}/")
785
905
  }
786
906
  it "builds a new pattern" do
787
- subject.partial_expand("one" => "1", "three" => "3").pattern.should ==
907
+ expect(subject.partial_expand("one" => "1", "three" => "3").pattern).to eq(
788
908
  "http://example.com/?one=1{&two}&three=3/"
909
+ )
789
910
  end
790
911
  end
791
912
  context "partial_expand with query string" do
792
- subject{
913
+ subject {
793
914
  Addressable::Template.new("http://example.com/{?two,one}/")
794
915
  }
795
916
  it "builds a new pattern" do
796
- subject.partial_expand("one" => "1").pattern.should ==
797
- "http://example.com/{?two}&one=1/"
917
+ expect(subject.partial_expand("one" => "1").pattern).to eq(
918
+ "http://example.com/?one=1{&two}/"
919
+ )
798
920
  end
799
921
  end
800
922
  context "partial_expand with path operator" do
801
- subject{
923
+ subject {
802
924
  Addressable::Template.new("http://example.com{/one,two}/")
803
925
  }
804
926
  it "builds a new pattern" do
805
- subject.partial_expand("one" => "1").pattern.should ==
927
+ expect(subject.partial_expand("one" => "1").pattern).to eq(
806
928
  "http://example.com/1{/two}/"
929
+ )
807
930
  end
808
931
  end
809
932
  end
@@ -812,11 +935,20 @@ describe Addressable::Template do
812
935
  subject { Addressable::Template.new("foo{foo}/{bar}baz") }
813
936
  it "can match" do
814
937
  data = subject.match("foofoo/bananabaz")
815
- data.mapping["foo"].should == "foo"
816
- data.mapping["bar"].should == "banana"
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)
817
949
  end
818
950
  it "lists vars" do
819
- subject.variables.should == ["foo", "bar"]
951
+ expect(subject.variables).to eq(["foo", "bar"])
820
952
  end
821
953
  end
822
954
 
@@ -824,11 +956,27 @@ describe Addressable::Template do
824
956
  subject { Addressable::Template.new("foo{+foo}{#bar}baz") }
825
957
  it "can match" do
826
958
  data = subject.match("foo/test/banana#bazbaz")
827
- data.mapping["foo"].should == "/test/banana"
828
- data.mapping["bar"].should == "baz"
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")
829
977
  end
830
978
  it "lists vars" do
831
- subject.variables.should == ["foo", "bar"]
979
+ expect(subject.variables).to eq(["foo", "bar"])
832
980
  end
833
981
  end
834
982
 
@@ -837,56 +985,56 @@ describe Addressable::Template do
837
985
  subject { Addressable::Template.new("foo{foo,bar}baz") }
838
986
  it "can match" do
839
987
  data = subject.match("foofoo,barbaz")
840
- data.mapping["foo"].should == "foo"
841
- data.mapping["bar"].should == "bar"
988
+ expect(data.mapping["foo"]).to eq("foo")
989
+ expect(data.mapping["bar"]).to eq("bar")
842
990
  end
843
991
  it "lists vars" do
844
- subject.variables.should == ["foo", "bar"]
992
+ expect(subject.variables).to eq(["foo", "bar"])
845
993
  end
846
994
  end
847
995
  context "+ operator" do
848
996
  subject { Addressable::Template.new("foo{+foo,bar}baz") }
849
997
  it "can match" do
850
998
  data = subject.match("foofoo/bar,barbaz")
851
- data.mapping["bar"].should == "foo/bar,bar"
852
- data.mapping["foo"].should == ""
999
+ expect(data.mapping["bar"]).to eq("foo/bar,bar")
1000
+ expect(data.mapping["foo"]).to eq("")
853
1001
  end
854
1002
  it "lists vars" do
855
- subject.variables.should == ["foo", "bar"]
1003
+ expect(subject.variables).to eq(["foo", "bar"])
856
1004
  end
857
1005
  end
858
1006
  context ". operator" do
859
1007
  subject { Addressable::Template.new("foo{.foo,bar}baz") }
860
1008
  it "can match" do
861
1009
  data = subject.match("foo.foo.barbaz")
862
- data.mapping["foo"].should == "foo"
863
- data.mapping["bar"].should == "bar"
1010
+ expect(data.mapping["foo"]).to eq("foo")
1011
+ expect(data.mapping["bar"]).to eq("bar")
864
1012
  end
865
1013
  it "lists vars" do
866
- subject.variables.should == ["foo", "bar"]
1014
+ expect(subject.variables).to eq(["foo", "bar"])
867
1015
  end
868
1016
  end
869
1017
  context "/ operator" do
870
1018
  subject { Addressable::Template.new("foo{/foo,bar}baz") }
871
1019
  it "can match" do
872
1020
  data = subject.match("foo/foo/barbaz")
873
- data.mapping["foo"].should == "foo"
874
- data.mapping["bar"].should == "bar"
1021
+ expect(data.mapping["foo"]).to eq("foo")
1022
+ expect(data.mapping["bar"]).to eq("bar")
875
1023
  end
876
1024
  it "lists vars" do
877
- subject.variables.should == ["foo", "bar"]
1025
+ expect(subject.variables).to eq(["foo", "bar"])
878
1026
  end
879
1027
  end
880
1028
  context "; operator" do
881
1029
  subject { Addressable::Template.new("foo{;foo,bar,baz}baz") }
882
1030
  it "can match" do
883
1031
  data = subject.match("foo;foo=bar%20baz;bar=foo;bazbaz")
884
- data.mapping["foo"].should == "bar baz"
885
- data.mapping["bar"].should == "foo"
886
- data.mapping["baz"].should == ""
1032
+ expect(data.mapping["foo"]).to eq("bar baz")
1033
+ expect(data.mapping["bar"]).to eq("foo")
1034
+ expect(data.mapping["baz"]).to eq("")
887
1035
  end
888
1036
  it "lists vars" do
889
- subject.variables.should == %w(foo bar baz)
1037
+ expect(subject.variables).to eq(%w(foo bar baz))
890
1038
  end
891
1039
  end
892
1040
  context "? operator" do
@@ -894,22 +1042,55 @@ describe Addressable::Template do
894
1042
  subject { Addressable::Template.new("foo{?foo,bar}baz") }
895
1043
  it "can match" do
896
1044
  data = subject.match("foo?foo=bar%20baz&bar=foobaz")
897
- data.mapping["foo"].should == "bar baz"
898
- data.mapping["bar"].should == "foo"
1045
+ expect(data.mapping["foo"]).to eq("bar baz")
1046
+ expect(data.mapping["bar"]).to eq("foo")
899
1047
  end
900
1048
  it "lists vars" do
901
- subject.variables.should == %w(foo bar)
1049
+ expect(subject.variables).to eq(%w(foo bar))
902
1050
  end
903
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
+
904
1085
  context "issue #71" do
905
1086
  subject { Addressable::Template.new("http://cyberscore.dev/api/users{?username}") }
906
1087
  it "can match" do
907
1088
  data = subject.match("http://cyberscore.dev/api/users?username=foobaz")
908
- data.mapping["username"].should == "foobaz"
1089
+ expect(data.mapping["username"]).to eq("foobaz")
909
1090
  end
910
1091
  it "lists vars" do
911
- subject.variables.should == %w(username)
912
- subject.keys.should == %w(username)
1092
+ expect(subject.variables).to eq(%w(username))
1093
+ expect(subject.keys).to eq(%w(username))
913
1094
  end
914
1095
  end
915
1096
  end
@@ -917,11 +1098,11 @@ describe Addressable::Template do
917
1098
  subject { Addressable::Template.new("foo{&foo,bar}baz") }
918
1099
  it "can match" do
919
1100
  data = subject.match("foo&foo=bar%20baz&bar=foobaz")
920
- data.mapping["foo"].should == "bar baz"
921
- data.mapping["bar"].should == "foo"
1101
+ expect(data.mapping["foo"]).to eq("bar baz")
1102
+ expect(data.mapping["bar"]).to eq("foo")
922
1103
  end
923
1104
  it "lists vars" do
924
- subject.variables.should == %w(foo bar)
1105
+ expect(subject.variables).to eq(%w(foo bar))
925
1106
  end
926
1107
  end
927
1108
  end
@@ -931,70 +1112,152 @@ describe Addressable::Template do
931
1112
  context "EXPRESSION" do
932
1113
  subject { Addressable::Template::EXPRESSION }
933
1114
  it "should be able to match an expression" do
934
- subject.should match("{foo}")
935
- subject.should match("{foo,9}")
936
- subject.should match("{foo.bar,baz}")
937
- subject.should match("{+foo.bar,baz}")
938
- subject.should match("{foo,foo%20bar}")
939
- subject.should match("{#foo:20,baz*}")
940
- subject.should match("stuff{#foo:20,baz*}things")
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")
941
1122
  end
942
1123
  it "should fail on non vars" do
943
- subject.should_not match("!{foo")
944
- subject.should_not match("{foo.bar.}")
945
- subject.should_not match("!{}")
1124
+ expect(subject).not_to match("!{foo")
1125
+ expect(subject).not_to match("{foo.bar.}")
1126
+ expect(subject).not_to match("!{}")
946
1127
  end
947
1128
  end
948
1129
  context "VARNAME" do
949
1130
  subject { Addressable::Template::VARNAME }
950
1131
  it "should be able to match a variable" do
951
- subject.should match("foo")
952
- subject.should match("9")
953
- subject.should match("foo.bar")
954
- subject.should match("foo_bar")
955
- subject.should match("foo_bar.baz")
956
- subject.should match("foo%20bar")
957
- subject.should match("foo%20bar.baz")
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")
958
1139
  end
959
1140
  it "should fail on non vars" do
960
- subject.should_not match("!foo")
961
- subject.should_not match("foo.bar.")
962
- subject.should_not match("foo%2%00bar")
963
- subject.should_not match("foo_ba%r")
964
- subject.should_not match("foo_bar*")
965
- subject.should_not match("foo_bar:20")
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
966
1155
  end
967
1156
  end
968
1157
  context "VARIABLE_LIST" do
969
1158
  subject { Addressable::Template::VARIABLE_LIST }
970
1159
  it "should be able to match a variable list" do
971
- subject.should match("foo,bar")
972
- subject.should match("foo")
973
- subject.should match("foo,bar*,baz")
974
- subject.should match("foo.bar,bar_baz*,baz:12")
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")
975
1164
  end
976
1165
  it "should fail on non vars" do
977
- subject.should_not match(",foo,bar*,baz")
978
- subject.should_not match("foo,*bar,baz")
979
- subject.should_not match("foo,,bar*,baz")
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")
980
1169
  end
981
1170
  end
982
1171
  context "VARSPEC" do
983
1172
  subject { Addressable::Template::VARSPEC }
984
1173
  it "should be able to match a variable with modifier" do
985
- subject.should match("9:8")
986
- subject.should match("foo.bar*")
987
- subject.should match("foo_bar:12")
988
- subject.should match("foo_bar.baz*")
989
- subject.should match("foo%20bar:12")
990
- subject.should match("foo%20bar.baz*")
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*")
991
1180
  end
992
1181
  it "should fail on non vars" do
993
- subject.should_not match("!foo")
994
- subject.should_not match("*foo")
995
- subject.should_not match("fo*o")
996
- subject.should_not match("fo:o")
997
- subject.should_not match("foo:")
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'])
998
1261
  end
999
1262
  end
1000
1263
  end