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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +189 -28
- data/Gemfile +22 -8
- data/README.md +95 -61
- data/Rakefile +16 -16
- data/addressable.gemspec +28 -0
- data/lib/addressable/idna/native.rb +31 -8
- data/lib/addressable/idna/pure.rb +49 -202
- data/lib/addressable/idna.rb +3 -2
- data/lib/addressable/template.rb +255 -64
- data/lib/addressable/uri.rb +663 -306
- data/lib/addressable/version.rb +5 -4
- data/lib/addressable.rb +4 -0
- data/spec/addressable/idna_spec.rb +116 -45
- data/spec/addressable/net_http_compat_spec.rb +6 -3
- data/spec/addressable/security_spec.rb +58 -0
- data/spec/addressable/template_spec.rb +645 -382
- data/spec/addressable/uri_spec.rb +3014 -1238
- data/spec/spec_helper.rb +33 -0
- data/tasks/clobber.rake +2 -0
- data/tasks/gem.rake +27 -17
- data/tasks/git.rake +3 -1
- data/tasks/metrics.rake +2 -0
- data/tasks/profile.rake +72 -0
- data/tasks/rspec.rake +10 -45
- data/tasks/yard.rake +2 -0
- metadata +75 -73
- data/tasks/rubyforge.rake +0 -89
- data/website/index.html +0 -110
|
@@ -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
|
-
|
|
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" => '.',
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =>
|
|
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" => "."
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
717
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
752
|
-
"http://example.com
|
|
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.
|
|
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.
|
|
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
|
-
|
|
896
|
+
expect{
|
|
777
897
|
subject.expand({"query" => "Bogus!"},
|
|
778
898
|
ExampleTwoProcessor).to_str
|
|
779
|
-
}.
|
|
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.
|
|
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.
|
|
797
|
-
"http://example.com
|
|
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.
|
|
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"].
|
|
816
|
-
data.mapping["bar"].
|
|
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.
|
|
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"].
|
|
828
|
-
data.mapping["bar"].
|
|
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.
|
|
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"].
|
|
841
|
-
data.mapping["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.
|
|
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"].
|
|
852
|
-
data.mapping["foo"].
|
|
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.
|
|
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"].
|
|
863
|
-
data.mapping["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.
|
|
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"].
|
|
874
|
-
data.mapping["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.
|
|
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"].
|
|
885
|
-
data.mapping["bar"].
|
|
886
|
-
data.mapping["baz"].
|
|
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.
|
|
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"].
|
|
898
|
-
data.mapping["bar"].
|
|
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.
|
|
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"].
|
|
1089
|
+
expect(data.mapping["username"]).to eq("foobaz")
|
|
909
1090
|
end
|
|
910
1091
|
it "lists vars" do
|
|
911
|
-
subject.variables.
|
|
912
|
-
subject.keys.
|
|
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"].
|
|
921
|
-
data.mapping["bar"].
|
|
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.
|
|
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.
|
|
935
|
-
subject.
|
|
936
|
-
subject.
|
|
937
|
-
subject.
|
|
938
|
-
subject.
|
|
939
|
-
subject.
|
|
940
|
-
subject.
|
|
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.
|
|
944
|
-
subject.
|
|
945
|
-
subject.
|
|
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.
|
|
952
|
-
subject.
|
|
953
|
-
subject.
|
|
954
|
-
subject.
|
|
955
|
-
subject.
|
|
956
|
-
subject.
|
|
957
|
-
subject.
|
|
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.
|
|
961
|
-
subject.
|
|
962
|
-
subject.
|
|
963
|
-
subject.
|
|
964
|
-
subject.
|
|
965
|
-
subject.
|
|
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.
|
|
972
|
-
subject.
|
|
973
|
-
subject.
|
|
974
|
-
subject.
|
|
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.
|
|
978
|
-
subject.
|
|
979
|
-
subject.
|
|
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.
|
|
986
|
-
subject.
|
|
987
|
-
subject.
|
|
988
|
-
subject.
|
|
989
|
-
subject.
|
|
990
|
-
subject.
|
|
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.
|
|
994
|
-
subject.
|
|
995
|
-
subject.
|
|
996
|
-
subject.
|
|
997
|
-
subject.
|
|
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
|