addressable 2.2.3 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of addressable might be problematic. Click here for more details.

@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ # coding: utf-8
4
+ # Copyright (C) Bob Aman
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+
18
+
19
+ require "spec_helper"
20
+
21
+ require "addressable/uri"
22
+
23
+ describe Addressable::URI, "when created with a URI known to cause crashes " +
24
+ "in certain browsers" do
25
+ it "should parse correctly" do
26
+ uri = Addressable::URI.parse('%%30%30')
27
+ expect(uri.path).to eq('%%30%30')
28
+ expect(uri.normalize.path).to eq('%2500')
29
+ end
30
+
31
+ it "should parse correctly as a full URI" do
32
+ uri = Addressable::URI.parse('http://www.example.com/%%30%30')
33
+ expect(uri.path).to eq('/%%30%30')
34
+ expect(uri.normalize.path).to eq('/%2500')
35
+ end
36
+ end
37
+
38
+ describe Addressable::URI, "when created with a URI known to cause crashes " +
39
+ "in certain browsers" do
40
+ it "should parse correctly" do
41
+ uri = Addressable::URI.parse('لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗')
42
+ expect(uri.path).to eq('لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗')
43
+ expect(uri.normalize.path).to eq(
44
+ '%D9%84%D9%8F%D8%B5%D9%91%D8%A8%D9%8F%D9%84%D9%8F%D9%84%D8%B5%D9%91' +
45
+ '%D8%A8%D9%8F%D8%B1%D8%B1%D9%8B%20%E0%A5%A3%20%E0%A5%A3h%20%E0%A5' +
46
+ '%A3%20%E0%A5%A3%20%E5%86%97'
47
+ )
48
+ end
49
+
50
+ it "should parse correctly as a full URI" do
51
+ uri = Addressable::URI.parse('http://www.example.com/لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗')
52
+ expect(uri.path).to eq('/لُصّبُلُلصّبُررً ॣ ॣh ॣ ॣ 冗')
53
+ expect(uri.normalize.path).to eq(
54
+ '/%D9%84%D9%8F%D8%B5%D9%91%D8%A8%D9%8F%D9%84%D9%8F%D9%84%D8%B5%D9%91' +
55
+ '%D8%A8%D9%8F%D8%B1%D8%B1%D9%8B%20%E0%A5%A3%20%E0%A5%A3h%20%E0%A5' +
56
+ '%A3%20%E0%A5%A3%20%E5%86%97'
57
+ )
58
+ end
59
+ end
@@ -1,2152 +1,1451 @@
1
- # encoding:utf-8
2
- #--
3
- # Addressable, Copyright (c) 2006-2007 Bob Aman
1
+ # frozen_string_literal: true
2
+
3
+ # coding: utf-8
4
+ # Copyright (C) Bob Aman
4
5
  #
5
- # Permission is hereby granted, free of charge, to any person obtaining
6
- # a copy of this software and associated documentation files (the
7
- # "Software"), to deal in the Software without restriction, including
8
- # without limitation the rights to use, copy, modify, merge, publish,
9
- # distribute, sublicense, and/or sell copies of the Software, and to
10
- # permit persons to whom the Software is furnished to do so, subject to
11
- # the following conditions:
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
12
9
  #
13
- # The above copyright notice and this permission notice shall be
14
- # included in all copies or substantial portions of the Software.
10
+ # http://www.apache.org/licenses/LICENSE-2.0
15
11
  #
16
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
- # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
- # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
- # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
- # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23
- #++
24
-
25
- require "addressable/template"
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
26
17
 
27
- if !"".respond_to?("force_encoding")
28
- class String
29
- def force_encoding(encoding)
30
- @encoding = encoding
31
- end
32
18
 
33
- def encoding
34
- @encoding ||= Encoding::ASCII_8BIT
35
- end
36
- end
19
+ require "spec_helper"
37
20
 
38
- class Encoding
39
- def initialize(name)
40
- @name = name
41
- end
21
+ require "bigdecimal"
22
+ require "addressable/template"
42
23
 
43
- def to_s
44
- return @name
24
+ shared_examples_for 'expands' do |tests|
25
+ tests.each do |template, expansion|
26
+ exp = expansion.is_a?(Array) ? expansion.first : expansion
27
+ it "#{template} to #{exp}" do
28
+ tmpl = Addressable::Template.new(template).expand(subject)
29
+ if expansion.is_a?(Array)
30
+ expect(expansion.any?{|i| i == tmpl.to_str}).to be true
31
+ else
32
+ expect(tmpl.to_str).to eq(expansion)
33
+ end
45
34
  end
46
-
47
- UTF_8 = Encoding.new("UTF-8")
48
- ASCII_8BIT = Encoding.new("US-ASCII")
49
- end
50
- end
51
-
52
- class ExampleProcessor
53
- def self.validate(name, value)
54
- return !!(value =~ /^[\w ]+$/) if name == "query"
55
- return true
56
- end
57
-
58
- def self.transform(name, value)
59
- return value.gsub(/ /, "+") if name == "query"
60
- return value
61
- end
62
-
63
- def self.restore(name, value)
64
- return value.gsub(/\+/, " ") if name == "query"
65
- return value.tr("A-Za-z", "N-ZA-Mn-za-m") if name == "rot13"
66
- return value
67
- end
68
-
69
- def self.match(name)
70
- return ".*?" if name == "first"
71
- return ".*"
72
- end
73
- end
74
-
75
- class SlashlessProcessor
76
- def self.match(name)
77
- return "[^/\\n]*"
78
- end
79
- end
80
-
81
- class NoOpProcessor
82
- def self.transform(name, value)
83
- value
84
- end
85
- end
86
-
87
- describe Addressable::Template do
88
- it "should raise a TypeError for invalid patterns" do
89
- (lambda do
90
- Addressable::Template.new(42)
91
- end).should raise_error(TypeError, "Can't convert Fixnum into String.")
92
35
  end
93
36
  end
94
37
 
95
- describe Addressable::Template, "created with the pattern '/'" do
96
- before do
97
- @template = Addressable::Template.new("/")
38
+ describe "eql?" do
39
+ let(:template) { Addressable::Template.new('https://www.example.com/{foo}') }
40
+ it 'is equal when the pattern matches' do
41
+ other_template = Addressable::Template.new('https://www.example.com/{foo}')
42
+ expect(template).to be_eql(other_template)
43
+ expect(other_template).to be_eql(template)
98
44
  end
99
-
100
- it "should have no variables" do
101
- @template.variables.should be_empty
45
+ it 'is not equal when the pattern differs' do
46
+ other_template = Addressable::Template.new('https://www.example.com/{bar}')
47
+ expect(template).to_not be_eql(other_template)
48
+ expect(other_template).to_not be_eql(template)
102
49
  end
103
-
104
- it "should have the correct mapping when extracting from '/'" do
105
- @template.extract("/").should == {}
50
+ it 'is not equal to non-templates' do
51
+ uri = 'https://www.example.com/foo/bar'
52
+ addressable_template = Addressable::Template.new uri
53
+ addressable_uri = Addressable::URI.parse uri
54
+ expect(addressable_template).to_not be_eql(addressable_uri)
55
+ expect(addressable_uri).to_not be_eql(addressable_template)
106
56
  end
107
57
  end
108
58
 
109
- describe Addressable::URI, "when parsed from '/one/'" do
110
- before do
111
- @uri = Addressable::URI.parse("/one/")
59
+ describe "==" do
60
+ let(:template) { Addressable::Template.new('https://www.example.com/{foo}') }
61
+ it 'is equal when the pattern matches' do
62
+ other_template = Addressable::Template.new('https://www.example.com/{foo}')
63
+ expect(template).to eq other_template
64
+ expect(other_template).to eq template
112
65
  end
113
-
114
- it "should not match the pattern '/two/'" do
115
- Addressable::Template.new("/two/").extract(@uri).should == nil
66
+ it 'is not equal when the pattern differs' do
67
+ other_template = Addressable::Template.new('https://www.example.com/{bar}')
68
+ expect(template).not_to eq other_template
69
+ expect(other_template).not_to eq template
116
70
  end
117
-
118
- it "should have the correct mapping when extracting values " +
119
- "using the pattern '/{number}/'" do
120
- Addressable::Template.new(
121
- "/{number}/"
122
- ).extract(@uri).should == {"number" => "one"}
71
+ it 'is not equal to non-templates' do
72
+ uri = 'https://www.example.com/foo/bar'
73
+ addressable_template = Addressable::Template.new uri
74
+ addressable_uri = Addressable::URI.parse uri
75
+ expect(addressable_template).not_to eq addressable_uri
76
+ expect(addressable_uri).not_to eq addressable_template
123
77
  end
124
78
  end
125
79
 
126
- describe Addressable::Template, "created with the pattern '/{number}/'" do
127
- before do
128
- @template = Addressable::Template.new("/{number}/")
129
- end
130
-
131
- it "should have the variables ['number']" do
132
- @template.variables.should == ["number"]
133
- end
134
-
135
- it "should not match the pattern '/'" do
136
- @template.match("/").should == nil
80
+ describe "Type conversion" do
81
+ subject {
82
+ {
83
+ :var => true,
84
+ :hello => 1234,
85
+ :nothing => nil,
86
+ :sym => :symbolic,
87
+ :decimal => BigDecimal('1')
88
+ }
89
+ }
90
+
91
+ it_behaves_like 'expands', {
92
+ '{var}' => 'true',
93
+ '{hello}' => '1234',
94
+ '{nothing}' => '',
95
+ '{sym}' => 'symbolic',
96
+ '{decimal}' => RUBY_VERSION < '2.4.0' ? '0.1E1' : '0.1e1'
97
+ }
98
+ end
99
+
100
+ describe "Level 1:" do
101
+ subject {
102
+ {:var => "value", :hello => "Hello World!"}
103
+ }
104
+ it_behaves_like 'expands', {
105
+ '{var}' => 'value',
106
+ '{hello}' => 'Hello%20World%21'
107
+ }
108
+ end
109
+
110
+ describe "Level 2" do
111
+ subject {
112
+ {
113
+ :var => "value",
114
+ :hello => "Hello World!",
115
+ :path => "/foo/bar"
116
+ }
117
+ }
118
+ context "Operator +:" do
119
+ it_behaves_like 'expands', {
120
+ '{+var}' => 'value',
121
+ '{+hello}' => 'Hello%20World!',
122
+ '{+path}/here' => '/foo/bar/here',
123
+ 'here?ref={+path}' => 'here?ref=/foo/bar'
124
+ }
137
125
  end
138
-
139
- it "should match the pattern '/two/'" do
140
- @template.match("/two/").mapping.should == {"number" => "two"}
126
+ context "Operator #:" do
127
+ it_behaves_like 'expands', {
128
+ 'X{#var}' => 'X#value',
129
+ 'X{#hello}' => 'X#Hello%20World!'
130
+ }
141
131
  end
142
132
  end
143
133
 
144
- describe Addressable::Template,
145
- "created with the pattern '/{number}/{number}/'" do
146
- before do
147
- @template = Addressable::Template.new("/{number}/{number}/")
134
+ describe "Level 3" do
135
+ subject {
136
+ {
137
+ :var => "value",
138
+ :hello => "Hello World!",
139
+ :empty => "",
140
+ :path => "/foo/bar",
141
+ :x => "1024",
142
+ :y => "768"
143
+ }
144
+ }
145
+ context "Operator nil (multiple vars):" do
146
+ it_behaves_like 'expands', {
147
+ 'map?{x,y}' => 'map?1024,768',
148
+ '{x,hello,y}' => '1024,Hello%20World%21,768'
149
+ }
148
150
  end
149
-
150
- it "should have one variable" do
151
- @template.variables.should == ["number"]
151
+ context "Operator + (multiple vars):" do
152
+ it_behaves_like 'expands', {
153
+ '{+x,hello,y}' => '1024,Hello%20World!,768',
154
+ '{+path,x}/here' => '/foo/bar,1024/here'
155
+ }
152
156
  end
153
-
154
- it "should have the correct mapping when extracting from '/1/1/'" do
155
- @template.extract("/1/1/").should == {"number" => "1"}
157
+ context "Operator # (multiple vars):" do
158
+ it_behaves_like 'expands', {
159
+ '{#x,hello,y}' => '#1024,Hello%20World!,768',
160
+ '{#path,x}/here' => '#/foo/bar,1024/here'
161
+ }
156
162
  end
157
-
158
- it "should not match '/1/2/'" do
159
- @template.match("/1/2/").should == nil
163
+ context "Operator ." do
164
+ it_behaves_like 'expands', {
165
+ 'X{.var}' => 'X.value',
166
+ 'X{.x,y}' => 'X.1024.768'
167
+ }
160
168
  end
161
-
162
- it "should not match '/2/1/'" do
163
- @template.match("/2/1/").should == nil
169
+ context "Operator /" do
170
+ it_behaves_like 'expands', {
171
+ '{/var}' => '/value',
172
+ '{/var,x}/here' => '/value/1024/here'
173
+ }
164
174
  end
165
-
166
- it "should not match '/1/'" do
167
- @template.match("/1/").should == nil
175
+ context "Operator ;" do
176
+ it_behaves_like 'expands', {
177
+ '{;x,y}' => ';x=1024;y=768',
178
+ '{;x,y,empty}' => ';x=1024;y=768;empty'
179
+ }
168
180
  end
169
-
170
- it "should not match '/1/1/1/'" do
171
- @template.match("/1/1/1/").should == nil
181
+ context "Operator ?" do
182
+ it_behaves_like 'expands', {
183
+ '{?x,y}' => '?x=1024&y=768',
184
+ '{?x,y,empty}' => '?x=1024&y=768&empty='
185
+ }
172
186
  end
173
-
174
- it "should not match '/1/2/3/'" do
175
- @template.match("/1/2/3/").should == nil
187
+ context "Operator &" do
188
+ it_behaves_like 'expands', {
189
+ '?fixed=yes{&x}' => '?fixed=yes&x=1024',
190
+ '{&x,y,empty}' => '&x=1024&y=768&empty='
191
+ }
176
192
  end
177
193
  end
178
194
 
179
- describe Addressable::Template,
180
- "created with the pattern '/{number}{-prefix|.|number}'" do
181
- before do
182
- @template = Addressable::Template.new("/{number}{-prefix|.|number}")
183
- end
184
-
185
- it "should have one variable" do
186
- @template.variables.should == ["number"]
195
+ describe "Level 4" do
196
+ subject {
197
+ {
198
+ :var => "value",
199
+ :hello => "Hello World!",
200
+ :path => "/foo/bar",
201
+ :semi => ";",
202
+ :list => %w(red green blue),
203
+ :keys => {"semi" => ';', "dot" => '.', "comma" => ','}
204
+ }
205
+ }
206
+ context "Expansion with value modifiers" do
207
+ it_behaves_like 'expands', {
208
+ '{var:3}' => 'val',
209
+ '{var:30}' => 'value',
210
+ '{list}' => 'red,green,blue',
211
+ '{list*}' => 'red,green,blue',
212
+ '{keys}' => [
213
+ 'semi,%3B,dot,.,comma,%2C',
214
+ 'dot,.,semi,%3B,comma,%2C',
215
+ 'comma,%2C,semi,%3B,dot,.',
216
+ 'semi,%3B,comma,%2C,dot,.',
217
+ 'dot,.,comma,%2C,semi,%3B',
218
+ 'comma,%2C,dot,.,semi,%3B'
219
+ ],
220
+ '{keys*}' => [
221
+ 'semi=%3B,dot=.,comma=%2C',
222
+ 'dot=.,semi=%3B,comma=%2C',
223
+ 'comma=%2C,semi=%3B,dot=.',
224
+ 'semi=%3B,comma=%2C,dot=.',
225
+ 'dot=.,comma=%2C,semi=%3B',
226
+ 'comma=%2C,dot=.,semi=%3B'
227
+ ]
228
+ }
187
229
  end
188
-
189
- it "should have the correct mapping when extracting from '/1.1'" do
190
- @template.extract("/1.1").should == {"number" => "1"}
230
+ context "Operator + with value modifiers" do
231
+ it_behaves_like 'expands', {
232
+ '{+path:6}/here' => '/foo/b/here',
233
+ '{+list}' => 'red,green,blue',
234
+ '{+list*}' => 'red,green,blue',
235
+ '{+keys}' => [
236
+ 'semi,;,dot,.,comma,,',
237
+ 'dot,.,semi,;,comma,,',
238
+ 'comma,,,semi,;,dot,.',
239
+ 'semi,;,comma,,,dot,.',
240
+ 'dot,.,comma,,,semi,;',
241
+ 'comma,,,dot,.,semi,;'
242
+ ],
243
+ '{+keys*}' => [
244
+ 'semi=;,dot=.,comma=,',
245
+ 'dot=.,semi=;,comma=,',
246
+ 'comma=,,semi=;,dot=.',
247
+ 'semi=;,comma=,,dot=.',
248
+ 'dot=.,comma=,,semi=;',
249
+ 'comma=,,dot=.,semi=;'
250
+ ]
251
+ }
191
252
  end
192
-
193
- it "should have the correct mapping when extracting from '/99.99'" do
194
- @template.extract("/99.99").should == {"number" => "99"}
253
+ context "Operator # with value modifiers" do
254
+ it_behaves_like 'expands', {
255
+ '{#path:6}/here' => '#/foo/b/here',
256
+ '{#list}' => '#red,green,blue',
257
+ '{#list*}' => '#red,green,blue',
258
+ '{#keys}' => [
259
+ '#semi,;,dot,.,comma,,',
260
+ '#dot,.,semi,;,comma,,',
261
+ '#comma,,,semi,;,dot,.',
262
+ '#semi,;,comma,,,dot,.',
263
+ '#dot,.,comma,,,semi,;',
264
+ '#comma,,,dot,.,semi,;'
265
+ ],
266
+ '{#keys*}' => [
267
+ '#semi=;,dot=.,comma=,',
268
+ '#dot=.,semi=;,comma=,',
269
+ '#comma=,,semi=;,dot=.',
270
+ '#semi=;,comma=,,dot=.',
271
+ '#dot=.,comma=,,semi=;',
272
+ '#comma=,,dot=.,semi=;'
273
+ ]
274
+ }
195
275
  end
196
-
197
- it "should not match '/1.2'" do
198
- @template.match("/1.2").should == nil
276
+ context "Operator . with value modifiers" do
277
+ it_behaves_like 'expands', {
278
+ 'X{.var:3}' => 'X.val',
279
+ 'X{.list}' => 'X.red,green,blue',
280
+ 'X{.list*}' => 'X.red.green.blue',
281
+ 'X{.keys}' => [
282
+ 'X.semi,%3B,dot,.,comma,%2C',
283
+ 'X.dot,.,semi,%3B,comma,%2C',
284
+ 'X.comma,%2C,semi,%3B,dot,.',
285
+ 'X.semi,%3B,comma,%2C,dot,.',
286
+ 'X.dot,.,comma,%2C,semi,%3B',
287
+ 'X.comma,%2C,dot,.,semi,%3B'
288
+ ],
289
+ 'X{.keys*}' => [
290
+ 'X.semi=%3B.dot=..comma=%2C',
291
+ 'X.dot=..semi=%3B.comma=%2C',
292
+ 'X.comma=%2C.semi=%3B.dot=.',
293
+ 'X.semi=%3B.comma=%2C.dot=.',
294
+ 'X.dot=..comma=%2C.semi=%3B',
295
+ 'X.comma=%2C.dot=..semi=%3B'
296
+ ]
297
+ }
199
298
  end
200
-
201
- it "should not match '/2.1'" do
202
- @template.match("/2.1").should == nil
299
+ context "Operator / with value modifiers" do
300
+ it_behaves_like 'expands', {
301
+ '{/var:1,var}' => '/v/value',
302
+ '{/list}' => '/red,green,blue',
303
+ '{/list*}' => '/red/green/blue',
304
+ '{/list*,path:4}' => '/red/green/blue/%2Ffoo',
305
+ '{/keys}' => [
306
+ '/semi,%3B,dot,.,comma,%2C',
307
+ '/dot,.,semi,%3B,comma,%2C',
308
+ '/comma,%2C,semi,%3B,dot,.',
309
+ '/semi,%3B,comma,%2C,dot,.',
310
+ '/dot,.,comma,%2C,semi,%3B',
311
+ '/comma,%2C,dot,.,semi,%3B'
312
+ ],
313
+ '{/keys*}' => [
314
+ '/semi=%3B/dot=./comma=%2C',
315
+ '/dot=./semi=%3B/comma=%2C',
316
+ '/comma=%2C/semi=%3B/dot=.',
317
+ '/semi=%3B/comma=%2C/dot=.',
318
+ '/dot=./comma=%2C/semi=%3B',
319
+ '/comma=%2C/dot=./semi=%3B'
320
+ ]
321
+ }
203
322
  end
204
-
205
- it "should not match '/1'" do
206
- @template.match("/1").should == nil
323
+ context "Operator ; with value modifiers" do
324
+ it_behaves_like 'expands', {
325
+ '{;hello:5}' => ';hello=Hello',
326
+ '{;list}' => ';list=red,green,blue',
327
+ '{;list*}' => ';list=red;list=green;list=blue',
328
+ '{;keys}' => [
329
+ ';keys=semi,%3B,dot,.,comma,%2C',
330
+ ';keys=dot,.,semi,%3B,comma,%2C',
331
+ ';keys=comma,%2C,semi,%3B,dot,.',
332
+ ';keys=semi,%3B,comma,%2C,dot,.',
333
+ ';keys=dot,.,comma,%2C,semi,%3B',
334
+ ';keys=comma,%2C,dot,.,semi,%3B'
335
+ ],
336
+ '{;keys*}' => [
337
+ ';semi=%3B;dot=.;comma=%2C',
338
+ ';dot=.;semi=%3B;comma=%2C',
339
+ ';comma=%2C;semi=%3B;dot=.',
340
+ ';semi=%3B;comma=%2C;dot=.',
341
+ ';dot=.;comma=%2C;semi=%3B',
342
+ ';comma=%2C;dot=.;semi=%3B'
343
+ ]
344
+ }
207
345
  end
208
-
209
- it "should not match '/1.1.1'" do
210
- @template.match("/1.1.1").should == nil
346
+ context "Operator ? with value modifiers" do
347
+ it_behaves_like 'expands', {
348
+ '{?var:3}' => '?var=val',
349
+ '{?list}' => '?list=red,green,blue',
350
+ '{?list*}' => '?list=red&list=green&list=blue',
351
+ '{?keys}' => [
352
+ '?keys=semi,%3B,dot,.,comma,%2C',
353
+ '?keys=dot,.,semi,%3B,comma,%2C',
354
+ '?keys=comma,%2C,semi,%3B,dot,.',
355
+ '?keys=semi,%3B,comma,%2C,dot,.',
356
+ '?keys=dot,.,comma,%2C,semi,%3B',
357
+ '?keys=comma,%2C,dot,.,semi,%3B'
358
+ ],
359
+ '{?keys*}' => [
360
+ '?semi=%3B&dot=.&comma=%2C',
361
+ '?dot=.&semi=%3B&comma=%2C',
362
+ '?comma=%2C&semi=%3B&dot=.',
363
+ '?semi=%3B&comma=%2C&dot=.',
364
+ '?dot=.&comma=%2C&semi=%3B',
365
+ '?comma=%2C&dot=.&semi=%3B'
366
+ ]
367
+ }
211
368
  end
212
-
213
- it "should not match '/1.23'" do
214
- @template.match("/1.23").should == nil
369
+ context "Operator & with value modifiers" do
370
+ it_behaves_like 'expands', {
371
+ '{&var:3}' => '&var=val',
372
+ '{&list}' => '&list=red,green,blue',
373
+ '{&list*}' => '&list=red&list=green&list=blue',
374
+ '{&keys}' => [
375
+ '&keys=semi,%3B,dot,.,comma,%2C',
376
+ '&keys=dot,.,semi,%3B,comma,%2C',
377
+ '&keys=comma,%2C,semi,%3B,dot,.',
378
+ '&keys=semi,%3B,comma,%2C,dot,.',
379
+ '&keys=dot,.,comma,%2C,semi,%3B',
380
+ '&keys=comma,%2C,dot,.,semi,%3B'
381
+ ],
382
+ '{&keys*}' => [
383
+ '&semi=%3B&dot=.&comma=%2C',
384
+ '&dot=.&semi=%3B&comma=%2C',
385
+ '&comma=%2C&semi=%3B&dot=.',
386
+ '&semi=%3B&comma=%2C&dot=.',
387
+ '&dot=.&comma=%2C&semi=%3B',
388
+ '&comma=%2C&dot=.&semi=%3B'
389
+ ]
390
+ }
215
391
  end
216
392
  end
217
-
218
- describe Addressable::Template,
219
- "created with the pattern '/{number}/{-suffix|/|number}'" do
220
- before do
221
- @template = Addressable::Template.new("/{number}/{-suffix|/|number}")
222
- end
223
-
224
- it "should have one variable" do
225
- @template.variables.should == ["number"]
226
- end
227
-
228
- it "should have the correct mapping when extracting from '/1/1/'" do
229
- @template.extract("/1/1/").should == {"number" => "1"}
230
- end
231
-
232
- it "should have the correct mapping when extracting from '/99/99/'" do
233
- @template.extract("/99/99/").should == {"number" => "99"}
234
- end
235
-
236
- it "should not match '/1/1'" do
237
- @template.match("/1/1").should == nil
238
- end
239
-
240
- it "should not match '/11/'" do
241
- @template.match("/11/").should == nil
242
- end
243
-
244
- it "should not match '/1/2/'" do
245
- @template.match("/1/2/").should == nil
246
- end
247
-
248
- it "should not match '/2/1/'" do
249
- @template.match("/2/1/").should == nil
250
- end
251
-
252
- it "should not match '/1/'" do
253
- @template.match("/1/").should == nil
254
- end
255
-
256
- it "should not match '/1/1/1/'" do
257
- @template.match("/1/1/1/").should == nil
393
+ describe "Modifiers" do
394
+ subject {
395
+ {
396
+ :var => "value",
397
+ :semi => ";",
398
+ :year => %w(1965 2000 2012),
399
+ :dom => %w(example com)
400
+ }
401
+ }
402
+ context "length" do
403
+ it_behaves_like 'expands', {
404
+ '{var:3}' => 'val',
405
+ '{var:30}' => 'value',
406
+ '{var}' => 'value',
407
+ '{semi}' => '%3B',
408
+ '{semi:2}' => '%3B'
409
+ }
258
410
  end
259
-
260
- it "should not match '/1/23/'" do
261
- @template.match("/1/23/").should == nil
411
+ context "explode" do
412
+ it_behaves_like 'expands', {
413
+ 'find{?year*}' => 'find?year=1965&year=2000&year=2012',
414
+ 'www{.dom*}' => 'www.example.com',
415
+ }
262
416
  end
263
417
  end
264
-
265
- describe Addressable::Template,
266
- "created with the pattern '/{number}/?{-join|&|number}'" do
267
- before do
268
- @template = Addressable::Template.new(
269
- "/{number}/?{-join|&|number,letter}"
270
- )
271
- end
272
-
273
- it "should have one variable" do
274
- @template.variables.should == ["number", "letter"]
275
- end
276
-
277
- it "should have the correct mapping when extracting from '/1/?number=1'" do
278
- @template.extract("/1/?number=1").should == {"number" => "1"}
279
- end
280
-
281
- it "should have the correct mapping when extracting " +
282
- "from '/99/?number=99'" do
283
- @template.extract("/99/?number=99").should == {"number" => "99"}
284
- end
285
-
286
- it "should have the correct mapping when extracting " +
287
- "from '/1/?number=1&letter=a'" do
288
- @template.extract("/1/?number=1&letter=a").should == {
289
- "number" => "1", "letter" => "a"
418
+ describe "Expansion" do
419
+ subject {
420
+ {
421
+ :count => ["one", "two", "three"],
422
+ :dom => ["example", "com"],
423
+ :dub => "me/too",
424
+ :hello => "Hello World!",
425
+ :half => "50%",
426
+ :var => "value",
427
+ :who => "fred",
428
+ :base => "http://example.com/home/",
429
+ :path => "/foo/bar",
430
+ :list => ["red", "green", "blue"],
431
+ :keys => {"semi" => ";","dot" => ".","comma" => ","},
432
+ :v => "6",
433
+ :x => "1024",
434
+ :y => "768",
435
+ :empty => "",
436
+ :empty_keys => {},
437
+ :undef => nil
438
+ }
439
+ }
440
+ context "concatenation" do
441
+ it_behaves_like 'expands', {
442
+ '{count}' => 'one,two,three',
443
+ '{count*}' => 'one,two,three',
444
+ '{/count}' => '/one,two,three',
445
+ '{/count*}' => '/one/two/three',
446
+ '{;count}' => ';count=one,two,three',
447
+ '{;count*}' => ';count=one;count=two;count=three',
448
+ '{?count}' => '?count=one,two,three',
449
+ '{?count*}' => '?count=one&count=two&count=three',
450
+ '{&count*}' => '&count=one&count=two&count=three'
290
451
  }
291
452
  end
292
-
293
- it "should not match '/1/?number=1&bogus=foo'" do
294
- @template.match("/1/?number=1&bogus=foo").should == nil
295
- end
296
-
297
- it "should not match '/1/?number=2'" do
298
- @template.match("/1/?number=2").should == nil
299
- end
300
-
301
- it "should not match '/2/?number=1'" do
302
- @template.match("/2/?number=1").should == nil
453
+ context "simple expansion" do
454
+ it_behaves_like 'expands', {
455
+ '{var}' => 'value',
456
+ '{hello}' => 'Hello%20World%21',
457
+ '{half}' => '50%25',
458
+ 'O{empty}X' => 'OX',
459
+ 'O{undef}X' => 'OX',
460
+ '{x,y}' => '1024,768',
461
+ '{x,hello,y}' => '1024,Hello%20World%21,768',
462
+ '?{x,empty}' => '?1024,',
463
+ '?{x,undef}' => '?1024',
464
+ '?{undef,y}' => '?768',
465
+ '{var:3}' => 'val',
466
+ '{var:30}' => 'value',
467
+ '{list}' => 'red,green,blue',
468
+ '{list*}' => 'red,green,blue',
469
+ '{keys}' => [
470
+ 'semi,%3B,dot,.,comma,%2C',
471
+ 'dot,.,semi,%3B,comma,%2C',
472
+ 'comma,%2C,semi,%3B,dot,.',
473
+ 'semi,%3B,comma,%2C,dot,.',
474
+ 'dot,.,comma,%2C,semi,%3B',
475
+ 'comma,%2C,dot,.,semi,%3B'
476
+ ],
477
+ '{keys*}' => [
478
+ 'semi=%3B,dot=.,comma=%2C',
479
+ 'dot=.,semi=%3B,comma=%2C',
480
+ 'comma=%2C,semi=%3B,dot=.',
481
+ 'semi=%3B,comma=%2C,dot=.',
482
+ 'dot=.,comma=%2C,semi=%3B',
483
+ 'comma=%2C,dot=.,semi=%3B'
484
+ ]
485
+ }
303
486
  end
304
-
305
- it "should not match '/1/?'" do
306
- @template.match("/1/?").should == nil
487
+ context "reserved expansion (+)" do
488
+ it_behaves_like 'expands', {
489
+ '{+var}' => 'value',
490
+ '{+hello}' => 'Hello%20World!',
491
+ '{+half}' => '50%25',
492
+ '{base}index' => 'http%3A%2F%2Fexample.com%2Fhome%2Findex',
493
+ '{+base}index' => 'http://example.com/home/index',
494
+ 'O{+empty}X' => 'OX',
495
+ 'O{+undef}X' => 'OX',
496
+ '{+path}/here' => '/foo/bar/here',
497
+ 'here?ref={+path}' => 'here?ref=/foo/bar',
498
+ 'up{+path}{var}/here' => 'up/foo/barvalue/here',
499
+ '{+x,hello,y}' => '1024,Hello%20World!,768',
500
+ '{+path,x}/here' => '/foo/bar,1024/here',
501
+ '{+path:6}/here' => '/foo/b/here',
502
+ '{+list}' => 'red,green,blue',
503
+ '{+list*}' => 'red,green,blue',
504
+ '{+keys}' => [
505
+ 'semi,;,dot,.,comma,,',
506
+ 'dot,.,semi,;,comma,,',
507
+ 'comma,,,semi,;,dot,.',
508
+ 'semi,;,comma,,,dot,.',
509
+ 'dot,.,comma,,,semi,;',
510
+ 'comma,,,dot,.,semi,;'
511
+ ],
512
+ '{+keys*}' => [
513
+ 'semi=;,dot=.,comma=,',
514
+ 'dot=.,semi=;,comma=,',
515
+ 'comma=,,semi=;,dot=.',
516
+ 'semi=;,comma=,,dot=.',
517
+ 'dot=.,comma=,,semi=;',
518
+ 'comma=,,dot=.,semi=;'
519
+ ]
520
+ }
307
521
  end
308
- end
309
-
310
- describe Addressable::Template,
311
- "created with the pattern '/{number}/{-list|/|number}/'" do
312
- before do
313
- @template = Addressable::Template.new("/{number}/{-list|/|number}/")
522
+ context "fragment expansion (#)" do
523
+ it_behaves_like 'expands', {
524
+ '{#var}' => '#value',
525
+ '{#hello}' => '#Hello%20World!',
526
+ '{#half}' => '#50%25',
527
+ 'foo{#empty}' => 'foo#',
528
+ 'foo{#undef}' => 'foo',
529
+ '{#x,hello,y}' => '#1024,Hello%20World!,768',
530
+ '{#path,x}/here' => '#/foo/bar,1024/here',
531
+ '{#path:6}/here' => '#/foo/b/here',
532
+ '{#list}' => '#red,green,blue',
533
+ '{#list*}' => '#red,green,blue',
534
+ '{#keys}' => [
535
+ '#semi,;,dot,.,comma,,',
536
+ '#dot,.,semi,;,comma,,',
537
+ '#comma,,,semi,;,dot,.',
538
+ '#semi,;,comma,,,dot,.',
539
+ '#dot,.,comma,,,semi,;',
540
+ '#comma,,,dot,.,semi,;'
541
+ ],
542
+ '{#keys*}' => [
543
+ '#semi=;,dot=.,comma=,',
544
+ '#dot=.,semi=;,comma=,',
545
+ '#comma=,,semi=;,dot=.',
546
+ '#semi=;,comma=,,dot=.',
547
+ '#dot=.,comma=,,semi=;',
548
+ '#comma=,,dot=.,semi=;'
549
+ ]
550
+ }
314
551
  end
315
-
316
- it "should have one variable" do
317
- @template.variables.should == ["number"]
552
+ context "label expansion (.)" do
553
+ it_behaves_like 'expands', {
554
+ '{.who}' => '.fred',
555
+ '{.who,who}' => '.fred.fred',
556
+ '{.half,who}' => '.50%25.fred',
557
+ 'www{.dom*}' => 'www.example.com',
558
+ 'X{.var}' => 'X.value',
559
+ 'X{.empty}' => 'X.',
560
+ 'X{.undef}' => 'X',
561
+ 'X{.var:3}' => 'X.val',
562
+ 'X{.list}' => 'X.red,green,blue',
563
+ 'X{.list*}' => 'X.red.green.blue',
564
+ 'X{.keys}' => [
565
+ 'X.semi,%3B,dot,.,comma,%2C',
566
+ 'X.dot,.,semi,%3B,comma,%2C',
567
+ 'X.comma,%2C,semi,%3B,dot,.',
568
+ 'X.semi,%3B,comma,%2C,dot,.',
569
+ 'X.dot,.,comma,%2C,semi,%3B',
570
+ 'X.comma,%2C,dot,.,semi,%3B'
571
+ ],
572
+ 'X{.keys*}' => [
573
+ 'X.semi=%3B.dot=..comma=%2C',
574
+ 'X.dot=..semi=%3B.comma=%2C',
575
+ 'X.comma=%2C.semi=%3B.dot=.',
576
+ 'X.semi=%3B.comma=%2C.dot=.',
577
+ 'X.dot=..comma=%2C.semi=%3B',
578
+ 'X.comma=%2C.dot=..semi=%3B'
579
+ ],
580
+ 'X{.empty_keys}' => 'X',
581
+ 'X{.empty_keys*}' => 'X'
582
+ }
318
583
  end
319
-
320
- it "should not match '/1/1/'" do
321
- @template.match("/1/1/").should == nil
584
+ context "path expansion (/)" do
585
+ it_behaves_like 'expands', {
586
+ '{/who}' => '/fred',
587
+ '{/who,who}' => '/fred/fred',
588
+ '{/half,who}' => '/50%25/fred',
589
+ '{/who,dub}' => '/fred/me%2Ftoo',
590
+ '{/var}' => '/value',
591
+ '{/var,empty}' => '/value/',
592
+ '{/var,undef}' => '/value',
593
+ '{/var,x}/here' => '/value/1024/here',
594
+ '{/var:1,var}' => '/v/value',
595
+ '{/list}' => '/red,green,blue',
596
+ '{/list*}' => '/red/green/blue',
597
+ '{/list*,path:4}' => '/red/green/blue/%2Ffoo',
598
+ '{/keys}' => [
599
+ '/semi,%3B,dot,.,comma,%2C',
600
+ '/dot,.,semi,%3B,comma,%2C',
601
+ '/comma,%2C,semi,%3B,dot,.',
602
+ '/semi,%3B,comma,%2C,dot,.',
603
+ '/dot,.,comma,%2C,semi,%3B',
604
+ '/comma,%2C,dot,.,semi,%3B'
605
+ ],
606
+ '{/keys*}' => [
607
+ '/semi=%3B/dot=./comma=%2C',
608
+ '/dot=./semi=%3B/comma=%2C',
609
+ '/comma=%2C/semi=%3B/dot=.',
610
+ '/semi=%3B/comma=%2C/dot=.',
611
+ '/dot=./comma=%2C/semi=%3B',
612
+ '/comma=%2C/dot=./semi=%3B'
613
+ ]
614
+ }
322
615
  end
323
-
324
- it "should not match '/1/2/'" do
325
- @template.match("/1/2/").should == nil
616
+ context "path-style expansion (;)" do
617
+ it_behaves_like 'expands', {
618
+ '{;who}' => ';who=fred',
619
+ '{;half}' => ';half=50%25',
620
+ '{;empty}' => ';empty',
621
+ '{;v,empty,who}' => ';v=6;empty;who=fred',
622
+ '{;v,bar,who}' => ';v=6;who=fred',
623
+ '{;x,y}' => ';x=1024;y=768',
624
+ '{;x,y,empty}' => ';x=1024;y=768;empty',
625
+ '{;x,y,undef}' => ';x=1024;y=768',
626
+ '{;hello:5}' => ';hello=Hello',
627
+ '{;list}' => ';list=red,green,blue',
628
+ '{;list*}' => ';list=red;list=green;list=blue',
629
+ '{;keys}' => [
630
+ ';keys=semi,%3B,dot,.,comma,%2C',
631
+ ';keys=dot,.,semi,%3B,comma,%2C',
632
+ ';keys=comma,%2C,semi,%3B,dot,.',
633
+ ';keys=semi,%3B,comma,%2C,dot,.',
634
+ ';keys=dot,.,comma,%2C,semi,%3B',
635
+ ';keys=comma,%2C,dot,.,semi,%3B'
636
+ ],
637
+ '{;keys*}' => [
638
+ ';semi=%3B;dot=.;comma=%2C',
639
+ ';dot=.;semi=%3B;comma=%2C',
640
+ ';comma=%2C;semi=%3B;dot=.',
641
+ ';semi=%3B;comma=%2C;dot=.',
642
+ ';dot=.;comma=%2C;semi=%3B',
643
+ ';comma=%2C;dot=.;semi=%3B'
644
+ ]
645
+ }
326
646
  end
327
-
328
- it "should not match '/2/1/'" do
329
- @template.match("/2/1/").should == nil
647
+ context "form query expansion (?)" do
648
+ it_behaves_like 'expands', {
649
+ '{?who}' => '?who=fred',
650
+ '{?half}' => '?half=50%25',
651
+ '{?x,y}' => '?x=1024&y=768',
652
+ '{?x,y,empty}' => '?x=1024&y=768&empty=',
653
+ '{?x,y,undef}' => '?x=1024&y=768',
654
+ '{?var:3}' => '?var=val',
655
+ '{?list}' => '?list=red,green,blue',
656
+ '{?list*}' => '?list=red&list=green&list=blue',
657
+ '{?keys}' => [
658
+ '?keys=semi,%3B,dot,.,comma,%2C',
659
+ '?keys=dot,.,semi,%3B,comma,%2C',
660
+ '?keys=comma,%2C,semi,%3B,dot,.',
661
+ '?keys=semi,%3B,comma,%2C,dot,.',
662
+ '?keys=dot,.,comma,%2C,semi,%3B',
663
+ '?keys=comma,%2C,dot,.,semi,%3B'
664
+ ],
665
+ '{?keys*}' => [
666
+ '?semi=%3B&dot=.&comma=%2C',
667
+ '?dot=.&semi=%3B&comma=%2C',
668
+ '?comma=%2C&semi=%3B&dot=.',
669
+ '?semi=%3B&comma=%2C&dot=.',
670
+ '?dot=.&comma=%2C&semi=%3B',
671
+ '?comma=%2C&dot=.&semi=%3B'
672
+ ]
673
+ }
330
674
  end
331
-
332
- it "should not match '/1/1/1/'" do
333
- @template.match("/1/1/1/").should == nil
675
+ context "form query expansion (&)" do
676
+ it_behaves_like 'expands', {
677
+ '{&who}' => '&who=fred',
678
+ '{&half}' => '&half=50%25',
679
+ '?fixed=yes{&x}' => '?fixed=yes&x=1024',
680
+ '{&x,y,empty}' => '&x=1024&y=768&empty=',
681
+ '{&x,y,undef}' => '&x=1024&y=768',
682
+ '{&var:3}' => '&var=val',
683
+ '{&list}' => '&list=red,green,blue',
684
+ '{&list*}' => '&list=red&list=green&list=blue',
685
+ '{&keys}' => [
686
+ '&keys=semi,%3B,dot,.,comma,%2C',
687
+ '&keys=dot,.,semi,%3B,comma,%2C',
688
+ '&keys=comma,%2C,semi,%3B,dot,.',
689
+ '&keys=semi,%3B,comma,%2C,dot,.',
690
+ '&keys=dot,.,comma,%2C,semi,%3B',
691
+ '&keys=comma,%2C,dot,.,semi,%3B'
692
+ ],
693
+ '{&keys*}' => [
694
+ '&semi=%3B&dot=.&comma=%2C',
695
+ '&dot=.&semi=%3B&comma=%2C',
696
+ '&comma=%2C&semi=%3B&dot=.',
697
+ '&semi=%3B&comma=%2C&dot=.',
698
+ '&dot=.&comma=%2C&semi=%3B',
699
+ '&comma=%2C&dot=.&semi=%3B'
700
+ ]
701
+ }
334
702
  end
703
+ context "non-string key in match data" do
704
+ subject {Addressable::Template.new("http://example.com/{one}")}
335
705
 
336
- it "should not match '/1/1/1/1/'" do
337
- @template.match("/1/1/1/1/").should == nil
706
+ it "raises TypeError" do
707
+ expect { subject.expand(Object.new => "1") }.to raise_error TypeError
708
+ end
338
709
  end
339
710
  end
340
711
 
341
- describe Addressable::Template, "created with the pattern " +
342
- "'http://www.example.com/?{-join|&|query,number}'" do
343
- before do
344
- @template = Addressable::Template.new(
345
- "http://www.example.com/?{-join|&|query,number}"
346
- )
347
- end
348
-
349
- it "when inspected, should have the correct class name" do
350
- @template.inspect.should include("Addressable::Template")
351
- end
352
-
353
- it "when inspected, should have the correct object id" do
354
- @template.inspect.should include("%#0x" % @template.object_id)
355
- end
356
-
357
- it "should have the variables ['query', 'number']" do
358
- @template.variables.should == ["query", "number"]
359
- end
360
-
361
- it "should not match the pattern 'http://www.example.com/'" do
362
- @template.match("http://www.example.com/").should == nil
712
+ class ExampleTwoProcessor
713
+ def self.restore(name, value)
714
+ return value.gsub(/-/, " ") if name == "query"
715
+ return value
363
716
  end
364
717
 
365
- it "should match the pattern 'http://www.example.com/?'" do
366
- @template.match("http://www.example.com/?").mapping.should == {}
718
+ def self.match(name)
719
+ return ".*?" if name == "first"
720
+ return ".*"
367
721
  end
368
-
369
- it "should match the pattern " +
370
- "'http://www.example.com/?query=mycelium'" do
371
- match = @template.match(
372
- "http://www.example.com/?query=mycelium"
373
- )
374
- match.variables.should == ["query", "number"]
375
- match.values.should == ["mycelium", nil]
376
- match.mapping.should == {"query" => "mycelium"}
377
- match.inspect.should =~ /MatchData/
722
+ def self.validate(name, value)
723
+ return !!(value =~ /^[\w ]+$/) if name == "query"
724
+ return true
378
725
  end
379
726
 
380
- it "should match the pattern " +
381
- "'http://www.example.com/?query=mycelium&number=100'" do
382
- @template.match(
383
- "http://www.example.com/?query=mycelium&number=100"
384
- ).mapping.should == {"query" => "mycelium", "number" => "100"}
727
+ def self.transform(name, value)
728
+ return value.gsub(/ /, "+") if name == "query"
729
+ return value
385
730
  end
386
731
  end
387
732
 
388
- describe Addressable::URI, "when parsed from '/one/two/'" do
389
- before do
390
- @uri = Addressable::URI.parse("/one/two/")
391
- end
392
-
393
- it "should not match the pattern '/{number}/' " +
394
- "with the SlashlessProcessor" do
395
- Addressable::Template.new(
396
- "/{number}/"
397
- ).extract(@uri, SlashlessProcessor).should == nil
398
- end
399
-
400
- it "should have the correct mapping when extracting values " +
401
- "using the pattern '/{number}/' without a processor" do
402
- Addressable::Template.new("/{number}/").extract(@uri).should == {
403
- "number" => "one/two"
404
- }
405
- end
406
-
407
- it "should have the correct mapping when extracting values " +
408
- "using the pattern '/{first}/{second}/' with the SlashlessProcessor" do
409
- Addressable::Template.new(
410
- "/{first}/{second}/"
411
- ).extract(@uri, SlashlessProcessor).should == {
412
- "first" => "one",
413
- "second" => "two"
414
- }
733
+ class DumbProcessor
734
+ def self.match(name)
735
+ return ".*?" if name == "first"
415
736
  end
416
737
  end
417
738
 
418
- describe Addressable::URI, "when parsed from " +
419
- "'http://example.com/search/an+example+search+query/'" do
420
- before do
421
- @uri = Addressable::URI.parse(
422
- "http://example.com/search/an+example+search+query/")
423
- end
424
-
425
- it "should have the correct mapping when extracting values using " +
426
- "the pattern 'http://example.com/search/{query}/' with the " +
427
- "ExampleProcessor" do
428
- Addressable::Template.new(
429
- "http://example.com/search/{query}/"
430
- ).extract(@uri, ExampleProcessor).should == {
431
- "query" => "an example search query"
432
- }
433
- end
434
-
435
- it "should have the correct mapping when extracting values " +
436
- "using the pattern " +
437
- "'http://example.com/search/{-list|+|query}/'" do
438
- Addressable::Template.new(
439
- "http://example.com/search/{-list|+|query}/"
440
- ).extract(@uri).should == {
441
- "query" => ["an", "example", "search", "query"]
442
- }
443
- end
444
-
445
- it "should return nil when extracting values using " +
446
- "a non-matching pattern" do
447
- Addressable::Template.new(
448
- "http://bogus.com/{thingy}/"
449
- ).extract(@uri).should == nil
739
+ describe Addressable::Template do
740
+ describe 'initialize' do
741
+ context 'with a non-string' do
742
+ it 'raises a TypeError' do
743
+ expect { Addressable::Template.new(nil) }.to raise_error(TypeError)
744
+ end
745
+ end
450
746
  end
451
- end
452
747
 
453
- describe Addressable::URI, "when parsed from " +
454
- "'http://example.com/a/b/c/'" do
455
- before do
456
- @uri = Addressable::URI.parse(
457
- "http://example.com/a/b/c/")
748
+ describe 'freeze' do
749
+ subject { Addressable::Template.new("http://example.com/{first}/{+second}/") }
750
+ it 'freezes the template' do
751
+ expect(subject.freeze).to be_frozen
752
+ end
458
753
  end
459
754
 
460
- it "should have the correct mapping when extracting values " +
461
- "using the pattern " +
462
- "'http://example.com/{first}/{second}/' with the ExampleProcessor" do
463
- Addressable::Template.new(
464
- "http://example.com/{first}/{second}/"
465
- ).extract(@uri, ExampleProcessor).should == {
466
- "first" => "a",
467
- "second" => "b/c"
755
+ describe "Matching" do
756
+ let(:uri){
757
+ Addressable::URI.parse(
758
+ "http://example.com/search/an-example-search-query/"
759
+ )
468
760
  }
469
- end
470
-
471
- it "should have the correct mapping when extracting values " +
472
- "using the pattern " +
473
- "'http://example.com/{first}/{-list|/|second}/'" do
474
- Addressable::Template.new(
475
- "http://example.com/{first}/{-list|/|second}/"
476
- ).extract(@uri).should == {
477
- "first" => "a",
478
- "second" => ["b", "c"]
761
+ let(:uri2){
762
+ Addressable::URI.parse("http://example.com/a/b/c/")
479
763
  }
480
- end
481
-
482
- it "should have the correct mapping when extracting values " +
483
- "using the pattern " +
484
- "'http://example.com/{first}/{-list|/|rot13}/' " +
485
- "with the ExampleProcessor" do
486
- Addressable::Template.new(
487
- "http://example.com/{first}/{-list|/|rot13}/"
488
- ).extract(@uri, ExampleProcessor).should == {
489
- "first" => "a",
490
- "rot13" => ["o", "p"]
764
+ let(:uri3){
765
+ Addressable::URI.parse("http://example.com/;a=1;b=2;c=3;first=foo")
491
766
  }
492
- end
493
-
494
- it "should have the correct mapping when extracting values " +
495
- "using the pattern " +
496
- "'http://example.com/{-list|/|rot13}/' " +
497
- "with the ExampleProcessor" do
498
- Addressable::Template.new(
499
- "http://example.com/{-list|/|rot13}/"
500
- ).extract(@uri, ExampleProcessor).should == {
501
- "rot13" => ["n", "o", "p"]
767
+ let(:uri4){
768
+ Addressable::URI.parse("http://example.com/?a=1&b=2&c=3&first=foo")
502
769
  }
503
- end
504
-
505
- it "should not map to anything when extracting values " +
506
- "using the pattern " +
507
- "'http://example.com/{-list|/|rot13}/'" do
508
- Addressable::Template.new(
509
- "http://example.com/{-join|/|a,b,c}/"
510
- ).extract(@uri).should == nil
511
- end
512
- end
513
-
514
- describe Addressable::URI, "when parsed from " +
515
- "'http://example.com/?a=one&b=two&c=three'" do
516
- before do
517
- @uri = Addressable::URI.parse("http://example.com/?a=one&b=two&c=three")
518
- end
519
-
520
- it "should have the correct mapping when extracting values " +
521
- "using the pattern " +
522
- "'http://example.com/?{-join|&|a,b,c}'" do
523
- Addressable::Template.new(
524
- "http://example.com/?{-join|&|a,b,c}"
525
- ).extract(@uri).should == {
526
- "a" => "one",
527
- "b" => "two",
528
- "c" => "three"
770
+ let(:uri5){
771
+ "http://example.com/foo"
529
772
  }
530
- end
531
- end
773
+ context "first uri with ExampleTwoProcessor" do
774
+ subject {
775
+ Addressable::Template.new(
776
+ "http://example.com/search/{query}/"
777
+ ).match(uri, ExampleTwoProcessor)
778
+ }
779
+ its(:variables){ should == ["query"] }
780
+ its(:captures){ should == ["an example search query"] }
781
+ end
532
782
 
533
- describe Addressable::URI, "when parsed from " +
534
- "'http://example.com/?rot13=frperg'" do
535
- before do
536
- @uri = Addressable::URI.parse("http://example.com/?rot13=frperg")
537
- end
783
+ context "second uri with ExampleTwoProcessor" do
784
+ subject {
785
+ Addressable::Template.new(
786
+ "http://example.com/{first}/{+second}/"
787
+ ).match(uri2, ExampleTwoProcessor)
788
+ }
789
+ its(:variables){ should == ["first", "second"] }
790
+ its(:captures){ should == ["a", "b/c"] }
791
+ end
538
792
 
539
- it "should have the correct mapping when extracting values " +
540
- "using the pattern " +
541
- "'http://example.com/?{-join|&|rot13}' with the ExampleProcessor" do
542
- Addressable::Template.new(
543
- "http://example.com/?{-join|&|rot13}"
544
- ).extract(@uri, ExampleProcessor).should == {
545
- "rot13" => "secret"
546
- }
547
- end
548
- end
793
+ context "second uri with DumbProcessor" do
794
+ subject {
795
+ Addressable::Template.new(
796
+ "http://example.com/{first}/{+second}/"
797
+ ).match(uri2, DumbProcessor)
798
+ }
799
+ its(:variables){ should == ["first", "second"] }
800
+ its(:captures){ should == ["a", "b/c"] }
801
+ end
549
802
 
550
- describe Addressable::URI, "when parsed from " +
551
- "'http://example.org///something///'" do
552
- before do
553
- @uri = Addressable::URI.parse("http://example.org///something///")
803
+ context "second uri" do
804
+ subject {
805
+ Addressable::Template.new(
806
+ "http://example.com/{first}{/second*}/"
807
+ ).match(uri2)
808
+ }
809
+ its(:variables){ should == ["first", "second"] }
810
+ its(:captures){ should == ["a", ["b","c"]] }
811
+ end
812
+ context "third uri" do
813
+ subject {
814
+ Addressable::Template.new(
815
+ "http://example.com/{;hash*,first}"
816
+ ).match(uri3)
817
+ }
818
+ its(:variables){ should == ["hash", "first"] }
819
+ its(:captures){ should == [
820
+ {"a" => "1", "b" => "2", "c" => "3", "first" => "foo"}, nil] }
821
+ end
822
+ # Note that this expansion is impossible to revert deterministically - the
823
+ # * operator means first could have been a key of hash or a separate key.
824
+ # Semantically, a separate key is more likely, but both are possible.
825
+ context "fourth uri" do
826
+ subject {
827
+ Addressable::Template.new(
828
+ "http://example.com/{?hash*,first}"
829
+ ).match(uri4)
830
+ }
831
+ its(:variables){ should == ["hash", "first"] }
832
+ its(:captures){ should == [
833
+ {"a" => "1", "b" => "2", "c" => "3", "first"=> "foo"}, nil] }
834
+ end
835
+ context "fifth uri" do
836
+ subject {
837
+ Addressable::Template.new(
838
+ "http://example.com/{path}{?hash*,first}"
839
+ ).match(uri5)
840
+ }
841
+ its(:variables){ should == ["path", "hash", "first"] }
842
+ its(:captures){ should == ["foo", nil, nil] }
843
+ end
554
844
  end
555
845
 
556
- it "should have the correct mapping when extracting values " +
557
- "using the pattern 'http://example.org{-prefix|/|parts}/'" do
558
- Addressable::Template.new(
559
- "http://example.org{-prefix|/|parts}/"
560
- ).extract(@uri).should == {
561
- "parts" => ["", "", "something", "", ""]
562
- }
846
+ describe 'match' do
847
+ subject { Addressable::Template.new('http://example.com/first/second/') }
848
+ context 'when the URI is the same as the template' do
849
+ it 'returns the match data itself with an empty mapping' do
850
+ uri = Addressable::URI.parse('http://example.com/first/second/')
851
+ match_data = subject.match(uri)
852
+ expect(match_data).to be_an Addressable::Template::MatchData
853
+ expect(match_data.uri).to eq(uri)
854
+ expect(match_data.template).to eq(subject)
855
+ expect(match_data.mapping).to be_empty
856
+ expect(match_data.inspect).to be_an String
857
+ end
858
+ end
563
859
  end
564
860
 
565
- it "should have the correct mapping when extracting values " +
566
- "using the pattern 'http://example.org/{-suffix|/|parts}'" do
567
- Addressable::Template.new(
568
- "http://example.org/{-suffix|/|parts}"
569
- ).extract(@uri).should == {
570
- "parts" => ["", "", "something", "", ""]
861
+ describe "extract" do
862
+ let(:template) {
863
+ Addressable::Template.new(
864
+ "http://{host}{/segments*}/{?one,two,bogus}{#fragment}"
865
+ )
571
866
  }
572
- end
867
+ let(:uri){ "http://example.com/a/b/c/?one=1&two=2#foo" }
868
+ let(:uri2){ "http://example.com/a/b/c/#foo" }
869
+ it "should be able to extract with queries" do
870
+ expect(template.extract(uri)).to eq({
871
+ "host" => "example.com",
872
+ "segments" => %w(a b c),
873
+ "one" => "1",
874
+ "bogus" => nil,
875
+ "two" => "2",
876
+ "fragment" => "foo"
877
+ })
878
+ end
879
+ it "should be able to extract without queries" do
880
+ expect(template.extract(uri2)).to eq({
881
+ "host" => "example.com",
882
+ "segments" => %w(a b c),
883
+ "one" => nil,
884
+ "bogus" => nil,
885
+ "two" => nil,
886
+ "fragment" => "foo"
887
+ })
888
+ end
573
889
 
574
- it "should have the correct mapping when extracting values " +
575
- "using the pattern 'http://example.org/{-list|/|parts}'" do
576
- Addressable::Template.new(
577
- "http://example.org/{-list|/|parts}"
578
- ).extract(@uri).should == {
579
- "parts" => ["", "", "something", "", ""]
580
- }
890
+ context "issue #137" do
891
+ subject { Addressable::Template.new('/path{?page,per_page}') }
892
+
893
+ it "can match empty" do
894
+ data = subject.extract("/path")
895
+ expect(data["page"]).to eq(nil)
896
+ expect(data["per_page"]).to eq(nil)
897
+ expect(data.keys.sort).to eq(['page', 'per_page'])
898
+ end
899
+
900
+ it "can match first var" do
901
+ data = subject.extract("/path?page=1")
902
+ expect(data["page"]).to eq("1")
903
+ expect(data["per_page"]).to eq(nil)
904
+ expect(data.keys.sort).to eq(['page', 'per_page'])
905
+ end
906
+
907
+ it "can match second var" do
908
+ data = subject.extract("/path?per_page=1")
909
+ expect(data["page"]).to eq(nil)
910
+ expect(data["per_page"]).to eq("1")
911
+ expect(data.keys.sort).to eq(['page', 'per_page'])
912
+ end
913
+
914
+ it "can match both vars" do
915
+ data = subject.extract("/path?page=2&per_page=1")
916
+ expect(data["page"]).to eq("2")
917
+ expect(data["per_page"]).to eq("1")
918
+ expect(data.keys.sort).to eq(['page', 'per_page'])
919
+ end
920
+ end
581
921
  end
582
- end
583
922
 
584
- describe Addressable::URI, "when parsed from " +
585
- "'http://example.com/one/spacer/two/'" do
586
- before do
587
- @uri = Addressable::URI.parse("http://example.com/one/spacer/two/")
923
+ describe "Partial expand with symbols" do
924
+ context "partial_expand with two simple values" do
925
+ subject {
926
+ Addressable::Template.new("http://example.com/{one}/{two}/")
927
+ }
928
+ it "builds a new pattern" do
929
+ expect(subject.partial_expand(:one => "1").pattern).to eq(
930
+ "http://example.com/1/{two}/"
931
+ )
932
+ end
933
+ end
934
+ context "partial_expand query with missing param in middle" do
935
+ subject {
936
+ Addressable::Template.new("http://example.com/{?one,two,three}/")
937
+ }
938
+ it "builds a new pattern" do
939
+ expect(subject.partial_expand(:one => "1", :three => "3").pattern).to eq(
940
+ "http://example.com/?one=1{&two}&three=3/"
941
+ )
942
+ end
943
+ end
944
+ context "partial_expand form style query with missing param at beginning" do
945
+ subject {
946
+ Addressable::Template.new("http://example.com/{?one,two}/")
947
+ }
948
+ it "builds a new pattern" do
949
+ expect(subject.partial_expand(:two => "2").pattern).to eq(
950
+ "http://example.com/?two=2{&one}/"
951
+ )
952
+ end
953
+ end
954
+ context "issue #307 - partial_expand form query with nil params" do
955
+ subject do
956
+ Addressable::Template.new("http://example.com/{?one,two,three}/")
957
+ end
958
+ it "builds a new pattern with two=nil" do
959
+ expect(subject.partial_expand(two: nil).pattern).to eq(
960
+ "http://example.com/{?one}{&three}/"
961
+ )
962
+ end
963
+ it "builds a new pattern with one=nil and two=nil" do
964
+ expect(subject.partial_expand(one: nil, two: nil).pattern).to eq(
965
+ "http://example.com/{?three}/"
966
+ )
967
+ end
968
+ it "builds a new pattern with one=1 and two=nil" do
969
+ expect(subject.partial_expand(one: 1, two: nil).pattern).to eq(
970
+ "http://example.com/?one=1{&three}/"
971
+ )
972
+ end
973
+ it "builds a new pattern with one=nil and two=2" do
974
+ expect(subject.partial_expand(one: nil, two: 2).pattern).to eq(
975
+ "http://example.com/?two=2{&three}/"
976
+ )
977
+ end
978
+ it "builds a new pattern with one=nil" do
979
+ expect(subject.partial_expand(one: nil).pattern).to eq(
980
+ "http://example.com/{?two}{&three}/"
981
+ )
982
+ end
983
+ end
984
+ context "partial_expand with query string" do
985
+ subject {
986
+ Addressable::Template.new("http://example.com/{?two,one}/")
987
+ }
988
+ it "builds a new pattern" do
989
+ expect(subject.partial_expand(:one => "1").pattern).to eq(
990
+ "http://example.com/?one=1{&two}/"
991
+ )
992
+ end
993
+ end
994
+ context "partial_expand with path operator" do
995
+ subject {
996
+ Addressable::Template.new("http://example.com{/one,two}/")
997
+ }
998
+ it "builds a new pattern" do
999
+ expect(subject.partial_expand(:one => "1").pattern).to eq(
1000
+ "http://example.com/1{/two}/"
1001
+ )
1002
+ end
1003
+ end
1004
+ context "partial expand with unicode values" do
1005
+ subject do
1006
+ Addressable::Template.new("http://example.com/{resource}/{query}/")
1007
+ end
1008
+ it "normalizes unicode by default" do
1009
+ template = subject.partial_expand("query" => "Cafe\u0301")
1010
+ expect(template.pattern).to eq(
1011
+ "http://example.com/{resource}/Caf%C3%A9/"
1012
+ )
1013
+ end
1014
+
1015
+ it "does not normalize unicode when byte semantics requested" do
1016
+ template = subject.partial_expand({"query" => "Cafe\u0301"}, nil, false)
1017
+ expect(template.pattern).to eq(
1018
+ "http://example.com/{resource}/Cafe%CC%81/"
1019
+ )
1020
+ end
1021
+ end
588
1022
  end
589
-
590
- it "should have the correct mapping when extracting values " +
591
- "using the pattern " +
592
- "'http://example.com/{first}/spacer/{second}/'" do
593
- Addressable::Template.new(
594
- "http://example.com/{first}/spacer/{second}/"
595
- ).extract(@uri).should == {
596
- "first" => "one",
597
- "second" => "two"
598
- }
1023
+ describe "Partial expand with strings" do
1024
+ context "partial_expand with two simple values" do
1025
+ subject {
1026
+ Addressable::Template.new("http://example.com/{one}/{two}/")
1027
+ }
1028
+ it "builds a new pattern" do
1029
+ expect(subject.partial_expand("one" => "1").pattern).to eq(
1030
+ "http://example.com/1/{two}/"
1031
+ )
1032
+ end
1033
+ end
1034
+ context "partial_expand query with missing param in middle" do
1035
+ subject {
1036
+ Addressable::Template.new("http://example.com/{?one,two,three}/")
1037
+ }
1038
+ it "builds a new pattern" do
1039
+ expect(subject.partial_expand("one" => "1", "three" => "3").pattern).to eq(
1040
+ "http://example.com/?one=1{&two}&three=3/"
1041
+ )
1042
+ end
1043
+ end
1044
+ context "partial_expand with query string" do
1045
+ subject {
1046
+ Addressable::Template.new("http://example.com/{?two,one}/")
1047
+ }
1048
+ it "builds a new pattern" do
1049
+ expect(subject.partial_expand("one" => "1").pattern).to eq(
1050
+ "http://example.com/?one=1{&two}/"
1051
+ )
1052
+ end
1053
+ end
1054
+ context "partial_expand with path operator" do
1055
+ subject {
1056
+ Addressable::Template.new("http://example.com{/one,two}/")
1057
+ }
1058
+ it "builds a new pattern" do
1059
+ expect(subject.partial_expand("one" => "1").pattern).to eq(
1060
+ "http://example.com/1{/two}/"
1061
+ )
1062
+ end
1063
+ end
599
1064
  end
600
-
601
- it "should have the correct mapping when extracting values " +
602
- "using the pattern " +
603
- "'http://example.com{-prefix|/|stuff}/'" do
604
- Addressable::Template.new(
605
- "http://example.com{-prefix|/|stuff}/"
606
- ).extract(@uri).should == {
607
- "stuff" => ["one", "spacer", "two"]
608
- }
1065
+ describe "Expand" do
1066
+ context "expand with unicode values" do
1067
+ subject do
1068
+ Addressable::Template.new("http://example.com/search/{query}/")
1069
+ end
1070
+ it "normalizes unicode by default" do
1071
+ uri = subject.expand("query" => "Cafe\u0301").to_str
1072
+ expect(uri).to eq("http://example.com/search/Caf%C3%A9/")
1073
+ end
1074
+
1075
+ it "does not normalize unicode when byte semantics requested" do
1076
+ uri = subject.expand({ "query" => "Cafe\u0301" }, nil, false).to_str
1077
+ expect(uri).to eq("http://example.com/search/Cafe%CC%81/")
1078
+ end
1079
+ end
1080
+ context "expand with a processor" do
1081
+ subject {
1082
+ Addressable::Template.new("http://example.com/search/{query}/")
1083
+ }
1084
+ it "processes spaces" do
1085
+ expect(subject.expand({"query" => "an example search query"},
1086
+ ExampleTwoProcessor).to_str).to eq(
1087
+ "http://example.com/search/an+example+search+query/"
1088
+ )
1089
+ end
1090
+ it "validates" do
1091
+ expect{
1092
+ subject.expand({"query" => "Bogus!"},
1093
+ ExampleTwoProcessor).to_str
1094
+ }.to raise_error(Addressable::Template::InvalidTemplateValueError)
1095
+ end
1096
+ end
1097
+ context "partial_expand query with missing param in middle" do
1098
+ subject {
1099
+ Addressable::Template.new("http://example.com/{?one,two,three}/")
1100
+ }
1101
+ it "builds a new pattern" do
1102
+ expect(subject.partial_expand("one" => "1", "three" => "3").pattern).to eq(
1103
+ "http://example.com/?one=1{&two}&three=3/"
1104
+ )
1105
+ end
1106
+ end
1107
+ context "partial_expand with query string" do
1108
+ subject {
1109
+ Addressable::Template.new("http://example.com/{?two,one}/")
1110
+ }
1111
+ it "builds a new pattern" do
1112
+ expect(subject.partial_expand("one" => "1").pattern).to eq(
1113
+ "http://example.com/?one=1{&two}/"
1114
+ )
1115
+ end
1116
+ end
1117
+ context "partial_expand with path operator" do
1118
+ subject {
1119
+ Addressable::Template.new("http://example.com{/one,two}/")
1120
+ }
1121
+ it "builds a new pattern" do
1122
+ expect(subject.partial_expand("one" => "1").pattern).to eq(
1123
+ "http://example.com/1{/two}/"
1124
+ )
1125
+ end
1126
+ end
609
1127
  end
1128
+ context "Matching with operators" do
1129
+ describe "Level 1:" do
1130
+ subject { Addressable::Template.new("foo{foo}/{bar}baz") }
1131
+ it "can match" do
1132
+ data = subject.match("foofoo/bananabaz")
1133
+ expect(data.mapping["foo"]).to eq("foo")
1134
+ expect(data.mapping["bar"]).to eq("banana")
1135
+ end
1136
+ it "can fail" do
1137
+ expect(subject.match("bar/foo")).to be_nil
1138
+ expect(subject.match("foobaz")).to be_nil
1139
+ end
1140
+ it "can match empty" do
1141
+ data = subject.match("foo/baz")
1142
+ expect(data.mapping["foo"]).to eq(nil)
1143
+ expect(data.mapping["bar"]).to eq(nil)
1144
+ end
1145
+ it "lists vars" do
1146
+ expect(subject.variables).to eq(["foo", "bar"])
1147
+ end
1148
+ end
610
1149
 
611
- it "should have the correct mapping when extracting values " +
612
- "using the pattern " +
613
- "'http://example.com/o{-prefix|/|stuff}/'" do
614
- Addressable::Template.new(
615
- "http://example.com/o{-prefix|/|stuff}/"
616
- ).extract(@uri).should == nil
617
- end
1150
+ describe "Level 2:" do
1151
+ subject { Addressable::Template.new("foo{+foo}{#bar}baz") }
1152
+ it "can match" do
1153
+ data = subject.match("foo/test/banana#bazbaz")
1154
+ expect(data.mapping["foo"]).to eq("/test/banana")
1155
+ expect(data.mapping["bar"]).to eq("baz")
1156
+ end
1157
+ it "can match empty level 2 #" do
1158
+ data = subject.match("foo/test/bananabaz")
1159
+ expect(data.mapping["foo"]).to eq("/test/banana")
1160
+ expect(data.mapping["bar"]).to eq(nil)
1161
+ data = subject.match("foo/test/banana#baz")
1162
+ expect(data.mapping["foo"]).to eq("/test/banana")
1163
+ expect(data.mapping["bar"]).to eq("")
1164
+ end
1165
+ it "can match empty level 2 +" do
1166
+ data = subject.match("foobaz")
1167
+ expect(data.mapping["foo"]).to eq(nil)
1168
+ expect(data.mapping["bar"]).to eq(nil)
1169
+ data = subject.match("foo#barbaz")
1170
+ expect(data.mapping["foo"]).to eq(nil)
1171
+ expect(data.mapping["bar"]).to eq("bar")
1172
+ end
1173
+ it "lists vars" do
1174
+ expect(subject.variables).to eq(["foo", "bar"])
1175
+ end
1176
+ end
618
1177
 
619
- it "should have the correct mapping when extracting values " +
620
- "using the pattern " +
621
- "'http://example.com/{first}/spacer{-prefix|/|stuff}/'" do
622
- Addressable::Template.new(
623
- "http://example.com/{first}/spacer{-prefix|/|stuff}/"
624
- ).extract(@uri).should == {
625
- "first" => "one",
626
- "stuff" => "two"
627
- }
1178
+ describe "Level 3:" do
1179
+ context "no operator" do
1180
+ subject { Addressable::Template.new("foo{foo,bar}baz") }
1181
+ it "can match" do
1182
+ data = subject.match("foofoo,barbaz")
1183
+ expect(data.mapping["foo"]).to eq("foo")
1184
+ expect(data.mapping["bar"]).to eq("bar")
1185
+ end
1186
+ it "lists vars" do
1187
+ expect(subject.variables).to eq(["foo", "bar"])
1188
+ end
1189
+ end
1190
+ context "+ operator" do
1191
+ subject { Addressable::Template.new("foo{+foo,bar}baz") }
1192
+ it "can match" do
1193
+ data = subject.match("foofoo/bar,barbaz")
1194
+ expect(data.mapping["bar"]).to eq("foo/bar,bar")
1195
+ expect(data.mapping["foo"]).to eq("")
1196
+ end
1197
+ it "lists vars" do
1198
+ expect(subject.variables).to eq(["foo", "bar"])
1199
+ end
1200
+ end
1201
+ context ". operator" do
1202
+ subject { Addressable::Template.new("foo{.foo,bar}baz") }
1203
+ it "can match" do
1204
+ data = subject.match("foo.foo.barbaz")
1205
+ expect(data.mapping["foo"]).to eq("foo")
1206
+ expect(data.mapping["bar"]).to eq("bar")
1207
+ end
1208
+ it "lists vars" do
1209
+ expect(subject.variables).to eq(["foo", "bar"])
1210
+ end
1211
+ end
1212
+ context "/ operator" do
1213
+ subject { Addressable::Template.new("foo{/foo,bar}baz") }
1214
+ it "can match" do
1215
+ data = subject.match("foo/foo/barbaz")
1216
+ expect(data.mapping["foo"]).to eq("foo")
1217
+ expect(data.mapping["bar"]).to eq("bar")
1218
+ end
1219
+ it "lists vars" do
1220
+ expect(subject.variables).to eq(["foo", "bar"])
1221
+ end
1222
+ end
1223
+ context "; operator" do
1224
+ subject { Addressable::Template.new("foo{;foo,bar,baz}baz") }
1225
+ it "can match" do
1226
+ data = subject.match("foo;foo=bar%20baz;bar=foo;bazbaz")
1227
+ expect(data.mapping["foo"]).to eq("bar baz")
1228
+ expect(data.mapping["bar"]).to eq("foo")
1229
+ expect(data.mapping["baz"]).to eq("")
1230
+ end
1231
+ it "lists vars" do
1232
+ expect(subject.variables).to eq(%w(foo bar baz))
1233
+ end
1234
+ end
1235
+ context "? operator" do
1236
+ context "test" do
1237
+ subject { Addressable::Template.new("foo{?foo,bar}baz") }
1238
+ it "can match" do
1239
+ data = subject.match("foo?foo=bar%20baz&bar=foobaz")
1240
+ expect(data.mapping["foo"]).to eq("bar baz")
1241
+ expect(data.mapping["bar"]).to eq("foo")
1242
+ end
1243
+ it "lists vars" do
1244
+ expect(subject.variables).to eq(%w(foo bar))
1245
+ end
1246
+ end
1247
+
1248
+ context "issue #137" do
1249
+ subject { Addressable::Template.new('/path{?page,per_page}') }
1250
+
1251
+ it "can match empty" do
1252
+ data = subject.match("/path")
1253
+ expect(data.mapping["page"]).to eq(nil)
1254
+ expect(data.mapping["per_page"]).to eq(nil)
1255
+ expect(data.mapping.keys.sort).to eq(['page', 'per_page'])
1256
+ end
1257
+
1258
+ it "can match first var" do
1259
+ data = subject.match("/path?page=1")
1260
+ expect(data.mapping["page"]).to eq("1")
1261
+ expect(data.mapping["per_page"]).to eq(nil)
1262
+ expect(data.mapping.keys.sort).to eq(['page', 'per_page'])
1263
+ end
1264
+
1265
+ it "can match second var" do
1266
+ data = subject.match("/path?per_page=1")
1267
+ expect(data.mapping["page"]).to eq(nil)
1268
+ expect(data.mapping["per_page"]).to eq("1")
1269
+ expect(data.mapping.keys.sort).to eq(['page', 'per_page'])
1270
+ end
1271
+
1272
+ it "can match both vars" do
1273
+ data = subject.match("/path?page=2&per_page=1")
1274
+ expect(data.mapping["page"]).to eq("2")
1275
+ expect(data.mapping["per_page"]).to eq("1")
1276
+ expect(data.mapping.keys.sort).to eq(['page', 'per_page'])
1277
+ end
1278
+ end
1279
+
1280
+ context "issue #71" do
1281
+ subject { Addressable::Template.new("http://cyberscore.dev/api/users{?username}") }
1282
+ it "can match" do
1283
+ data = subject.match("http://cyberscore.dev/api/users?username=foobaz")
1284
+ expect(data.mapping["username"]).to eq("foobaz")
1285
+ end
1286
+ it "lists vars" do
1287
+ expect(subject.variables).to eq(%w(username))
1288
+ expect(subject.keys).to eq(%w(username))
1289
+ end
1290
+ end
1291
+ end
1292
+ context "& operator" do
1293
+ subject { Addressable::Template.new("foo{&foo,bar}baz") }
1294
+ it "can match" do
1295
+ data = subject.match("foo&foo=bar%20baz&bar=foobaz")
1296
+ expect(data.mapping["foo"]).to eq("bar baz")
1297
+ expect(data.mapping["bar"]).to eq("foo")
1298
+ end
1299
+ it "lists vars" do
1300
+ expect(subject.variables).to eq(%w(foo bar))
1301
+ end
1302
+ end
1303
+ end
628
1304
  end
629
1305
 
630
- it "should not match anything when extracting values " +
631
- "using the incorrect suffix pattern " +
632
- "'http://example.com/{-prefix|/|stuff}/'" do
633
- Addressable::Template.new(
634
- "http://example.com/{-prefix|/|stuff}/"
635
- ).extract(@uri).should == nil
1306
+ context "support regexes:" do
1307
+ context "EXPRESSION" do
1308
+ subject { Addressable::Template::EXPRESSION }
1309
+ it "should be able to match an expression" do
1310
+ expect(subject).to match("{foo}")
1311
+ expect(subject).to match("{foo,9}")
1312
+ expect(subject).to match("{foo.bar,baz}")
1313
+ expect(subject).to match("{+foo.bar,baz}")
1314
+ expect(subject).to match("{foo,foo%20bar}")
1315
+ expect(subject).to match("{#foo:20,baz*}")
1316
+ expect(subject).to match("stuff{#foo:20,baz*}things")
1317
+ end
1318
+ it "should fail on non vars" do
1319
+ expect(subject).not_to match("!{foo")
1320
+ expect(subject).not_to match("{foo.bar.}")
1321
+ expect(subject).not_to match("!{}")
1322
+ end
1323
+ end
1324
+ context "VARNAME" do
1325
+ subject { Addressable::Template::VARNAME }
1326
+ it "should be able to match a variable" do
1327
+ expect(subject).to match("foo")
1328
+ expect(subject).to match("9")
1329
+ expect(subject).to match("foo.bar")
1330
+ expect(subject).to match("foo_bar")
1331
+ expect(subject).to match("foo_bar.baz")
1332
+ expect(subject).to match("foo%20bar")
1333
+ expect(subject).to match("foo%20bar.baz")
1334
+ end
1335
+ it "should fail on non vars" do
1336
+ expect(subject).not_to match("!foo")
1337
+ expect(subject).not_to match("foo.bar.")
1338
+ expect(subject).not_to match("foo%2%00bar")
1339
+ expect(subject).not_to match("foo_ba%r")
1340
+ expect(subject).not_to match("foo_bar*")
1341
+ expect(subject).not_to match("foo_bar:20")
1342
+ end
1343
+ end
1344
+ context "VARIABLE_LIST" do
1345
+ subject { Addressable::Template::VARIABLE_LIST }
1346
+ it "should be able to match a variable list" do
1347
+ expect(subject).to match("foo,bar")
1348
+ expect(subject).to match("foo")
1349
+ expect(subject).to match("foo,bar*,baz")
1350
+ expect(subject).to match("foo.bar,bar_baz*,baz:12")
1351
+ end
1352
+ it "should fail on non vars" do
1353
+ expect(subject).not_to match(",foo,bar*,baz")
1354
+ expect(subject).not_to match("foo,*bar,baz")
1355
+ expect(subject).not_to match("foo,,bar*,baz")
1356
+ end
1357
+ end
1358
+ context "VARSPEC" do
1359
+ subject { Addressable::Template::VARSPEC }
1360
+ it "should be able to match a variable with modifier" do
1361
+ expect(subject).to match("9:8")
1362
+ expect(subject).to match("foo.bar*")
1363
+ expect(subject).to match("foo_bar:12")
1364
+ expect(subject).to match("foo_bar.baz*")
1365
+ expect(subject).to match("foo%20bar:12")
1366
+ expect(subject).to match("foo%20bar.baz*")
1367
+ end
1368
+ it "should fail on non vars" do
1369
+ expect(subject).not_to match("!foo")
1370
+ expect(subject).not_to match("*foo")
1371
+ expect(subject).not_to match("fo*o")
1372
+ expect(subject).not_to match("fo:o")
1373
+ expect(subject).not_to match("foo:")
1374
+ end
1375
+ end
636
1376
  end
1377
+ end
637
1378
 
638
- it "should have the correct mapping when extracting values " +
639
- "using the pattern " +
640
- "'http://example.com{-prefix|/|rot13}/' with the ExampleProcessor" do
641
- Addressable::Template.new(
642
- "http://example.com{-prefix|/|rot13}/"
643
- ).extract(@uri, ExampleProcessor).should == {
644
- "rot13" => ["bar", "fcnpre", "gjb"]
645
- }
1379
+ describe Addressable::Template::MatchData do
1380
+ let(:template) { Addressable::Template.new('{foo}/{bar}') }
1381
+ subject(:its) { template.match('ab/cd') }
1382
+ its(:uri) { should == Addressable::URI.parse('ab/cd') }
1383
+ its(:template) { should == template }
1384
+ its(:mapping) { should == { 'foo' => 'ab', 'bar' => 'cd' } }
1385
+ its(:variables) { should == ['foo', 'bar'] }
1386
+ its(:keys) { should == ['foo', 'bar'] }
1387
+ its(:names) { should == ['foo', 'bar'] }
1388
+ its(:values) { should == ['ab', 'cd'] }
1389
+ its(:captures) { should == ['ab', 'cd'] }
1390
+ its(:to_a) { should == ['ab/cd', 'ab', 'cd'] }
1391
+ its(:to_s) { should == 'ab/cd' }
1392
+ its(:string) { should == its.to_s }
1393
+ its(:pre_match) { should == "" }
1394
+ its(:post_match) { should == "" }
1395
+
1396
+ describe 'values_at' do
1397
+ it 'returns an array with the values' do
1398
+ expect(its.values_at(0, 2)).to eq(['ab/cd', 'cd'])
1399
+ end
1400
+ it 'allows mixing integer an string keys' do
1401
+ expect(its.values_at('foo', 1)).to eq(['ab', 'ab'])
1402
+ end
1403
+ it 'accepts unknown keys' do
1404
+ expect(its.values_at('baz', 'foo')).to eq([nil, 'ab'])
1405
+ end
646
1406
  end
647
1407
 
648
- it "should have the correct mapping when extracting values " +
649
- "using the pattern " +
650
- "'http://example.com{-prefix|/|rot13}' with the ExampleProcessor" do
651
- Addressable::Template.new(
652
- "http://example.com{-prefix|/|rot13}"
653
- ).extract(@uri, ExampleProcessor).should == {
654
- "rot13" => ["bar", "fcnpre", "gjb", ""]
655
- }
656
- end
657
-
658
- it "should not match anything when extracting values " +
659
- "using the incorrect suffix pattern " +
660
- "'http://example.com/{-prefix|/|rot13}' with the ExampleProcessor" do
661
- Addressable::Template.new(
662
- "http://example.com/{-prefix|/|rot13}"
663
- ).extract(@uri, ExampleProcessor).should == nil
664
- end
665
-
666
- it "should have the correct mapping when extracting values " +
667
- "using the pattern " +
668
- "'http://example.com/{-suffix|/|stuff}'" do
669
- Addressable::Template.new(
670
- "http://example.com/{-suffix|/|stuff}"
671
- ).extract(@uri).should == {
672
- "stuff" => ["one", "spacer", "two"]
673
- }
674
- end
675
-
676
- it "should have the correct mapping when extracting values " +
677
- "using the pattern " +
678
- "'http://example.com/{-suffix|/|stuff}o'" do
679
- Addressable::Template.new(
680
- "http://example.com/{-suffix|/|stuff}o"
681
- ).extract(@uri).should == nil
682
- end
683
-
684
- it "should have the correct mapping when extracting values " +
685
- "using the pattern " +
686
- "'http://example.com/o{-suffix|/|stuff}'" do
687
- Addressable::Template.new(
688
- "http://example.com/o{-suffix|/|stuff}"
689
- ).extract(@uri).should == {"stuff"=>["ne", "spacer", "two"]}
690
- end
691
-
692
- it "should have the correct mapping when extracting values " +
693
- "using the pattern " +
694
- "'http://example.com/{first}/spacer/{-suffix|/|stuff}'" do
695
- Addressable::Template.new(
696
- "http://example.com/{first}/spacer/{-suffix|/|stuff}"
697
- ).extract(@uri).should == {
698
- "first" => "one",
699
- "stuff" => "two"
700
- }
701
- end
702
-
703
- it "should not match anything when extracting values " +
704
- "using the incorrect suffix pattern " +
705
- "'http://example.com/{-suffix|/|stuff}/'" do
706
- Addressable::Template.new(
707
- "http://example.com/{-suffix|/|stuff}/"
708
- ).extract(@uri).should == nil
709
- end
710
-
711
- it "should have the correct mapping when extracting values " +
712
- "using the pattern " +
713
- "'http://example.com/{-suffix|/|rot13}' with the ExampleProcessor" do
714
- Addressable::Template.new(
715
- "http://example.com/{-suffix|/|rot13}"
716
- ).extract(@uri, ExampleProcessor).should == {
717
- "rot13" => ["bar", "fcnpre", "gjb"]
718
- }
719
- end
720
-
721
- it "should have the correct mapping when extracting values " +
722
- "using the pattern " +
723
- "'http://example.com{-suffix|/|rot13}' with the ExampleProcessor" do
724
- Addressable::Template.new(
725
- "http://example.com{-suffix|/|rot13}"
726
- ).extract(@uri, ExampleProcessor).should == {
727
- "rot13" => ["", "bar", "fcnpre", "gjb"]
728
- }
729
- end
730
-
731
- it "should not match anything when extracting values " +
732
- "using the incorrect suffix pattern " +
733
- "'http://example.com/{-suffix|/|rot13}/' with the ExampleProcessor" do
734
- Addressable::Template.new(
735
- "http://example.com/{-suffix|/|rot13}/"
736
- ).extract(@uri, ExampleProcessor).should == nil
737
- end
738
- end
739
-
740
- describe Addressable::URI, "when parsed from " +
741
- "'http://example.com/?email=bob@sporkmonger.com'" do
742
- before do
743
- @uri = Addressable::URI.parse(
744
- "http://example.com/?email=bob@sporkmonger.com"
745
- )
746
- end
747
-
748
- it "should not match anything when extracting values " +
749
- "using the incorrect opt pattern " +
750
- "'http://example.com/?email={-opt|bogus@bogus.com|test}'" do
751
- Addressable::Template.new(
752
- "http://example.com/?email={-opt|bogus@bogus.com|test}"
753
- ).extract(@uri).should == nil
754
- end
755
-
756
- it "should not match anything when extracting values " +
757
- "using the incorrect neg pattern " +
758
- "'http://example.com/?email={-neg|bogus@bogus.com|test}'" do
759
- Addressable::Template.new(
760
- "http://example.com/?email={-neg|bogus@bogus.com|test}"
761
- ).extract(@uri).should == nil
762
- end
763
-
764
- it "should indicate a match when extracting values " +
765
- "using the opt pattern " +
766
- "'http://example.com/?email={-opt|bob@sporkmonger.com|test}'" do
767
- Addressable::Template.new(
768
- "http://example.com/?email={-opt|bob@sporkmonger.com|test}"
769
- ).extract(@uri).should == {}
770
- end
771
-
772
- it "should indicate a match when extracting values " +
773
- "using the neg pattern " +
774
- "'http://example.com/?email={-neg|bob@sporkmonger.com|test}'" do
775
- Addressable::Template.new(
776
- "http://example.com/?email={-neg|bob@sporkmonger.com|test}"
777
- ).extract(@uri).should == {}
778
- end
779
- end
780
-
781
- describe Addressable::URI, "when parsed from " +
782
- "'http://example.com/?email='" do
783
- before do
784
- @uri = Addressable::URI.parse(
785
- "http://example.com/?email="
786
- )
787
- end
788
-
789
- it "should indicate a match when extracting values " +
790
- "using the opt pattern " +
791
- "'http://example.com/?email={-opt|bob@sporkmonger.com|test}'" do
792
- Addressable::Template.new(
793
- "http://example.com/?email={-opt|bob@sporkmonger.com|test}"
794
- ).extract(@uri).should == {}
795
- end
796
-
797
- it "should indicate a match when extracting values " +
798
- "using the neg pattern " +
799
- "'http://example.com/?email={-neg|bob@sporkmonger.com|test}'" do
800
- Addressable::Template.new(
801
- "http://example.com/?email={-neg|bob@sporkmonger.com|test}"
802
- ).extract(@uri).should == {}
803
- end
804
- end
805
-
806
- describe Addressable::URI, "when parsed from " +
807
- "'http://example.com/a/b/c/?one=1&two=2#foo'" do
808
- before do
809
- @uri = Addressable::URI.parse(
810
- "http://example.com/a/b/c/?one=1&two=2#foo"
811
- )
812
- end
813
-
814
- it "should have the correct mapping when extracting values " +
815
- "using the pattern " +
816
- "'http://{host}/{-suffix|/|segments}?{-join|&|one,two}\#{fragment}'" do
817
- Addressable::Template.new(
818
- "http://{host}/{-suffix|/|segments}?{-join|&|one,two}\#{fragment}"
819
- ).extract(@uri).should == {
820
- "host" => "example.com",
821
- "segments" => ["a", "b", "c"],
822
- "one" => "1",
823
- "two" => "2",
824
- "fragment" => "foo"
825
- }
826
- end
827
-
828
- it "should not match when extracting values " +
829
- "using the pattern " +
830
- "'http://{host}/{-suffix|/|segments}?{-join|&|one}\#{fragment}'" do
831
- Addressable::Template.new(
832
- "http://{host}/{-suffix|/|segments}?{-join|&|one}\#{fragment}"
833
- ).extract(@uri).should == nil
834
- end
835
-
836
- it "should not match when extracting values " +
837
- "using the pattern " +
838
- "'http://{host}/{-suffix|/|segments}?{-join|&|bogus}\#{fragment}'" do
839
- Addressable::Template.new(
840
- "http://{host}/{-suffix|/|segments}?{-join|&|bogus}\#{fragment}"
841
- ).extract(@uri).should == nil
842
- end
843
-
844
- it "should not match when extracting values " +
845
- "using the pattern " +
846
- "'http://{host}/{-suffix|/|segments}?" +
847
- "{-join|&|one,bogus}\#{fragment}'" do
848
- Addressable::Template.new(
849
- "http://{host}/{-suffix|/|segments}?{-join|&|one,bogus}\#{fragment}"
850
- ).extract(@uri).should == nil
851
- end
852
-
853
- it "should not match when extracting values " +
854
- "using the pattern " +
855
- "'http://{host}/{-suffix|/|segments}?" +
856
- "{-join|&|one,two,bogus}\#{fragment}'" do
857
- Addressable::Template.new(
858
- "http://{host}/{-suffix|/|segments}?{-join|&|one,two,bogus}\#{fragment}"
859
- ).extract(@uri).should == {
860
- "host" => "example.com",
861
- "segments" => ["a", "b", "c"],
862
- "one" => "1",
863
- "two" => "2",
864
- "fragment" => "foo"
865
- }
866
- end
867
- end
868
-
869
- describe Addressable::URI, "when given a pattern with bogus operators" do
870
- before do
871
- @uri = Addressable::URI.parse("http://example.com/a/b/c/")
872
- end
873
-
874
- it "should raise an InvalidTemplateOperatorError" do
875
- (lambda do
876
- Addressable::Template.new(
877
- "http://example.com/{-bogus|/|a,b,c}/"
878
- ).extract(@uri)
879
- end).should raise_error(
880
- Addressable::Template::InvalidTemplateOperatorError
881
- )
882
- end
883
-
884
- it "should raise an InvalidTemplateOperatorError" do
885
- (lambda do
886
- Addressable::Template.new(
887
- "http://example.com{-prefix|/|a,b,c}/"
888
- ).extract(@uri)
889
- end).should raise_error(
890
- Addressable::Template::InvalidTemplateOperatorError
891
- )
892
- end
893
-
894
- it "should raise an InvalidTemplateOperatorError" do
895
- (lambda do
896
- Addressable::Template.new(
897
- "http://example.com/{-suffix|/|a,b,c}"
898
- ).extract(@uri)
899
- end).should raise_error(
900
- Addressable::Template::InvalidTemplateOperatorError
901
- )
902
- end
903
-
904
- it "should raise an InvalidTemplateOperatorError" do
905
- (lambda do
906
- Addressable::Template.new(
907
- "http://example.com/{-list|/|a,b,c}/"
908
- ).extract(@uri)
909
- end).should raise_error(
910
- Addressable::Template::InvalidTemplateOperatorError
911
- )
912
- end
913
- end
914
-
915
- describe Addressable::URI, "when given a mapping that contains an Array" do
916
- before do
917
- @mapping = {"query" => "an example search query".split(" ")}
918
- end
919
-
920
- it "should result in 'http://example.com/search/an+example+search+query/'" +
921
- " when used to expand 'http://example.com/search/{-list|+|query}/'" do
922
- Addressable::Template.new(
923
- "http://example.com/search/{-list|+|query}/"
924
- ).expand(@mapping).to_str.should ==
925
- "http://example.com/search/an+example+search+query/"
926
- end
927
-
928
- it "should result in 'http://example.com/search/an+example+search+query/'" +
929
- " when used to expand 'http://example.com/search/{-list|+|query}/'" +
930
- " with a NoOpProcessor" do
931
- Addressable::Template.new(
932
- "http://example.com/search/{-list|+|query}/"
933
- ).expand(@mapping, NoOpProcessor).to_str.should ==
934
- "http://example.com/search/an+example+search+query/"
935
- end
936
- end
937
-
938
- describe Addressable::URI, "when given an empty mapping" do
939
- before do
940
- @mapping = {}
941
- end
942
-
943
- it "should result in 'http://example.com/search/'" +
944
- " when used to expand 'http://example.com/search/{-list|+|query}'" do
945
- Addressable::Template.new(
946
- "http://example.com/search/{-list|+|query}"
947
- ).expand(@mapping).to_str.should == "http://example.com/search/"
948
- end
949
-
950
- it "should result in 'http://example.com'" +
951
- " when used to expand 'http://example.com{-prefix|/|foo}'" do
952
- Addressable::Template.new(
953
- "http://example.com{-prefix|/|foo}"
954
- ).expand(@mapping).to_str.should == "http://example.com"
955
- end
956
-
957
- it "should result in 'http://example.com'" +
958
- " when used to expand 'http://example.com{-suffix|/|foo}'" do
959
- Addressable::Template.new(
960
- "http://example.com{-suffix|/|foo}"
961
- ).expand(@mapping).to_str.should == "http://example.com"
962
- end
963
- end
964
-
965
- describe Addressable::URI, "when given the template pattern " +
966
- "'http://example.com/search/{query}/' " +
967
- "to be processed with the ExampleProcessor" do
968
- before do
969
- @pattern = "http://example.com/search/{query}/"
970
- end
971
-
972
- it "should expand to " +
973
- "'http://example.com/search/an+example+search+query/' " +
974
- "with a mapping of {\"query\" => \"an example search query\"} " do
975
- Addressable::Template.new(
976
- "http://example.com/search/{query}/"
977
- ).expand({
978
- "query" => "an example search query"
979
- }, ExampleProcessor).to_s.should ==
980
- "http://example.com/search/an+example+search+query/"
981
- end
982
-
983
- it "should raise an error " +
984
- "with a mapping of {\"query\" => \"invalid!\"}" do
985
- (lambda do
986
- Addressable::Template.new(
987
- "http://example.com/search/{query}/"
988
- ).expand({"query" => "invalid!"}, ExampleProcessor).to_s
989
- end).should raise_error(Addressable::Template::InvalidTemplateValueError)
990
- end
991
- end
992
-
993
- # Section 3.3.1 of the URI Template draft v 01
994
- describe Addressable::URI, "when given the mapping supplied in " +
995
- "Section 3.3.1 of the URI Template draft v 01" do
996
- before do
997
- @mapping = {
998
- "a" => "fred",
999
- "b" => "barney",
1000
- "c" => "cheeseburger",
1001
- "d" => "one two three",
1002
- "e" => "20% tricky",
1003
- "f" => "",
1004
- "20" => "this-is-spinal-tap",
1005
- "scheme" => "https",
1006
- "p" => "quote=to+be+or+not+to+be",
1007
- "q" => "hullo#world"
1008
- }
1009
- end
1010
-
1011
- it "should result in 'http://example.org/page1#fred' " +
1012
- "when used to expand 'http://example.org/page1\#{a}'" do
1013
- Addressable::Template.new(
1014
- "http://example.org/page1\#{a}"
1015
- ).expand(@mapping).to_s.should == "http://example.org/page1#fred"
1016
- end
1017
-
1018
- it "should result in 'http://example.org/fred/barney/' " +
1019
- "when used to expand 'http://example.org/{a}/{b}/'" do
1020
- Addressable::Template.new(
1021
- "http://example.org/{a}/{b}/"
1022
- ).expand(@mapping).to_s.should == "http://example.org/fred/barney/"
1023
- end
1024
-
1025
- it "should result in 'http://example.org/fredbarney/' " +
1026
- "when used to expand 'http://example.org/{a}{b}/'" do
1027
- Addressable::Template.new(
1028
- "http://example.org/{a}{b}/"
1029
- ).expand(@mapping).to_s.should == "http://example.org/fredbarney/"
1030
- end
1031
-
1032
- it "should result in " +
1033
- "'http://example.com/order/cheeseburger/cheeseburger/cheeseburger/' " +
1034
- "when used to expand 'http://example.com/order/{c}/{c}/{c}/'" do
1035
- Addressable::Template.new(
1036
- "http://example.com/order/{c}/{c}/{c}/"
1037
- ).expand(@mapping).to_s.should ==
1038
- "http://example.com/order/cheeseburger/cheeseburger/cheeseburger/"
1039
- end
1040
-
1041
- it "should result in 'http://example.org/one%20two%20three' " +
1042
- "when used to expand 'http://example.org/{d}'" do
1043
- Addressable::Template.new(
1044
- "http://example.org/{d}"
1045
- ).expand(@mapping).to_s.should ==
1046
- "http://example.org/one%20two%20three"
1047
- end
1048
-
1049
- it "should result in 'http://example.org/20%25%20tricky' " +
1050
- "when used to expand 'http://example.org/{e}'" do
1051
- Addressable::Template.new(
1052
- "http://example.org/{e}"
1053
- ).expand(@mapping).to_s.should ==
1054
- "http://example.org/20%25%20tricky"
1055
- end
1056
-
1057
- it "should result in 'http://example.com//' " +
1058
- "when used to expand 'http://example.com/{f}/'" do
1059
- Addressable::Template.new(
1060
- "http://example.com/{f}/"
1061
- ).expand(@mapping).to_s.should ==
1062
- "http://example.com//"
1063
- end
1064
-
1065
- it "should result in " +
1066
- "'https://this-is-spinal-tap.example.org?date=&option=fred' " +
1067
- "when used to expand " +
1068
- "'{scheme}://{20}.example.org?date={wilma}&option={a}'" do
1069
- Addressable::Template.new(
1070
- "{scheme}://{20}.example.org?date={wilma}&option={a}"
1071
- ).expand(@mapping).to_s.should ==
1072
- "https://this-is-spinal-tap.example.org?date=&option=fred"
1073
- end
1074
-
1075
- # The v 01 draft conflicts with the v 03 draft here.
1076
- # The Addressable implementation uses v 03.
1077
- it "should result in " +
1078
- "'http://example.org?quote%3Dto%2Bbe%2Bor%2Bnot%2Bto%2Bbe' " +
1079
- "when used to expand 'http://example.org?{p}'" do
1080
- Addressable::Template.new(
1081
- "http://example.org?{p}"
1082
- ).expand(@mapping).to_s.should ==
1083
- "http://example.org?quote%3Dto%2Bbe%2Bor%2Bnot%2Bto%2Bbe"
1084
- end
1085
-
1086
- # The v 01 draft conflicts with the v 03 draft here.
1087
- # The Addressable implementation uses v 03.
1088
- it "should result in 'http://example.com/hullo%23world' " +
1089
- "when used to expand 'http://example.com/{q}'" do
1090
- Addressable::Template.new(
1091
- "http://example.com/{q}"
1092
- ).expand(@mapping).to_s.should == "http://example.com/hullo%23world"
1093
- end
1094
- end
1095
-
1096
- # Section 4.5 of the URI Template draft v 03
1097
- describe Addressable::URI, "when given the mapping supplied in " +
1098
- "Section 4.5 of the URI Template draft v 03" do
1099
- before do
1100
- @mapping = {
1101
- "foo" => "ϓ",
1102
- "bar" => "fred",
1103
- "baz" => "10,20,30",
1104
- "qux" => ["10","20","30"],
1105
- "corge" => [],
1106
- "grault" => "",
1107
- "garply" => "a/b/c",
1108
- "waldo" => "ben & jerrys",
1109
- "fred" => ["fred", "", "wilma"],
1110
- "plugh" => ["ẛ", "ṡ"],
1111
- "1-a_b.c" => "200"
1112
- }
1113
- end
1114
-
1115
- it "should result in 'http://example.org/?q=fred' " +
1116
- "when used to expand 'http://example.org/?q={bar}'" do
1117
- Addressable::Template.new(
1118
- "http://example.org/?q={bar}"
1119
- ).expand(@mapping).to_s.should == "http://example.org/?q=fred"
1120
- end
1121
-
1122
- it "should result in '/' " +
1123
- "when used to expand '/{xyzzy}'" do
1124
- Addressable::Template.new(
1125
- "/{xyzzy}"
1126
- ).expand(@mapping).to_s.should == "/"
1127
- end
1128
-
1129
- it "should result in " +
1130
- "'http://example.org/?foo=%CE%8E&bar=fred&baz=10%2C20%2C30' " +
1131
- "when used to expand " +
1132
- "'http://example.org/?{-join|&|foo,bar,xyzzy,baz}'" do
1133
- Addressable::Template.new(
1134
- "http://example.org/?{-join|&|foo,bar,xyzzy,baz}"
1135
- ).expand(@mapping).to_s.should ==
1136
- "http://example.org/?foo=%CE%8E&bar=fred&baz=10%2C20%2C30"
1137
- end
1138
-
1139
- it "should result in 'http://example.org/?d=10,20,30' " +
1140
- "when used to expand 'http://example.org/?d={-list|,|qux}'" do
1141
- Addressable::Template.new(
1142
- "http://example.org/?d={-list|,|qux}"
1143
- ).expand(
1144
- @mapping
1145
- ).to_s.should == "http://example.org/?d=10,20,30"
1146
- end
1147
-
1148
- it "should result in 'http://example.org/?d=10&d=20&d=30' " +
1149
- "when used to expand 'http://example.org/?d={-list|&d=|qux}'" do
1150
- Addressable::Template.new(
1151
- "http://example.org/?d={-list|&d=|qux}"
1152
- ).expand(
1153
- @mapping
1154
- ).to_s.should == "http://example.org/?d=10&d=20&d=30"
1155
- end
1156
-
1157
- it "should result in 'http://example.org/fredfred/a%2Fb%2Fc' " +
1158
- "when used to expand 'http://example.org/{bar}{bar}/{garply}'" do
1159
- Addressable::Template.new(
1160
- "http://example.org/{bar}{bar}/{garply}"
1161
- ).expand(
1162
- @mapping
1163
- ).to_s.should == "http://example.org/fredfred/a%2Fb%2Fc"
1164
- end
1165
-
1166
- it "should result in 'http://example.org/fred/fred//wilma' " +
1167
- "when used to expand 'http://example.org/{bar}{-prefix|/|fred}'" do
1168
- Addressable::Template.new(
1169
- "http://example.org/{bar}{-prefix|/|fred}"
1170
- ).expand(
1171
- @mapping
1172
- ).to_s.should == "http://example.org/fred/fred//wilma"
1173
- end
1174
-
1175
- it "should result in ':%E1%B9%A1:%E1%B9%A1:' " +
1176
- "when used to expand '{-neg|:|corge}{-suffix|:|plugh}'" do
1177
- Addressable::Template.new(
1178
- "{-neg|:|corge}{-suffix|:|plugh}"
1179
- ).expand(
1180
- @mapping
1181
- ).to_s.should == ":%E1%B9%A1:%E1%B9%A1:"
1182
- end
1183
-
1184
- it "should result in '../ben%20%26%20jerrys/' " +
1185
- "when used to expand '../{waldo}/'" do
1186
- Addressable::Template.new(
1187
- "../{waldo}/"
1188
- ).expand(
1189
- @mapping
1190
- ).to_s.should == "../ben%20%26%20jerrys/"
1191
- end
1192
-
1193
- it "should result in 'telnet:192.0.2.16:80' " +
1194
- "when used to expand 'telnet:192.0.2.16{-opt|:80|grault}'" do
1195
- Addressable::Template.new(
1196
- "telnet:192.0.2.16{-opt|:80|grault}"
1197
- ).expand(
1198
- @mapping
1199
- ).to_s.should == "telnet:192.0.2.16:80"
1200
- end
1201
-
1202
- it "should result in ':200:' " +
1203
- "when used to expand ':{1-a_b.c}:'" do
1204
- Addressable::Template.new(
1205
- ":{1-a_b.c}:"
1206
- ).expand(
1207
- @mapping
1208
- ).to_s.should == ":200:"
1209
- end
1210
- end
1211
-
1212
- describe Addressable::URI, "when given a mapping that contains a " +
1213
- "template-var within a value" do
1214
- before do
1215
- @mapping = {
1216
- "a" => "{b}",
1217
- "b" => "barney",
1218
- }
1219
- end
1220
-
1221
- it "should result in 'http://example.com/%7Bb%7D/barney/' " +
1222
- "when used to expand 'http://example.com/{a}/{b}/'" do
1223
- Addressable::Template.new(
1224
- "http://example.com/{a}/{b}/"
1225
- ).expand(
1226
- @mapping
1227
- ).to_s.should == "http://example.com/%7Bb%7D/barney/"
1228
- end
1229
-
1230
- it "should result in 'http://example.com//%7Bb%7D/' " +
1231
- "when used to expand 'http://example.com/{-opt|foo|foo}/{a}/'" do
1232
- Addressable::Template.new(
1233
- "http://example.com/{-opt|foo|foo}/{a}/"
1234
- ).expand(
1235
- @mapping
1236
- ).to_s.should == "http://example.com//%7Bb%7D/"
1237
- end
1238
-
1239
- it "should result in 'http://example.com//%7Bb%7D/' " +
1240
- "when used to expand 'http://example.com/{-neg|foo|b}/{a}/'" do
1241
- Addressable::Template.new(
1242
- "http://example.com/{-neg|foo|b}/{a}/"
1243
- ).expand(
1244
- @mapping
1245
- ).to_s.should == "http://example.com//%7Bb%7D/"
1246
- end
1247
-
1248
- it "should result in 'http://example.com//barney/%7Bb%7D/' " +
1249
- "when used to expand 'http://example.com/{-prefix|/|b}/{a}/'" do
1250
- Addressable::Template.new(
1251
- "http://example.com/{-prefix|/|b}/{a}/"
1252
- ).expand(
1253
- @mapping
1254
- ).to_s.should == "http://example.com//barney/%7Bb%7D/"
1255
- end
1256
-
1257
- it "should result in 'http://example.com/barney//%7Bb%7D/' " +
1258
- "when used to expand 'http://example.com/{-suffix|/|b}/{a}/'" do
1259
- Addressable::Template.new(
1260
- "http://example.com/{-suffix|/|b}/{a}/"
1261
- ).expand(
1262
- @mapping
1263
- ).to_s.should == "http://example.com/barney//%7Bb%7D/"
1264
- end
1265
-
1266
- it "should result in 'http://example.com/%7Bb%7D/?b=barney&c=42' " +
1267
- "when used to expand 'http://example.com/{a}/?{-join|&|b,c=42}'" do
1268
- Addressable::Template.new(
1269
- "http://example.com/{a}/?{-join|&|b,c=42}"
1270
- ).expand(
1271
- @mapping
1272
- ).to_s.should == "http://example.com/%7Bb%7D/?b=barney&c=42"
1273
- end
1274
-
1275
- it "should result in 'http://example.com/42/?b=barney' " +
1276
- "when used to expand 'http://example.com/{c=42}/?{-join|&|b}'" do
1277
- Addressable::Template.new(
1278
- "http://example.com/{c=42}/?{-join|&|b}"
1279
- ).expand(@mapping).to_s.should == "http://example.com/42/?b=barney"
1280
- end
1281
- end
1282
-
1283
- describe Addressable::URI, "when given a single variable mapping" do
1284
- before do
1285
- @mapping = {
1286
- "foo" => "fred"
1287
- }
1288
- end
1289
-
1290
- it "should result in 'fred' when used to expand '{foo}'" do
1291
- Addressable::Template.new(
1292
- "{foo}"
1293
- ).expand(@mapping).to_s.should == "fred"
1294
- end
1295
-
1296
- it "should result in 'wilma' when used to expand '{bar=wilma}'" do
1297
- Addressable::Template.new(
1298
- "{bar=wilma}"
1299
- ).expand(@mapping).to_s.should == "wilma"
1300
- end
1301
-
1302
- it "should result in '' when used to expand '{baz}'" do
1303
- Addressable::Template.new(
1304
- "{baz}"
1305
- ).expand(@mapping).to_s.should == ""
1306
- end
1307
- end
1308
-
1309
- describe Addressable::URI, "when given a simple mapping" do
1310
- before do
1311
- @mapping = {
1312
- "foo" => "fred",
1313
- "bar" => "barney",
1314
- "baz" => ""
1315
- }
1316
- end
1317
-
1318
- it "should result in 'foo=fred&bar=barney&baz=' when used to expand " +
1319
- "'{-join|&|foo,bar,baz,qux}'" do
1320
- Addressable::Template.new(
1321
- "{-join|&|foo,bar,baz,qux}"
1322
- ).expand(@mapping).to_s.should == "foo=fred&bar=barney&baz="
1323
- end
1324
-
1325
- it "should result in 'bar=barney' when used to expand " +
1326
- "'{-join|&|bar}'" do
1327
- Addressable::Template.new(
1328
- "{-join|&|bar}"
1329
- ).expand(@mapping).to_s.should == "bar=barney"
1330
- end
1331
-
1332
- it "should result in '' when used to expand " +
1333
- "'{-join|&|qux}'" do
1334
- Addressable::Template.new(
1335
- "{-join|&|qux}"
1336
- ).expand(@mapping).to_s.should == ""
1337
- end
1338
- end
1339
-
1340
- describe Addressable::URI, "extracting defaults from a pattern" do
1341
- before do
1342
- @template = Addressable::Template.new("{foo}{bar=baz}{-opt|found|cond}")
1343
- end
1344
-
1345
- it "should extract default value" do
1346
- @template.variable_defaults.should == {"bar" => "baz"}
1347
- end
1348
- end
1349
-
1350
- describe Addressable::URI, "when given a mapping with symbol keys" do
1351
- before do
1352
- @mapping = { :name => "fred" }
1353
- end
1354
-
1355
- it "should result in 'fred' when used to expand '{foo}'" do
1356
- Addressable::Template.new(
1357
- "{name}"
1358
- ).expand(@mapping).to_s.should == "fred"
1359
- end
1360
- end
1361
-
1362
- describe Addressable::URI, "when given a mapping with bogus keys" do
1363
- before do
1364
- @mapping = { Object.new => "fred" }
1365
- end
1366
-
1367
- it "should raise an error" do
1368
- (lambda do
1369
- Addressable::Template.new(
1370
- "{name}"
1371
- ).expand(@mapping)
1372
- end).should raise_error(TypeError)
1373
- end
1374
- end
1375
-
1376
- describe Addressable::URI, "when given a mapping with numeric values" do
1377
- before do
1378
- @mapping = { :id => 123 }
1379
- end
1380
-
1381
- it "should result in 'fred' when used to expand '{foo}'" do
1382
- Addressable::Template.new(
1383
- "{id}"
1384
- ).expand(@mapping).to_s.should == "123"
1385
- end
1386
- end
1387
-
1388
- describe Addressable::URI, "when given a mapping containing values " +
1389
- "that are already percent-encoded" do
1390
- before do
1391
- @mapping = {
1392
- "a" => "%7Bb%7D"
1393
- }
1394
- end
1395
-
1396
- it "should result in 'http://example.com/%257Bb%257D/' " +
1397
- "when used to expand 'http://example.com/{a}/'" do
1398
- Addressable::Template.new(
1399
- "http://example.com/{a}/"
1400
- ).expand(@mapping).to_s.should == "http://example.com/%257Bb%257D/"
1401
- end
1402
- end
1403
-
1404
- describe Addressable::URI, "when given a pattern with bogus operators" do
1405
- it "should raise an InvalidTemplateOperatorError" do
1406
- (lambda do
1407
- Addressable::Template.new(
1408
- "http://example.com/{-bogus|/|a,b,c}/"
1409
- ).expand({
1410
- "a" => "a", "b" => "b", "c" => "c"
1411
- })
1412
- end).should raise_error(
1413
- Addressable::Template::InvalidTemplateOperatorError
1414
- )
1415
- end
1416
-
1417
- it "should raise an InvalidTemplateOperatorError" do
1418
- (lambda do
1419
- Addressable::Template.new(
1420
- "http://example.com/{-prefix|/|a,b,c}/"
1421
- ).expand({
1422
- "a" => "a", "b" => "b", "c" => "c"
1423
- })
1424
- end).should raise_error(
1425
- Addressable::Template::InvalidTemplateOperatorError
1426
- )
1427
- end
1428
-
1429
- it "should raise an InvalidTemplateOperatorError" do
1430
- (lambda do
1431
- Addressable::Template.new(
1432
- "http://example.com/{-suffix|/|a,b,c}/"
1433
- ).expand({
1434
- "a" => "a", "b" => "b", "c" => "c"
1435
- })
1436
- end).should raise_error(
1437
- Addressable::Template::InvalidTemplateOperatorError
1438
- )
1439
- end
1440
-
1441
- it "should raise an InvalidTemplateOperatorError" do
1442
- (lambda do
1443
- Addressable::Template.new(
1444
- "http://example.com/{-join|/|a,b,c}/"
1445
- ).expand({
1446
- "a" => ["a"], "b" => ["b"], "c" => "c"
1447
- })
1448
- end).should raise_error(
1449
- Addressable::Template::InvalidTemplateOperatorError
1450
- )
1451
- end
1452
-
1453
- it "should raise an InvalidTemplateOperatorError" do
1454
- (lambda do
1455
- Addressable::Template.new(
1456
- "http://example.com/{-list|/|a,b,c}/"
1457
- ).expand({
1458
- "a" => ["a"], "b" => ["b"], "c" => "c"
1459
- })
1460
- end).should raise_error(
1461
- Addressable::Template::InvalidTemplateOperatorError
1462
- )
1463
- end
1464
- end
1465
-
1466
- describe Addressable::Template, "with a partially expanded template" do
1467
- before do
1468
- @initial_template = Addressable::Template.new(
1469
- "http://example.com/{one}/{two}/"
1470
- )
1471
- @partial_template = @initial_template.partial_expand({"one" => "1"})
1472
- end
1473
-
1474
- it "should produce the same result when fully expanded" do
1475
- @initial_template.expand({"one" => "1", "two" => "2"}).should ==
1476
- @partial_template.expand({"two" => "2"})
1477
- end
1478
-
1479
- it "should raise an error if the template is expanded with bogus values" do
1480
- (lambda do
1481
- @initial_template.expand({"one" => Object.new, "two" => Object.new})
1482
- end).should raise_error(TypeError)
1483
- (lambda do
1484
- @partial_template.expand({"two" => Object.new})
1485
- end).should raise_error(TypeError)
1486
- end
1487
- end
1488
-
1489
- describe Addressable::Template, "with a partially expanded template" do
1490
- before do
1491
- @initial_template = Addressable::Template.new(
1492
- "http://example.com/{one}/{two}/"
1493
- )
1494
- @partial_template = @initial_template.partial_expand({"two" => "2"})
1495
- end
1496
-
1497
- it "should produce the same result when fully expanded" do
1498
- @initial_template.expand({"one" => "1", "two" => "2"}).should ==
1499
- @partial_template.expand({"one" => "1"})
1500
- end
1501
- end
1502
-
1503
- describe Addressable::Template, "with a partially expanded template" do
1504
- before do
1505
- @initial_template = Addressable::Template.new(
1506
- "http://example.com/{one}/{two}/"
1507
- )
1508
- @partial_template = @initial_template.partial_expand({"two" => "2"})
1509
- end
1510
-
1511
- it "should produce the same result when fully expanded" do
1512
- @initial_template.expand({"one" => "1", "two" => "2"}).should ==
1513
- @partial_template.expand({"one" => "1"})
1514
- end
1515
- end
1516
-
1517
- describe Addressable::Template, "with a partially expanded template" do
1518
- before do
1519
- @initial_template = Addressable::Template.new(
1520
- "http://example.com/{one=1}/{two=2}/"
1521
- )
1522
- @partial_template = @initial_template.partial_expand({"one" => "3"})
1523
- end
1524
-
1525
- it "should produce the same result when fully expanded" do
1526
- @initial_template.expand({"one" => "3", "two" => "4"}).should ==
1527
- @partial_template.expand({"two" => "4"})
1528
- end
1529
-
1530
- it "should produce the correct result when fully expanded" do
1531
- @partial_template.expand({}).should === "http://example.com/3/2/"
1532
- end
1533
- end
1534
-
1535
- describe Addressable::Template, "with a partially expanded template" do
1536
- before do
1537
- @initial_template = Addressable::Template.new(
1538
- "http://example.com/{one=1}/{two=2}/"
1539
- )
1540
- @partial_template = @initial_template.partial_expand({"two" => "4"})
1541
- end
1542
-
1543
- it "should produce the same result when fully expanded" do
1544
- @initial_template.expand({"one" => "3", "two" => "4"}).should ==
1545
- @partial_template.expand({"one" => "3"})
1546
- end
1547
-
1548
- it "should produce the correct result when fully expanded" do
1549
- @partial_template.expand({}).should === "http://example.com/1/4/"
1550
- end
1551
- end
1552
-
1553
- describe Addressable::Template, "with a partially expanded template" do
1554
- before do
1555
- @initial_template = Addressable::Template.new(
1556
- "http://example.com/{-opt|found|one,two,three}"
1557
- )
1558
- @partial_template = @initial_template.partial_expand({})
1559
- end
1560
-
1561
- it "should produce the same result when fully expanded" do
1562
- @initial_template.expand({"one" => "1"}).to_str.should ==
1563
- @partial_template.expand({"two" => "2"}).to_str
1564
- end
1565
-
1566
- it "should produce the correct result when fully expanded" do
1567
- @partial_template.expand({}).to_str.should == "http://example.com/"
1568
- end
1569
-
1570
- it "should produce the correct result when fully expanded" do
1571
- @partial_template.expand({"one" => "1"}).to_str.should ==
1572
- "http://example.com/found"
1573
- end
1574
-
1575
- it "should produce the correct result when fully expanded" do
1576
- @partial_template.expand({"two" => "2"}).to_str.should ==
1577
- "http://example.com/found"
1578
- end
1579
-
1580
- it "should produce the correct result when fully expanded" do
1581
- @partial_template.expand({"three" => "3"}).to_str.should ==
1582
- "http://example.com/found"
1583
- end
1584
-
1585
- it "should produce the correct result when fully expanded" do
1586
- @partial_template.expand({"four" => "4"}).to_str.should ==
1587
- "http://example.com/"
1588
- end
1589
-
1590
- it "should produce the correct result when fully expanded" do
1591
- @partial_template.expand({"one" => "1", "two" => "2"}).to_str.should ==
1592
- "http://example.com/found"
1593
- end
1594
- end
1595
-
1596
- describe Addressable::Template, "with a partially expanded template" do
1597
- before do
1598
- @initial_template = Addressable::Template.new(
1599
- "http://example.com/{-opt|found|one,two,three}"
1600
- )
1601
- @partial_template = @initial_template.partial_expand({"one" => "1"})
1602
- end
1603
-
1604
- it "should produce the same result when fully expanded" do
1605
- @initial_template.expand({"one" => "1"}).to_str.should ==
1606
- @partial_template.expand({"two" => "2"}).to_str
1607
- end
1608
-
1609
- it "should produce the correct result when fully expanded" do
1610
- @partial_template.expand({}).to_str.should == "http://example.com/found"
1611
- end
1612
-
1613
- it "should produce the correct pattern" do
1614
- @partial_template.pattern.should == "http://example.com/found"
1615
- end
1616
- end
1617
-
1618
- describe Addressable::Template, "with a partially expanded template" do
1619
- before do
1620
- @initial_template = Addressable::Template.new(
1621
- "http://example.com/{-neg|notfound|one,two,three}"
1622
- )
1623
- @partial_template = @initial_template.partial_expand({})
1624
- end
1625
-
1626
- it "should produce the same result when fully expanded" do
1627
- @initial_template.expand({"one" => "1"}).to_str.should ==
1628
- @partial_template.expand({"two" => "2"}).to_str
1629
- end
1630
-
1631
- it "should produce the correct result when fully expanded" do
1632
- @partial_template.expand({}).to_str.should == "http://example.com/notfound"
1633
- end
1634
-
1635
- it "should produce the correct result when fully expanded" do
1636
- @partial_template.expand({"one" => "1"}).to_str.should ==
1637
- "http://example.com/"
1638
- end
1639
-
1640
- it "should produce the correct result when fully expanded" do
1641
- @partial_template.expand({"two" => "2"}).to_str.should ==
1642
- "http://example.com/"
1643
- end
1644
-
1645
- it "should produce the correct result when fully expanded" do
1646
- @partial_template.expand({"three" => "3"}).to_str.should ==
1647
- "http://example.com/"
1648
- end
1649
-
1650
- it "should produce the correct result when fully expanded" do
1651
- @partial_template.expand({"four" => "4"}).to_str.should ==
1652
- "http://example.com/notfound"
1653
- end
1654
-
1655
- it "should produce the correct result when fully expanded" do
1656
- @partial_template.expand({"one" => "1", "two" => "2"}).to_str.should ==
1657
- "http://example.com/"
1658
- end
1659
- end
1660
-
1661
- describe Addressable::Template, "with a partially expanded template" do
1662
- before do
1663
- @initial_template = Addressable::Template.new(
1664
- "http://example.com/{-neg|notfound|one,two,three}"
1665
- )
1666
- @partial_template = @initial_template.partial_expand({"one" => "1"})
1667
- end
1668
-
1669
- it "should produce the same result when fully expanded" do
1670
- @initial_template.expand({"one" => "1"}).to_str.should ==
1671
- @partial_template.expand({"two" => "2"}).to_str
1672
- end
1673
-
1674
- it "should produce the correct result when fully expanded" do
1675
- @partial_template.expand({}).to_str.should == "http://example.com/"
1676
- end
1677
-
1678
- it "should produce the correct pattern" do
1679
- @partial_template.pattern.should == "http://example.com/"
1680
- end
1681
- end
1682
-
1683
- describe Addressable::Template, "with a partially expanded template" do
1684
- before do
1685
- @initial_template = Addressable::Template.new(
1686
- "http://example.com/?{-prefix|x=|one}"
1687
- )
1688
- @partial_template = @initial_template.partial_expand({})
1689
- end
1690
-
1691
- it "should produce the same result when fully expanded" do
1692
- @initial_template.expand({"one" => "1"}).to_str.should ==
1693
- @partial_template.expand({"one" => "1"}).to_str
1694
- end
1695
-
1696
- it "should produce the correct result when fully expanded" do
1697
- @partial_template.expand({}).to_str.should == "http://example.com/?"
1698
- end
1699
-
1700
- it "should produce the correct result when fully expanded" do
1701
- @partial_template.expand({"one" => "1"}).to_str.should ==
1702
- "http://example.com/?x=1"
1703
- end
1704
-
1705
- it "should produce the correct result when fully expanded" do
1706
- @partial_template.expand({"two" => "2"}).to_str.should ==
1707
- "http://example.com/?"
1708
- end
1709
-
1710
- it "should produce the correct result when fully expanded" do
1711
- @partial_template.expand({"one" => "1", "two" => "2"}).to_str.should ==
1712
- "http://example.com/?x=1"
1713
- end
1714
- end
1715
-
1716
- describe Addressable::Template, "with a partially expanded template" do
1717
- before do
1718
- @initial_template = Addressable::Template.new(
1719
- "http://example.com/?{-prefix|x=|one}"
1720
- )
1721
- @partial_template = @initial_template.partial_expand({"one" => "1"})
1722
- end
1723
-
1724
- it "should produce the same result when fully expanded" do
1725
- @initial_template.expand({"one" => "1"}).to_str.should ==
1726
- @partial_template.expand({}).to_str
1727
- end
1728
-
1729
- it "should produce the correct result when fully expanded" do
1730
- @partial_template.expand({}).to_str.should == "http://example.com/?x=1"
1731
- end
1732
-
1733
- it "should produce the correct pattern" do
1734
- @partial_template.pattern.should == "http://example.com/?x=1"
1735
- end
1736
- end
1737
-
1738
- describe Addressable::Template, "with a partially expanded template" do
1739
- before do
1740
- @initial_template = Addressable::Template.new(
1741
- "http://example.com/?{-suffix|=x|one}"
1742
- )
1743
- @partial_template = @initial_template.partial_expand({})
1744
- end
1745
-
1746
- it "should produce the same result when fully expanded" do
1747
- @initial_template.expand({"one" => "1"}).to_str.should ==
1748
- @partial_template.expand({"one" => "1"}).to_str
1749
- end
1750
-
1751
- it "should produce the correct result when fully expanded" do
1752
- @partial_template.expand({}).to_str.should == "http://example.com/?"
1753
- end
1754
-
1755
- it "should produce the correct result when fully expanded" do
1756
- @partial_template.expand({"one" => "1"}).to_str.should ==
1757
- "http://example.com/?1=x"
1758
- end
1759
-
1760
- it "should produce the correct result when fully expanded" do
1761
- @partial_template.expand({"two" => "2"}).to_str.should ==
1762
- "http://example.com/?"
1763
- end
1764
-
1765
- it "should produce the correct result when fully expanded" do
1766
- @partial_template.expand({"one" => "1", "two" => "2"}).to_str.should ==
1767
- "http://example.com/?1=x"
1768
- end
1769
- end
1770
-
1771
- describe Addressable::Template, "with a partially expanded template" do
1772
- before do
1773
- @initial_template = Addressable::Template.new(
1774
- "http://example.com/?{-suffix|=x|one}"
1775
- )
1776
- @partial_template = @initial_template.partial_expand({"one" => "1"})
1777
- end
1778
-
1779
- it "should produce the same result when fully expanded" do
1780
- @initial_template.expand({"one" => "1"}).to_str.should ==
1781
- @partial_template.expand({}).to_str
1782
- end
1783
-
1784
- it "should produce the correct result when fully expanded" do
1785
- @partial_template.expand({}).to_str.should == "http://example.com/?1=x"
1786
- end
1787
-
1788
- it "should produce the correct pattern" do
1789
- @partial_template.pattern.should == "http://example.com/?1=x"
1790
- end
1791
- end
1792
-
1793
- describe Addressable::Template, "with a partially expanded template" do
1794
- before do
1795
- @initial_template = Addressable::Template.new(
1796
- "http://example.com/?{-join|&|one}"
1797
- )
1798
- @partial_template = @initial_template.partial_expand({})
1799
- end
1800
-
1801
- it "should produce the same result when fully expanded" do
1802
- @initial_template.expand({"one" => "1"}).to_str.should ==
1803
- @partial_template.expand({"one" => "1"}).to_str
1804
- end
1805
-
1806
- it "should produce the correct result when fully expanded" do
1807
- @partial_template.expand({}).to_str.should == "http://example.com/?"
1808
- end
1809
-
1810
- it "should produce the correct result when fully expanded" do
1811
- @partial_template.pattern.should == @initial_template.pattern
1812
- end
1813
- end
1814
-
1815
- describe Addressable::Template, "with a partially expanded template" do
1816
- before do
1817
- @initial_template = Addressable::Template.new(
1818
- "http://example.com/?{-join|&|one,two}"
1819
- )
1820
- @partial_template = @initial_template.partial_expand({"one" => "1"})
1821
- end
1822
-
1823
- it "should produce the same result when fully expanded" do
1824
- @initial_template.expand({"one" => "1", "two" => "2"}).to_str.should ==
1825
- @partial_template.expand({"two" => "2"}).to_str
1826
- end
1827
-
1828
- it "should produce the correct result when fully expanded" do
1829
- @partial_template.expand({}).to_str.should == "http://example.com/?one=1"
1830
- end
1831
- end
1832
-
1833
- describe Addressable::Template, "with a partially expanded template" do
1834
- before do
1835
- @initial_template = Addressable::Template.new(
1836
- "http://example.com/?{-join|&|one,two}"
1837
- )
1838
- @partial_template = @initial_template.partial_expand({"two" => "2"})
1839
- end
1840
-
1841
- it "should produce the same result when fully expanded" do
1842
- @initial_template.expand({"one" => "1", "two" => "2"}).to_str.should ==
1843
- @partial_template.expand({"one" => "1"}).to_str
1844
- end
1845
-
1846
- it "should produce the correct result when fully expanded" do
1847
- @partial_template.expand({}).to_str.should == "http://example.com/?two=2"
1848
- end
1849
- end
1850
-
1851
- describe Addressable::Template, "with a partially expanded template" do
1852
- before do
1853
- @initial_template = Addressable::Template.new(
1854
- "http://example.com/?{-join|&|one,two,three}"
1855
- )
1856
- @partial_template = @initial_template.partial_expand({"one" => "1"})
1857
- end
1858
-
1859
- it "should produce the same result when fully expanded" do
1860
- @initial_template.expand({
1861
- "one" => "1", "two" => "2", "three" => "3"
1862
- }).to_str.should ==
1863
- @partial_template.expand({"two" => "2", "three" => "3"}).to_str
1864
- end
1865
-
1866
- it "should produce the correct result when fully expanded" do
1867
- @partial_template.expand({}).to_str.should == "http://example.com/?one=1"
1868
- end
1869
-
1870
- it "should produce the correct result when fully expanded" do
1871
- @partial_template.expand({"two" => "2"}).to_str.should ==
1872
- "http://example.com/?one=1&two=2"
1873
- end
1874
-
1875
- it "should produce the correct result when fully expanded" do
1876
- @partial_template.expand({"three" => "3"}).to_str.should ==
1877
- "http://example.com/?one=1&three=3"
1878
- end
1879
- end
1880
-
1881
- describe Addressable::Template, "with a partially expanded template" do
1882
- before do
1883
- @initial_template = Addressable::Template.new(
1884
- "http://example.com/?{-join|&|one,two,three}"
1885
- )
1886
- @partial_template = @initial_template.partial_expand({"two" => "2"})
1887
- end
1888
-
1889
- it "should produce the same result when fully expanded" do
1890
- @initial_template.expand({
1891
- "one" => "1", "two" => "2", "three" => "3"
1892
- }).to_str.should ==
1893
- @partial_template.expand({"one" => "1", "three" => "3"}).to_str
1894
- end
1895
-
1896
- it "should produce the correct result when fully expanded" do
1897
- @partial_template.expand({}).to_str.should == "http://example.com/?two=2"
1898
- end
1899
-
1900
- it "should produce the correct result when fully expanded" do
1901
- @partial_template.expand({"one" => "1"}).to_str.should ==
1902
- "http://example.com/?one=1&two=2"
1903
- end
1904
-
1905
- it "should produce the correct result when fully expanded" do
1906
- @partial_template.expand({"three" => "3"}).to_str.should ==
1907
- "http://example.com/?two=2&three=3"
1908
- end
1909
- end
1910
-
1911
- describe Addressable::Template, "with a partially expanded template" do
1912
- before do
1913
- @initial_template = Addressable::Template.new(
1914
- "http://example.com/?{-join|&|one,two,three}"
1915
- )
1916
- @partial_template = @initial_template.partial_expand({"three" => "3"})
1917
- end
1918
-
1919
- it "should produce the same result when fully expanded" do
1920
- @initial_template.expand({
1921
- "one" => "1", "two" => "2", "three" => "3"
1922
- }).to_str.should ==
1923
- @partial_template.expand({"one" => "1", "two" => "2"}).to_str
1924
- end
1925
-
1926
- it "should produce the correct result when fully expanded" do
1927
- @partial_template.expand({}).to_str.should == "http://example.com/?three=3"
1928
- end
1929
-
1930
- it "should produce the correct result when fully expanded" do
1931
- @partial_template.expand({"one" => "1"}).to_str.should ==
1932
- "http://example.com/?one=1&three=3"
1933
- end
1934
-
1935
- it "should produce the correct result when fully expanded" do
1936
- @partial_template.expand({"two" => "2"}).to_str.should ==
1937
- "http://example.com/?two=2&three=3"
1938
- end
1939
- end
1940
-
1941
- describe Addressable::Template, "with a partially expanded template" do
1942
- before do
1943
- @initial_template = Addressable::Template.new(
1944
- "http://example.com/?{-join|&|one,two,three}"
1945
- )
1946
- @partial_template = @initial_template.partial_expand({
1947
- "one" => "1", "two" => "2"
1948
- })
1949
- end
1950
-
1951
- it "should produce the same result when fully expanded" do
1952
- @initial_template.expand({
1953
- "one" => "1", "two" => "2", "three" => "3"
1954
- }).to_str.should ==
1955
- @partial_template.expand({"three" => "3"}).to_str
1956
- end
1957
-
1958
- it "should produce the correct result when fully expanded" do
1959
- @partial_template.expand({}).to_str.should ==
1960
- "http://example.com/?one=1&two=2"
1961
- end
1962
-
1963
- it "should produce the correct result when fully expanded" do
1964
- @partial_template.expand({"three" => "3"}).to_str.should ==
1965
- "http://example.com/?one=1&two=2&three=3"
1966
- end
1967
- end
1968
-
1969
- describe Addressable::Template, "with a partially expanded template" do
1970
- before do
1971
- @initial_template = Addressable::Template.new(
1972
- "http://example.com/?{-join|&|one,two,three}"
1973
- )
1974
- @partial_template = @initial_template.partial_expand({
1975
- "one" => "1", "three" => "3"
1976
- })
1977
- end
1978
-
1979
- it "should produce the same result when fully expanded" do
1980
- @initial_template.expand({
1981
- "one" => "1", "two" => "2", "three" => "3"
1982
- }).to_str.should ==
1983
- @partial_template.expand({"two" => "2"}).to_str
1984
- end
1985
-
1986
- it "should produce the correct result when fully expanded" do
1987
- @partial_template.expand({}).to_str.should ==
1988
- "http://example.com/?one=1&three=3"
1989
- end
1990
-
1991
- it "should produce the correct result when fully expanded" do
1992
- @partial_template.expand({"two" => "2"}).to_str.should ==
1993
- "http://example.com/?one=1&two=2&three=3"
1994
- end
1995
- end
1996
-
1997
- describe Addressable::Template, "with a partially expanded template" do
1998
- before do
1999
- @initial_template = Addressable::Template.new(
2000
- "http://example.com/?{-join|&|one,two,three}"
2001
- )
2002
- @partial_template = @initial_template.partial_expand({
2003
- "two" => "2", "three" => "3"
2004
- })
2005
- end
2006
-
2007
- it "should produce the same result when fully expanded" do
2008
- @initial_template.expand({
2009
- "one" => "1", "two" => "2", "three" => "3"
2010
- }).to_str.should ==
2011
- @partial_template.expand({"one" => "1"}).to_str
2012
- end
2013
-
2014
- it "should produce the correct result when fully expanded" do
2015
- @partial_template.expand({}).to_str.should ==
2016
- "http://example.com/?two=2&three=3"
2017
- end
2018
-
2019
- it "should produce the correct result when fully expanded" do
2020
- @partial_template.expand({"one" => "1"}).to_str.should ==
2021
- "http://example.com/?one=1&two=2&three=3"
2022
- end
2023
- end
2024
-
2025
- describe Addressable::Template, "with a partially expanded template" do
2026
- before do
2027
- @initial_template = Addressable::Template.new(
2028
- "http://example.com/?{-join|&|one,two,three}"
2029
- )
2030
- end
2031
-
2032
- it "should raise an error when partially expanding a bogus operator" do
2033
- (lambda do
2034
- @initial_template.partial_expand({"one" => ["1"]})
2035
- end).should raise_error(
2036
- Addressable::Template::InvalidTemplateOperatorError
2037
- )
2038
- (lambda do
2039
- @initial_template.partial_expand({"two" => "2", "three" => ["3"]})
2040
- end).should raise_error(
2041
- Addressable::Template::InvalidTemplateOperatorError
2042
- )
2043
- end
2044
- end
2045
-
2046
- describe Addressable::Template, "with a partially expanded template" do
2047
- before do
2048
- @initial_template = Addressable::Template.new(
2049
- "http://example.com/{-list|/|numbers}/{-list|/|letters}/"
2050
- )
2051
- @partial_template = @initial_template.partial_expand({})
2052
- end
2053
-
2054
- it "should produce the same result when fully expanded" do
2055
- @initial_template.expand({
2056
- "numbers" => ["1", "2", "3"], "letters" => ["a", "b", "c"]
2057
- }).to_str.should == @partial_template.expand({
2058
- "numbers" => ["1", "2", "3"], "letters" => ["a", "b", "c"]
2059
- }).to_str
2060
- end
2061
-
2062
- it "should produce the correct result when fully expanded" do
2063
- @partial_template.expand({}).to_str.should == "http://example.com///"
2064
- end
2065
-
2066
- it "should produce the correct result when fully expanded" do
2067
- @partial_template.pattern.should == @initial_template.pattern
2068
- end
2069
- end
2070
-
2071
- describe Addressable::Template, "with a partially expanded template" do
2072
- before do
2073
- @initial_template = Addressable::Template.new(
2074
- "http://example.com/{-list|/|numbers}/{-list|/|letters}/"
2075
- )
2076
- @partial_template = @initial_template.partial_expand({
2077
- "numbers" => ["1", "2", "3"]
2078
- })
2079
- end
2080
-
2081
- it "should produce the same result when fully expanded" do
2082
- @initial_template.expand({
2083
- "numbers" => ["1", "2", "3"], "letters" => ["a", "b", "c"]
2084
- }).to_str.should == @partial_template.expand({
2085
- "letters" => ["a", "b", "c"]
2086
- }).to_str
2087
- end
2088
-
2089
- it "should produce the correct result when fully expanded" do
2090
- @partial_template.expand({}).to_str.should == "http://example.com/1/2/3//"
2091
- end
2092
- end
2093
-
2094
- describe Addressable::Template, "with a partially expanded template" do
2095
- before do
2096
- @initial_template = Addressable::Template.new(
2097
- "http://example.com/{-list|/|numbers}/{-list|/|letters}/"
2098
- )
2099
- @partial_template = @initial_template.partial_expand({
2100
- "letters" => ["a", "b", "c"]
2101
- })
2102
- end
2103
-
2104
- it "should produce the same result when fully expanded" do
2105
- @initial_template.expand({
2106
- "numbers" => ["1", "2", "3"], "letters" => ["a", "b", "c"]
2107
- }).to_str.should == @partial_template.expand({
2108
- "numbers" => ["1", "2", "3"]
2109
- }).to_str
2110
- end
2111
-
2112
- it "should produce the correct result when fully expanded" do
2113
- @partial_template.expand({}).to_str.should == "http://example.com//a/b/c/"
2114
- end
2115
- end
2116
-
2117
- describe Addressable::Template, "with a partially expanded template" do
2118
- before do
2119
- @initial_template = Addressable::Template.new(
2120
- "http://example.com/{-list|/|numbers}/{-list|/|letters}/"
2121
- )
2122
- end
2123
-
2124
- it "should raise an error when partially expanding a bogus operator" do
2125
- (lambda do
2126
- @initial_template.partial_expand({"numbers" => "1"})
2127
- end).should raise_error(
2128
- Addressable::Template::InvalidTemplateOperatorError
2129
- )
2130
- (lambda do
2131
- @initial_template.partial_expand({"letters" => "a"})
2132
- end).should raise_error(
2133
- Addressable::Template::InvalidTemplateOperatorError
2134
- )
2135
- end
2136
- end
2137
-
2138
- describe Addressable::Template, "with a partially expanded template" do
2139
- before do
2140
- @initial_template = Addressable::Template.new(
2141
- "http://example.com/{-bogus|/|one,two}/"
2142
- )
2143
- end
2144
-
2145
- it "should raise an error when partially expanding a bogus operator" do
2146
- (lambda do
2147
- @initial_template.partial_expand({"one" => "1"})
2148
- end).should raise_error(
2149
- Addressable::Template::InvalidTemplateOperatorError
2150
- )
1408
+ describe '[]' do
1409
+ context 'string key' do
1410
+ it 'returns the corresponding capture' do
1411
+ expect(its['foo']).to eq('ab')
1412
+ expect(its['bar']).to eq('cd')
1413
+ end
1414
+ it 'returns nil for unknown keys' do
1415
+ expect(its['baz']).to be_nil
1416
+ end
1417
+ end
1418
+ context 'symbol key' do
1419
+ it 'returns the corresponding capture' do
1420
+ expect(its[:foo]).to eq('ab')
1421
+ expect(its[:bar]).to eq('cd')
1422
+ end
1423
+ it 'returns nil for unknown keys' do
1424
+ expect(its[:baz]).to be_nil
1425
+ end
1426
+ end
1427
+ context 'integer key' do
1428
+ it 'returns the full URI for index 0' do
1429
+ expect(its[0]).to eq('ab/cd')
1430
+ end
1431
+ it 'returns the corresponding capture' do
1432
+ expect(its[1]).to eq('ab')
1433
+ expect(its[2]).to eq('cd')
1434
+ end
1435
+ it 'returns nil for unknown keys' do
1436
+ expect(its[3]).to be_nil
1437
+ end
1438
+ end
1439
+ context 'other key' do
1440
+ it 'raises an exception' do
1441
+ expect { its[Object.new] }.to raise_error(TypeError)
1442
+ end
1443
+ end
1444
+ context 'with length' do
1445
+ it 'returns an array starting at index with given length' do
1446
+ expect(its[0, 2]).to eq(['ab/cd', 'ab'])
1447
+ expect(its[2, 1]).to eq(['cd'])
1448
+ end
1449
+ end
2151
1450
  end
2152
1451
  end