addressablerer 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,238 @@
1
+ # coding: utf-8
2
+ # Copyright (C) 2006-2013 Bob Aman
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+ require "spec_helper"
18
+
19
+ # Have to use RubyGems to load the idn gem.
20
+ require "rubygems"
21
+
22
+ require "addressable/idna"
23
+
24
+ shared_examples_for "converting from unicode to ASCII" do
25
+ it "should convert 'www.google.com' correctly" do
26
+ Addressable::IDNA.to_ascii("www.google.com").should == "www.google.com"
27
+ end
28
+
29
+ it "should convert 'www.詹姆斯.com' correctly" do
30
+ Addressable::IDNA.to_ascii(
31
+ "www.詹姆斯.com"
32
+ ).should == "www.xn--8ws00zhy3a.com"
33
+ end
34
+
35
+ it "should convert 'www.Iñtërnâtiônàlizætiøn.com' correctly" do
36
+ "www.Iñtërnâtiônàlizætiøn.com"
37
+ Addressable::IDNA.to_ascii(
38
+ "www.I\xC3\xB1t\xC3\xABrn\xC3\xA2ti\xC3\xB4" +
39
+ "n\xC3\xA0liz\xC3\xA6ti\xC3\xB8n.com"
40
+ ).should == "www.xn--itrntinliztin-vdb0a5exd8ewcye.com"
41
+ end
42
+
43
+ it "should convert 'www.Iñtërnâtiônàlizætiøn.com' correctly" do
44
+ Addressable::IDNA.to_ascii(
45
+ "www.In\xCC\x83te\xCC\x88rna\xCC\x82tio\xCC\x82n" +
46
+ "a\xCC\x80liz\xC3\xA6ti\xC3\xB8n.com"
47
+ ).should == "www.xn--itrntinliztin-vdb0a5exd8ewcye.com"
48
+ end
49
+
50
+ it "should convert " +
51
+ "'www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp' " +
52
+ "correctly" do
53
+ Addressable::IDNA.to_ascii(
54
+ "www.\343\201\273\343\202\223\343\201\250\343\201\206\343\201\253\343" +
55
+ "\201\252\343\201\214\343\201\204\343\202\217\343\201\221\343\201\256" +
56
+ "\343\202\217\343\201\213\343\202\211\343\201\252\343\201\204\343\201" +
57
+ "\251\343\202\201\343\201\204\343\202\223\343\202\201\343\201\204\343" +
58
+ "\201\256\343\202\211\343\201\271\343\202\213\343\201\276\343\201\240" +
59
+ "\343\201\252\343\201\214\343\201\217\343\201\227\343\201\252\343\201" +
60
+ "\204\343\201\250\343\201\237\343\202\212\343\201\252\343\201\204." +
61
+ "w3.mag.keio.ac.jp"
62
+ ).should ==
63
+ "www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3" +
64
+ "fg11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp"
65
+ end
66
+
67
+ it "should convert " +
68
+ "'www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp' " +
69
+ "correctly" do
70
+ Addressable::IDNA.to_ascii(
71
+ "www.\343\201\273\343\202\223\343\201\250\343\201\206\343\201\253\343" +
72
+ "\201\252\343\201\213\343\202\231\343\201\204\343\202\217\343\201\221" +
73
+ "\343\201\256\343\202\217\343\201\213\343\202\211\343\201\252\343\201" +
74
+ "\204\343\201\250\343\202\231\343\202\201\343\201\204\343\202\223\343" +
75
+ "\202\201\343\201\204\343\201\256\343\202\211\343\201\270\343\202\231" +
76
+ "\343\202\213\343\201\276\343\201\237\343\202\231\343\201\252\343\201" +
77
+ "\213\343\202\231\343\201\217\343\201\227\343\201\252\343\201\204\343" +
78
+ "\201\250\343\201\237\343\202\212\343\201\252\343\201\204." +
79
+ "w3.mag.keio.ac.jp"
80
+ ).should ==
81
+ "www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3" +
82
+ "fg11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp"
83
+ end
84
+
85
+ it "should convert '点心和烤鸭.w3.mag.keio.ac.jp' correctly" do
86
+ Addressable::IDNA.to_ascii(
87
+ "点心和烤鸭.w3.mag.keio.ac.jp"
88
+ ).should == "xn--0trv4xfvn8el34t.w3.mag.keio.ac.jp"
89
+ end
90
+
91
+ it "should convert '가각갂갃간갅갆갇갈갉힢힣.com' correctly" do
92
+ Addressable::IDNA.to_ascii(
93
+ "가각갂갃간갅갆갇갈갉힢힣.com"
94
+ ).should == "xn--o39acdefghijk5883jma.com"
95
+ end
96
+
97
+ it "should convert " +
98
+ "'\347\242\274\346\250\231\346\272\226\350" +
99
+ "\220\254\345\234\213\347\242\274.com' correctly" do
100
+ Addressable::IDNA.to_ascii(
101
+ "\347\242\274\346\250\231\346\272\226\350" +
102
+ "\220\254\345\234\213\347\242\274.com"
103
+ ).should == "xn--9cs565brid46mda086o.com"
104
+ end
105
+
106
+ it "should convert 'リ宠퐱〹.com' correctly" do
107
+ Addressable::IDNA.to_ascii(
108
+ "\357\276\230\345\256\240\355\220\261\343\200\271.com"
109
+ ).should == "xn--eek174hoxfpr4k.com"
110
+ end
111
+
112
+ it "should convert 'リ宠퐱卄.com' correctly" do
113
+ Addressable::IDNA.to_ascii(
114
+ "\343\203\252\345\256\240\355\220\261\345\215\204.com"
115
+ ).should == "xn--eek174hoxfpr4k.com"
116
+ end
117
+
118
+ it "should convert 'ᆵ' correctly" do
119
+ Addressable::IDNA.to_ascii(
120
+ "\341\206\265"
121
+ ).should == "xn--4ud"
122
+ end
123
+
124
+ it "should convert 'ᆵ' correctly" do
125
+ Addressable::IDNA.to_ascii(
126
+ "\357\276\257"
127
+ ).should == "xn--4ud"
128
+ end
129
+ end
130
+
131
+ shared_examples_for "converting from ASCII to unicode" do
132
+ it "should convert 'www.google.com' correctly" do
133
+ Addressable::IDNA.to_unicode("www.google.com").should == "www.google.com"
134
+ end
135
+
136
+ it "should convert 'www.詹姆斯.com' correctly" do
137
+ Addressable::IDNA.to_unicode(
138
+ "www.xn--8ws00zhy3a.com"
139
+ ).should == "www.詹姆斯.com"
140
+ end
141
+
142
+ it "should convert 'www.iñtërnâtiônàlizætiøn.com' correctly" do
143
+ Addressable::IDNA.to_unicode(
144
+ "www.xn--itrntinliztin-vdb0a5exd8ewcye.com"
145
+ ).should == "www.iñtërnâtiônàlizætiøn.com"
146
+ end
147
+
148
+ it "should convert " +
149
+ "'www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp' " +
150
+ "correctly" do
151
+ Addressable::IDNA.to_unicode(
152
+ "www.xn--n8jaaaaai5bhf7as8fsfk3jnknefdde3" +
153
+ "fg11amb5gzdb4wi9bya3kc6lra.w3.mag.keio.ac.jp"
154
+ ).should ==
155
+ "www.ほんとうにながいわけのわからないどめいんめいのらべるまだながくしないとたりない.w3.mag.keio.ac.jp"
156
+ end
157
+
158
+ it "should convert '点心和烤鸭.w3.mag.keio.ac.jp' correctly" do
159
+ Addressable::IDNA.to_unicode(
160
+ "xn--0trv4xfvn8el34t.w3.mag.keio.ac.jp"
161
+ ).should == "点心和烤鸭.w3.mag.keio.ac.jp"
162
+ end
163
+
164
+ it "should convert '가각갂갃간갅갆갇갈갉힢힣.com' correctly" do
165
+ Addressable::IDNA.to_unicode(
166
+ "xn--o39acdefghijk5883jma.com"
167
+ ).should == "가각갂갃간갅갆갇갈갉힢힣.com"
168
+ end
169
+
170
+ it "should convert " +
171
+ "'\347\242\274\346\250\231\346\272\226\350" +
172
+ "\220\254\345\234\213\347\242\274.com' correctly" do
173
+ Addressable::IDNA.to_unicode(
174
+ "xn--9cs565brid46mda086o.com"
175
+ ).should ==
176
+ "\347\242\274\346\250\231\346\272\226\350" +
177
+ "\220\254\345\234\213\347\242\274.com"
178
+ end
179
+
180
+ it "should convert 'リ宠퐱卄.com' correctly" do
181
+ Addressable::IDNA.to_unicode(
182
+ "xn--eek174hoxfpr4k.com"
183
+ ).should == "\343\203\252\345\256\240\355\220\261\345\215\204.com"
184
+ end
185
+
186
+ it "should convert 'ᆵ' correctly" do
187
+ Addressable::IDNA.to_unicode(
188
+ "xn--4ud"
189
+ ).should == "\341\206\265"
190
+ end
191
+
192
+ it "should normalize 'string' correctly" do
193
+ Addressable::IDNA.unicode_normalize_kc(:'string').should == "string"
194
+ Addressable::IDNA.unicode_normalize_kc("string").should == "string"
195
+ end
196
+ end
197
+
198
+ describe Addressable::IDNA, "when using the pure-Ruby implementation" do
199
+ before do
200
+ Addressable.send(:remove_const, :IDNA)
201
+ load "addressable/idna/pure.rb"
202
+ end
203
+
204
+ it_should_behave_like "converting from unicode to ASCII"
205
+ it_should_behave_like "converting from ASCII to unicode"
206
+
207
+ begin
208
+ require "fiber"
209
+
210
+ it "should not blow up inside fibers" do
211
+ f = Fiber.new do
212
+ Addressable.send(:remove_const, :IDNA)
213
+ load "addressable/idna/pure.rb"
214
+ end
215
+ f.resume
216
+ end
217
+ rescue LoadError
218
+ # Fibers aren't supported in this version of Ruby, skip this test.
219
+ warn('Fibers unsupported.')
220
+ end
221
+ end
222
+
223
+ begin
224
+ require "idn"
225
+
226
+ describe Addressable::IDNA, "when using the native-code implementation" do
227
+ before do
228
+ Addressable.send(:remove_const, :IDNA)
229
+ load "addressable/idna/native.rb"
230
+ end
231
+
232
+ it_should_behave_like "converting from unicode to ASCII"
233
+ it_should_behave_like "converting from ASCII to unicode"
234
+ end
235
+ rescue LoadError
236
+ # Cannot test the native implementation without libidn support.
237
+ warn('Could not load native IDN implementation.')
238
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ # Copyright (C) 2006-2013 Bob Aman
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+ require "spec_helper"
18
+
19
+ require "addressable/uri"
20
+ require "net/http"
21
+
22
+ describe Net::HTTP do
23
+ it "should be compatible with Addressable" do
24
+ response_body =
25
+ Net::HTTP.get(Addressable::URI.parse('http://www.google.com/'))
26
+ response_body.should_not be_nil
27
+ end
28
+ end
@@ -0,0 +1,1152 @@
1
+ # coding: utf-8
2
+ # Copyright (C) 2006-2013 Bob Aman
3
+ #
4
+ # Licensed under the Apache License, Version 2.0 (the "License");
5
+ # you may not use this file except in compliance with the License.
6
+ # You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+
16
+
17
+ require "spec_helper"
18
+
19
+ require "addressable/template"
20
+
21
+ shared_examples_for 'expands' do |tests|
22
+ tests.each do |template, expansion|
23
+ exp = expansion.is_a?(Array) ? expansion.first : expansion
24
+ it "#{template} to #{exp}" do
25
+ tmpl = Addressable::Template.new(template).expand(subject)
26
+ if expansion.is_a?(Array)
27
+ expansion.any?{|i| i == tmpl.to_str}.should be_true
28
+ else
29
+ tmpl.to_str.should == expansion
30
+ end
31
+ end
32
+ end
33
+ end
34
+
35
+ describe "Type conversion" do
36
+ subject{
37
+ {:var => true, :hello => 1234, :nothing => nil, :sym => :symbolic}
38
+ }
39
+ it_behaves_like 'expands', {
40
+ '{var}' => 'true',
41
+ '{hello}' => '1234',
42
+ '{nothing}' => '',
43
+ '{sym}' => 'symbolic'
44
+ }
45
+ end
46
+
47
+ describe "Level 1:" do
48
+ subject{
49
+ {:var => "value", :hello => "Hello World!"}
50
+ }
51
+ it_behaves_like 'expands', {
52
+ '{var}' => 'value',
53
+ '{hello}' => 'Hello%20World%21'
54
+ }
55
+ end
56
+
57
+ describe "Level 2" do
58
+ subject{
59
+ {
60
+ :var => "value",
61
+ :hello => "Hello World!",
62
+ :path => "/foo/bar"
63
+ }
64
+ }
65
+ context "Operator +:" do
66
+ it_behaves_like 'expands', {
67
+ '{+var}' => 'value',
68
+ '{+hello}' => 'Hello%20World!',
69
+ '{+path}/here' => '/foo/bar/here',
70
+ 'here?ref={+path}' => 'here?ref=/foo/bar'
71
+ }
72
+ end
73
+ context "Operator #:" do
74
+ it_behaves_like 'expands', {
75
+ 'X{#var}' => 'X#value',
76
+ 'X{#hello}' => 'X#Hello%20World!'
77
+ }
78
+ end
79
+ end
80
+
81
+ describe "Level 3" do
82
+ subject{
83
+ {
84
+ :var => "value",
85
+ :hello => "Hello World!",
86
+ :empty => "",
87
+ :path => "/foo/bar",
88
+ :x => "1024",
89
+ :y => "768"
90
+ }
91
+ }
92
+ context "Operator nil (multiple vars):" do
93
+ it_behaves_like 'expands', {
94
+ 'map?{x,y}' => 'map?1024,768',
95
+ '{x,hello,y}' => '1024,Hello%20World%21,768'
96
+ }
97
+ end
98
+ context "Operator + (multiple vars):" do
99
+ it_behaves_like 'expands', {
100
+ '{+x,hello,y}' => '1024,Hello%20World!,768',
101
+ '{+path,x}/here' => '/foo/bar,1024/here'
102
+ }
103
+ end
104
+ context "Operator # (multiple vars):" do
105
+ it_behaves_like 'expands', {
106
+ '{#x,hello,y}' => '#1024,Hello%20World!,768',
107
+ '{#path,x}/here' => '#/foo/bar,1024/here'
108
+ }
109
+ end
110
+ context "Operator ." do
111
+ it_behaves_like 'expands', {
112
+ 'X{.var}' => 'X.value',
113
+ 'X{.x,y}' => 'X.1024.768'
114
+ }
115
+ end
116
+ context "Operator /" do
117
+ it_behaves_like 'expands', {
118
+ '{/var}' => '/value',
119
+ '{/var,x}/here' => '/value/1024/here'
120
+ }
121
+ end
122
+ context "Operator ;" do
123
+ it_behaves_like 'expands', {
124
+ '{;x,y}' => ';x=1024;y=768',
125
+ '{;x,y,empty}' => ';x=1024;y=768;empty'
126
+ }
127
+ end
128
+ context "Operator ?" do
129
+ it_behaves_like 'expands', {
130
+ '{?x,y}' => '?x=1024&y=768',
131
+ '{?x,y,empty}' => '?x=1024&y=768&empty='
132
+ }
133
+ end
134
+ context "Operator &" do
135
+ it_behaves_like 'expands', {
136
+ '?fixed=yes{&x}' => '?fixed=yes&x=1024',
137
+ '{&x,y,empty}' => '&x=1024&y=768&empty='
138
+ }
139
+ end
140
+ end
141
+
142
+ describe "Level 4" do
143
+ subject{
144
+ {
145
+ :var => "value",
146
+ :hello => "Hello World!",
147
+ :path => "/foo/bar",
148
+ :semi => ";",
149
+ :list => %w(red green blue),
150
+ :keys => {"semi" => ';', "dot" => '.', "comma" => ','}
151
+ }
152
+ }
153
+ context "Expansion with value modifiers" do
154
+ it_behaves_like 'expands', {
155
+ '{var:3}' => 'val',
156
+ '{var:30}' => 'value',
157
+ '{list}' => 'red,green,blue',
158
+ '{list*}' => 'red,green,blue',
159
+ '{keys}' => [
160
+ 'semi,%3B,dot,.,comma,%2C',
161
+ 'dot,.,semi,%3B,comma,%2C',
162
+ 'comma,%2C,semi,%3B,dot,.',
163
+ 'semi,%3B,comma,%2C,dot,.',
164
+ 'dot,.,comma,%2C,semi,%3B',
165
+ 'comma,%2C,dot,.,semi,%3B'
166
+ ],
167
+ '{keys*}' => [
168
+ 'semi=%3B,dot=.,comma=%2C',
169
+ 'dot=.,semi=%3B,comma=%2C',
170
+ 'comma=%2C,semi=%3B,dot=.',
171
+ 'semi=%3B,comma=%2C,dot=.',
172
+ 'dot=.,comma=%2C,semi=%3B',
173
+ 'comma=%2C,dot=.,semi=%3B'
174
+ ]
175
+ }
176
+ end
177
+ context "Operator + with value modifiers" do
178
+ it_behaves_like 'expands', {
179
+ '{+path:6}/here' => '/foo/b/here',
180
+ '{+list}' => 'red,green,blue',
181
+ '{+list*}' => 'red,green,blue',
182
+ '{+keys}' => [
183
+ 'semi,;,dot,.,comma,,',
184
+ 'dot,.,semi,;,comma,,',
185
+ 'comma,,,semi,;,dot,.',
186
+ 'semi,;,comma,,,dot,.',
187
+ 'dot,.,comma,,,semi,;',
188
+ 'comma,,,dot,.,semi,;'
189
+ ],
190
+ '{+keys*}' => [
191
+ 'semi=;,dot=.,comma=,',
192
+ 'dot=.,semi=;,comma=,',
193
+ 'comma=,,semi=;,dot=.',
194
+ 'semi=;,comma=,,dot=.',
195
+ 'dot=.,comma=,,semi=;',
196
+ 'comma=,,dot=.,semi=;'
197
+ ]
198
+ }
199
+ end
200
+ context "Operator # with value modifiers" do
201
+ it_behaves_like 'expands', {
202
+ '{#path:6}/here' => '#/foo/b/here',
203
+ '{#list}' => '#red,green,blue',
204
+ '{#list*}' => '#red,green,blue',
205
+ '{#keys}' => [
206
+ '#semi,;,dot,.,comma,,',
207
+ '#dot,.,semi,;,comma,,',
208
+ '#comma,,,semi,;,dot,.',
209
+ '#semi,;,comma,,,dot,.',
210
+ '#dot,.,comma,,,semi,;',
211
+ '#comma,,,dot,.,semi,;'
212
+ ],
213
+ '{#keys*}' => [
214
+ '#semi=;,dot=.,comma=,',
215
+ '#dot=.,semi=;,comma=,',
216
+ '#comma=,,semi=;,dot=.',
217
+ '#semi=;,comma=,,dot=.',
218
+ '#dot=.,comma=,,semi=;',
219
+ '#comma=,,dot=.,semi=;'
220
+ ]
221
+ }
222
+ end
223
+ context "Operator . with value modifiers" do
224
+ it_behaves_like 'expands', {
225
+ 'X{.var:3}' => 'X.val',
226
+ 'X{.list}' => 'X.red,green,blue',
227
+ 'X{.list*}' => 'X.red.green.blue',
228
+ 'X{.keys}' => [
229
+ 'X.semi,%3B,dot,.,comma,%2C',
230
+ 'X.dot,.,semi,%3B,comma,%2C',
231
+ 'X.comma,%2C,semi,%3B,dot,.',
232
+ 'X.semi,%3B,comma,%2C,dot,.',
233
+ 'X.dot,.,comma,%2C,semi,%3B',
234
+ 'X.comma,%2C,dot,.,semi,%3B'
235
+ ],
236
+ 'X{.keys*}' => [
237
+ 'X.semi=%3B.dot=..comma=%2C',
238
+ 'X.dot=..semi=%3B.comma=%2C',
239
+ 'X.comma=%2C.semi=%3B.dot=.',
240
+ 'X.semi=%3B.comma=%2C.dot=.',
241
+ 'X.dot=..comma=%2C.semi=%3B',
242
+ 'X.comma=%2C.dot=..semi=%3B'
243
+ ]
244
+ }
245
+ end
246
+ context "Operator / with value modifiers" do
247
+ it_behaves_like 'expands', {
248
+ '{/var:1,var}' => '/v/value',
249
+ '{/list}' => '/red,green,blue',
250
+ '{/list*}' => '/red/green/blue',
251
+ '{/list*,path:4}' => '/red/green/blue/%2Ffoo',
252
+ '{/keys}' => [
253
+ '/semi,%3B,dot,.,comma,%2C',
254
+ '/dot,.,semi,%3B,comma,%2C',
255
+ '/comma,%2C,semi,%3B,dot,.',
256
+ '/semi,%3B,comma,%2C,dot,.',
257
+ '/dot,.,comma,%2C,semi,%3B',
258
+ '/comma,%2C,dot,.,semi,%3B'
259
+ ],
260
+ '{/keys*}' => [
261
+ '/semi=%3B/dot=./comma=%2C',
262
+ '/dot=./semi=%3B/comma=%2C',
263
+ '/comma=%2C/semi=%3B/dot=.',
264
+ '/semi=%3B/comma=%2C/dot=.',
265
+ '/dot=./comma=%2C/semi=%3B',
266
+ '/comma=%2C/dot=./semi=%3B'
267
+ ]
268
+ }
269
+ end
270
+ context "Operator ; with value modifiers" do
271
+ it_behaves_like 'expands', {
272
+ '{;hello:5}' => ';hello=Hello',
273
+ '{;list}' => ';list=red,green,blue',
274
+ '{;list*}' => ';list=red;list=green;list=blue',
275
+ '{;keys}' => [
276
+ ';keys=semi,%3B,dot,.,comma,%2C',
277
+ ';keys=dot,.,semi,%3B,comma,%2C',
278
+ ';keys=comma,%2C,semi,%3B,dot,.',
279
+ ';keys=semi,%3B,comma,%2C,dot,.',
280
+ ';keys=dot,.,comma,%2C,semi,%3B',
281
+ ';keys=comma,%2C,dot,.,semi,%3B'
282
+ ],
283
+ '{;keys*}' => [
284
+ ';semi=%3B;dot=.;comma=%2C',
285
+ ';dot=.;semi=%3B;comma=%2C',
286
+ ';comma=%2C;semi=%3B;dot=.',
287
+ ';semi=%3B;comma=%2C;dot=.',
288
+ ';dot=.;comma=%2C;semi=%3B',
289
+ ';comma=%2C;dot=.;semi=%3B'
290
+ ]
291
+ }
292
+ end
293
+ context "Operator ? with value modifiers" do
294
+ it_behaves_like 'expands', {
295
+ '{?var:3}' => '?var=val',
296
+ '{?list}' => '?list=red,green,blue',
297
+ '{?list*}' => '?list=red&list=green&list=blue',
298
+ '{?keys}' => [
299
+ '?keys=semi,%3B,dot,.,comma,%2C',
300
+ '?keys=dot,.,semi,%3B,comma,%2C',
301
+ '?keys=comma,%2C,semi,%3B,dot,.',
302
+ '?keys=semi,%3B,comma,%2C,dot,.',
303
+ '?keys=dot,.,comma,%2C,semi,%3B',
304
+ '?keys=comma,%2C,dot,.,semi,%3B'
305
+ ],
306
+ '{?keys*}' => [
307
+ '?semi=%3B&dot=.&comma=%2C',
308
+ '?dot=.&semi=%3B&comma=%2C',
309
+ '?comma=%2C&semi=%3B&dot=.',
310
+ '?semi=%3B&comma=%2C&dot=.',
311
+ '?dot=.&comma=%2C&semi=%3B',
312
+ '?comma=%2C&dot=.&semi=%3B'
313
+ ]
314
+ }
315
+ end
316
+ context "Operator & with value modifiers" do
317
+ it_behaves_like 'expands', {
318
+ '{&var:3}' => '&var=val',
319
+ '{&list}' => '&list=red,green,blue',
320
+ '{&list*}' => '&list=red&list=green&list=blue',
321
+ '{&keys}' => [
322
+ '&keys=semi,%3B,dot,.,comma,%2C',
323
+ '&keys=dot,.,semi,%3B,comma,%2C',
324
+ '&keys=comma,%2C,semi,%3B,dot,.',
325
+ '&keys=semi,%3B,comma,%2C,dot,.',
326
+ '&keys=dot,.,comma,%2C,semi,%3B',
327
+ '&keys=comma,%2C,dot,.,semi,%3B'
328
+ ],
329
+ '{&keys*}' => [
330
+ '&semi=%3B&dot=.&comma=%2C',
331
+ '&dot=.&semi=%3B&comma=%2C',
332
+ '&comma=%2C&semi=%3B&dot=.',
333
+ '&semi=%3B&comma=%2C&dot=.',
334
+ '&dot=.&comma=%2C&semi=%3B',
335
+ '&comma=%2C&dot=.&semi=%3B'
336
+ ]
337
+ }
338
+ end
339
+ end
340
+ describe "Modifiers" do
341
+ subject{
342
+ {
343
+ :var => "value",
344
+ :semi => ";",
345
+ :year => %w(1965 2000 2012),
346
+ :dom => %w(example com)
347
+ }
348
+ }
349
+ context "length" do
350
+ it_behaves_like 'expands', {
351
+ '{var:3}' => 'val',
352
+ '{var:30}' => 'value',
353
+ '{var}' => 'value',
354
+ '{semi}' => '%3B',
355
+ '{semi:2}' => '%3B'
356
+ }
357
+ end
358
+ context "explode" do
359
+ it_behaves_like 'expands', {
360
+ 'find{?year*}' => 'find?year=1965&year=2000&year=2012',
361
+ 'www{.dom*}' => 'www.example.com',
362
+ }
363
+ end
364
+ end
365
+ describe "Expansion" do
366
+ subject{
367
+ {
368
+ :count => ["one", "two", "three"],
369
+ :dom => ["example", "com"],
370
+ :dub => "me/too",
371
+ :hello => "Hello World!",
372
+ :half => "50%",
373
+ :var => "value",
374
+ :who => "fred",
375
+ :base => "http://example.com/home/",
376
+ :path => "/foo/bar",
377
+ :list => ["red", "green", "blue"],
378
+ :keys => {"semi" => ";","dot" => ".","comma" => ","},
379
+ :v => "6",
380
+ :x => "1024",
381
+ :y => "768",
382
+ :empty => "",
383
+ :empty_keys => {},
384
+ :undef => nil
385
+ }
386
+ }
387
+ context "concatenation" do
388
+ it_behaves_like 'expands', {
389
+ '{count}' => 'one,two,three',
390
+ '{count*}' => 'one,two,three',
391
+ '{/count}' => '/one,two,three',
392
+ '{/count*}' => '/one/two/three',
393
+ '{;count}' => ';count=one,two,three',
394
+ '{;count*}' => ';count=one;count=two;count=three',
395
+ '{?count}' => '?count=one,two,three',
396
+ '{?count*}' => '?count=one&count=two&count=three',
397
+ '{&count*}' => '&count=one&count=two&count=three'
398
+ }
399
+ end
400
+ context "simple expansion" do
401
+ it_behaves_like 'expands', {
402
+ '{var}' => 'value',
403
+ '{hello}' => 'Hello%20World%21',
404
+ '{half}' => '50%25',
405
+ 'O{empty}X' => 'OX',
406
+ 'O{undef}X' => 'OX',
407
+ '{x,y}' => '1024,768',
408
+ '{x,hello,y}' => '1024,Hello%20World%21,768',
409
+ '?{x,empty}' => '?1024,',
410
+ '?{x,undef}' => '?1024',
411
+ '?{undef,y}' => '?768',
412
+ '{var:3}' => 'val',
413
+ '{var:30}' => 'value',
414
+ '{list}' => 'red,green,blue',
415
+ '{list*}' => 'red,green,blue',
416
+ '{keys}' => [
417
+ 'semi,%3B,dot,.,comma,%2C',
418
+ 'dot,.,semi,%3B,comma,%2C',
419
+ 'comma,%2C,semi,%3B,dot,.',
420
+ 'semi,%3B,comma,%2C,dot,.',
421
+ 'dot,.,comma,%2C,semi,%3B',
422
+ 'comma,%2C,dot,.,semi,%3B'
423
+ ],
424
+ '{keys*}' => [
425
+ 'semi=%3B,dot=.,comma=%2C',
426
+ 'dot=.,semi=%3B,comma=%2C',
427
+ 'comma=%2C,semi=%3B,dot=.',
428
+ 'semi=%3B,comma=%2C,dot=.',
429
+ 'dot=.,comma=%2C,semi=%3B',
430
+ 'comma=%2C,dot=.,semi=%3B'
431
+ ]
432
+ }
433
+ end
434
+ context "reserved expansion (+)" do
435
+ it_behaves_like 'expands', {
436
+ '{+var}' => 'value',
437
+ '{+hello}' => 'Hello%20World!',
438
+ '{+half}' => '50%25',
439
+ '{base}index' => 'http%3A%2F%2Fexample.com%2Fhome%2Findex',
440
+ '{+base}index' => 'http://example.com/home/index',
441
+ 'O{+empty}X' => 'OX',
442
+ 'O{+undef}X' => 'OX',
443
+ '{+path}/here' => '/foo/bar/here',
444
+ 'here?ref={+path}' => 'here?ref=/foo/bar',
445
+ 'up{+path}{var}/here' => 'up/foo/barvalue/here',
446
+ '{+x,hello,y}' => '1024,Hello%20World!,768',
447
+ '{+path,x}/here' => '/foo/bar,1024/here',
448
+ '{+path:6}/here' => '/foo/b/here',
449
+ '{+list}' => 'red,green,blue',
450
+ '{+list*}' => 'red,green,blue',
451
+ '{+keys}' => [
452
+ 'semi,;,dot,.,comma,,',
453
+ 'dot,.,semi,;,comma,,',
454
+ 'comma,,,semi,;,dot,.',
455
+ 'semi,;,comma,,,dot,.',
456
+ 'dot,.,comma,,,semi,;',
457
+ 'comma,,,dot,.,semi,;'
458
+ ],
459
+ '{+keys*}' => [
460
+ 'semi=;,dot=.,comma=,',
461
+ 'dot=.,semi=;,comma=,',
462
+ 'comma=,,semi=;,dot=.',
463
+ 'semi=;,comma=,,dot=.',
464
+ 'dot=.,comma=,,semi=;',
465
+ 'comma=,,dot=.,semi=;'
466
+ ]
467
+ }
468
+ end
469
+ context "fragment expansion (#)" do
470
+ it_behaves_like 'expands', {
471
+ '{#var}' => '#value',
472
+ '{#hello}' => '#Hello%20World!',
473
+ '{#half}' => '#50%25',
474
+ 'foo{#empty}' => 'foo#',
475
+ 'foo{#undef}' => 'foo',
476
+ '{#x,hello,y}' => '#1024,Hello%20World!,768',
477
+ '{#path,x}/here' => '#/foo/bar,1024/here',
478
+ '{#path:6}/here' => '#/foo/b/here',
479
+ '{#list}' => '#red,green,blue',
480
+ '{#list*}' => '#red,green,blue',
481
+ '{#keys}' => [
482
+ '#semi,;,dot,.,comma,,',
483
+ '#dot,.,semi,;,comma,,',
484
+ '#comma,,,semi,;,dot,.',
485
+ '#semi,;,comma,,,dot,.',
486
+ '#dot,.,comma,,,semi,;',
487
+ '#comma,,,dot,.,semi,;'
488
+ ],
489
+ '{#keys*}' => [
490
+ '#semi=;,dot=.,comma=,',
491
+ '#dot=.,semi=;,comma=,',
492
+ '#comma=,,semi=;,dot=.',
493
+ '#semi=;,comma=,,dot=.',
494
+ '#dot=.,comma=,,semi=;',
495
+ '#comma=,,dot=.,semi=;'
496
+ ]
497
+ }
498
+ end
499
+ context "label expansion (.)" do
500
+ it_behaves_like 'expands', {
501
+ '{.who}' => '.fred',
502
+ '{.who,who}' => '.fred.fred',
503
+ '{.half,who}' => '.50%25.fred',
504
+ 'www{.dom*}' => 'www.example.com',
505
+ 'X{.var}' => 'X.value',
506
+ 'X{.empty}' => 'X.',
507
+ 'X{.undef}' => 'X',
508
+ 'X{.var:3}' => 'X.val',
509
+ 'X{.list}' => 'X.red,green,blue',
510
+ 'X{.list*}' => 'X.red.green.blue',
511
+ 'X{.keys}' => [
512
+ 'X.semi,%3B,dot,.,comma,%2C',
513
+ 'X.dot,.,semi,%3B,comma,%2C',
514
+ 'X.comma,%2C,semi,%3B,dot,.',
515
+ 'X.semi,%3B,comma,%2C,dot,.',
516
+ 'X.dot,.,comma,%2C,semi,%3B',
517
+ 'X.comma,%2C,dot,.,semi,%3B'
518
+ ],
519
+ 'X{.keys*}' => [
520
+ 'X.semi=%3B.dot=..comma=%2C',
521
+ 'X.dot=..semi=%3B.comma=%2C',
522
+ 'X.comma=%2C.semi=%3B.dot=.',
523
+ 'X.semi=%3B.comma=%2C.dot=.',
524
+ 'X.dot=..comma=%2C.semi=%3B',
525
+ 'X.comma=%2C.dot=..semi=%3B'
526
+ ],
527
+ 'X{.empty_keys}' => 'X',
528
+ 'X{.empty_keys*}' => 'X'
529
+ }
530
+ end
531
+ context "path expansion (/)" do
532
+ it_behaves_like 'expands', {
533
+ '{/who}' => '/fred',
534
+ '{/who,who}' => '/fred/fred',
535
+ '{/half,who}' => '/50%25/fred',
536
+ '{/who,dub}' => '/fred/me%2Ftoo',
537
+ '{/var}' => '/value',
538
+ '{/var,empty}' => '/value/',
539
+ '{/var,undef}' => '/value',
540
+ '{/var,x}/here' => '/value/1024/here',
541
+ '{/var:1,var}' => '/v/value',
542
+ '{/list}' => '/red,green,blue',
543
+ '{/list*}' => '/red/green/blue',
544
+ '{/list*,path:4}' => '/red/green/blue/%2Ffoo',
545
+ '{/keys}' => [
546
+ '/semi,%3B,dot,.,comma,%2C',
547
+ '/dot,.,semi,%3B,comma,%2C',
548
+ '/comma,%2C,semi,%3B,dot,.',
549
+ '/semi,%3B,comma,%2C,dot,.',
550
+ '/dot,.,comma,%2C,semi,%3B',
551
+ '/comma,%2C,dot,.,semi,%3B'
552
+ ],
553
+ '{/keys*}' => [
554
+ '/semi=%3B/dot=./comma=%2C',
555
+ '/dot=./semi=%3B/comma=%2C',
556
+ '/comma=%2C/semi=%3B/dot=.',
557
+ '/semi=%3B/comma=%2C/dot=.',
558
+ '/dot=./comma=%2C/semi=%3B',
559
+ '/comma=%2C/dot=./semi=%3B'
560
+ ]
561
+ }
562
+ end
563
+ context "path-style expansion (;)" do
564
+ it_behaves_like 'expands', {
565
+ '{;who}' => ';who=fred',
566
+ '{;half}' => ';half=50%25',
567
+ '{;empty}' => ';empty',
568
+ '{;v,empty,who}' => ';v=6;empty;who=fred',
569
+ '{;v,bar,who}' => ';v=6;who=fred',
570
+ '{;x,y}' => ';x=1024;y=768',
571
+ '{;x,y,empty}' => ';x=1024;y=768;empty',
572
+ '{;x,y,undef}' => ';x=1024;y=768',
573
+ '{;hello:5}' => ';hello=Hello',
574
+ '{;list}' => ';list=red,green,blue',
575
+ '{;list*}' => ';list=red;list=green;list=blue',
576
+ '{;keys}' => [
577
+ ';keys=semi,%3B,dot,.,comma,%2C',
578
+ ';keys=dot,.,semi,%3B,comma,%2C',
579
+ ';keys=comma,%2C,semi,%3B,dot,.',
580
+ ';keys=semi,%3B,comma,%2C,dot,.',
581
+ ';keys=dot,.,comma,%2C,semi,%3B',
582
+ ';keys=comma,%2C,dot,.,semi,%3B'
583
+ ],
584
+ '{;keys*}' => [
585
+ ';semi=%3B;dot=.;comma=%2C',
586
+ ';dot=.;semi=%3B;comma=%2C',
587
+ ';comma=%2C;semi=%3B;dot=.',
588
+ ';semi=%3B;comma=%2C;dot=.',
589
+ ';dot=.;comma=%2C;semi=%3B',
590
+ ';comma=%2C;dot=.;semi=%3B'
591
+ ]
592
+ }
593
+ end
594
+ context "form query expansion (?)" do
595
+ it_behaves_like 'expands', {
596
+ '{?who}' => '?who=fred',
597
+ '{?half}' => '?half=50%25',
598
+ '{?x,y}' => '?x=1024&y=768',
599
+ '{?x,y,empty}' => '?x=1024&y=768&empty=',
600
+ '{?x,y,undef}' => '?x=1024&y=768',
601
+ '{?var:3}' => '?var=val',
602
+ '{?list}' => '?list=red,green,blue',
603
+ '{?list*}' => '?list=red&list=green&list=blue',
604
+ '{?keys}' => [
605
+ '?keys=semi,%3B,dot,.,comma,%2C',
606
+ '?keys=dot,.,semi,%3B,comma,%2C',
607
+ '?keys=comma,%2C,semi,%3B,dot,.',
608
+ '?keys=semi,%3B,comma,%2C,dot,.',
609
+ '?keys=dot,.,comma,%2C,semi,%3B',
610
+ '?keys=comma,%2C,dot,.,semi,%3B'
611
+ ],
612
+ '{?keys*}' => [
613
+ '?semi=%3B&dot=.&comma=%2C',
614
+ '?dot=.&semi=%3B&comma=%2C',
615
+ '?comma=%2C&semi=%3B&dot=.',
616
+ '?semi=%3B&comma=%2C&dot=.',
617
+ '?dot=.&comma=%2C&semi=%3B',
618
+ '?comma=%2C&dot=.&semi=%3B'
619
+ ]
620
+ }
621
+ end
622
+ context "form query expansion (&)" do
623
+ it_behaves_like 'expands', {
624
+ '{&who}' => '&who=fred',
625
+ '{&half}' => '&half=50%25',
626
+ '?fixed=yes{&x}' => '?fixed=yes&x=1024',
627
+ '{&x,y,empty}' => '&x=1024&y=768&empty=',
628
+ '{&x,y,undef}' => '&x=1024&y=768',
629
+ '{&var:3}' => '&var=val',
630
+ '{&list}' => '&list=red,green,blue',
631
+ '{&list*}' => '&list=red&list=green&list=blue',
632
+ '{&keys}' => [
633
+ '&keys=semi,%3B,dot,.,comma,%2C',
634
+ '&keys=dot,.,semi,%3B,comma,%2C',
635
+ '&keys=comma,%2C,semi,%3B,dot,.',
636
+ '&keys=semi,%3B,comma,%2C,dot,.',
637
+ '&keys=dot,.,comma,%2C,semi,%3B',
638
+ '&keys=comma,%2C,dot,.,semi,%3B'
639
+ ],
640
+ '{&keys*}' => [
641
+ '&semi=%3B&dot=.&comma=%2C',
642
+ '&dot=.&semi=%3B&comma=%2C',
643
+ '&comma=%2C&semi=%3B&dot=.',
644
+ '&semi=%3B&comma=%2C&dot=.',
645
+ '&dot=.&comma=%2C&semi=%3B',
646
+ '&comma=%2C&dot=.&semi=%3B'
647
+ ]
648
+ }
649
+ end
650
+ end
651
+
652
+ class ExampleTwoProcessor
653
+ def self.restore(name, value)
654
+ return value.gsub(/-/, " ") if name == "query"
655
+ return value
656
+ end
657
+
658
+ def self.match(name)
659
+ return ".*?" if name == "first"
660
+ return ".*"
661
+ end
662
+ def self.validate(name, value)
663
+ return !!(value =~ /^[\w ]+$/) if name == "query"
664
+ return true
665
+ end
666
+
667
+ def self.transform(name, value)
668
+ return value.gsub(/ /, "+") if name == "query"
669
+ return value
670
+ end
671
+ end
672
+
673
+
674
+ describe Addressable::Template do
675
+ describe "Matching" do
676
+ let(:uri){
677
+ Addressable::URI.parse(
678
+ "http://example.com/search/an-example-search-query/"
679
+ )
680
+ }
681
+ let(:uri2){
682
+ Addressable::URI.parse("http://example.com/a/b/c/")
683
+ }
684
+ let(:uri3){
685
+ Addressable::URI.parse("http://example.com/;a=1;b=2;c=3;first=foo")
686
+ }
687
+ let(:uri4){
688
+ Addressable::URI.parse("http://example.com/?a=1&b=2&c=3&first=foo")
689
+ }
690
+ context "first uri with ExampleTwoProcessor" do
691
+ subject{
692
+ match = Addressable::Template.new(
693
+ "http://example.com/search/{query}/"
694
+ ).match(uri, ExampleTwoProcessor)
695
+ }
696
+ its(:variables){ should == ["query"]}
697
+ its(:captures){ should == ["an example search query"]}
698
+ end
699
+
700
+ context "second uri with ExampleTwoProcessor" do
701
+ subject{
702
+ match = Addressable::Template.new(
703
+ "http://example.com/{first}/{+second}/"
704
+ ).match(uri2, ExampleTwoProcessor)
705
+ }
706
+ its(:variables){ should == ["first", "second"]}
707
+ its(:captures){ should == ["a", "b/c"] }
708
+ end
709
+ context "second uri" do
710
+ subject{
711
+ match = Addressable::Template.new(
712
+ "http://example.com/{first}{/second*}/"
713
+ ).match(uri2)
714
+ }
715
+ its(:variables){ should == ["first", "second"]}
716
+ its(:captures){ should == ["a", ["b","c"]] }
717
+ end
718
+ context "third uri" do
719
+ subject{
720
+ match = Addressable::Template.new(
721
+ "http://example.com/{;hash*,first}"
722
+ ).match(uri3)
723
+ }
724
+ its(:variables){ should == ["hash", "first"]}
725
+ its(:captures){ should == [
726
+ {"a" => "1", "b" => "2", "c" => "3", "first" => "foo"}, nil] }
727
+ end
728
+ context "fourth uri" do
729
+ subject{
730
+ match = Addressable::Template.new(
731
+ "http://example.com/{?hash*,first}"
732
+ ).match(uri4)
733
+ }
734
+ its(:variables){ should == ["hash", "first"]}
735
+ its(:captures){ should == [
736
+ {"a" => "1", "b" => "2", "c" => "3", "first"=> "foo"}, nil] }
737
+ end
738
+ end
739
+ describe "extract" do
740
+ let(:template) {
741
+ Addressable::Template.new(
742
+ "http://{host}{/segments*}/{?one,two,bogus}{#fragment}"
743
+ )
744
+ }
745
+ let(:uri){ "http://example.com/a/b/c/?one=1&two=2#foo" }
746
+ it "should be able to extract" do
747
+ template.extract(uri).should == {
748
+ "host" => "example.com",
749
+ "segments" => %w(a b c),
750
+ "one" => "1",
751
+ "bogus" => nil,
752
+ "two" => "2",
753
+ "fragment" => "foo"
754
+ }
755
+ end
756
+ end
757
+ describe "Partial expand with symbols" do
758
+ context "partial_expand with two simple values" do
759
+ subject{
760
+ Addressable::Template.new("http://example.com/{one}/{two}/")
761
+ }
762
+ it "builds a new pattern" do
763
+ subject.partial_expand(:one => "1").pattern.should ==
764
+ "http://example.com/1/{two}/"
765
+ end
766
+ end
767
+ context "partial_expand query with missing param in middle" do
768
+ subject{
769
+ Addressable::Template.new("http://example.com/{?one,two,three}/")
770
+ }
771
+ it "builds a new pattern" do
772
+ subject.partial_expand(:one => "1", :three => "3").pattern.should ==
773
+ "http://example.com/?one=1{&two}&three=3/"
774
+ end
775
+ end
776
+ context "partial_expand with query string" do
777
+ subject{
778
+ Addressable::Template.new("http://example.com/{?two,one}/")
779
+ }
780
+ it "builds a new pattern" do
781
+ subject.partial_expand(:one => "1").pattern.should ==
782
+ "http://example.com/{?two}&one=1/"
783
+ end
784
+ end
785
+ context "partial_expand with path operator" do
786
+ subject{
787
+ Addressable::Template.new("http://example.com{/one,two}/")
788
+ }
789
+ it "builds a new pattern" do
790
+ subject.partial_expand(:one => "1").pattern.should ==
791
+ "http://example.com/1{/two}/"
792
+ end
793
+ end
794
+ end
795
+ describe "Partial expand with strings" do
796
+ context "partial_expand with two simple values" do
797
+ subject{
798
+ Addressable::Template.new("http://example.com/{one}/{two}/")
799
+ }
800
+ it "builds a new pattern" do
801
+ subject.partial_expand("one" => "1").pattern.should ==
802
+ "http://example.com/1/{two}/"
803
+ end
804
+ end
805
+ context "partial_expand query with missing param in middle" do
806
+ subject{
807
+ Addressable::Template.new("http://example.com/{?one,two,three}/")
808
+ }
809
+ it "builds a new pattern" do
810
+ subject.partial_expand("one" => "1", "three" => "3").pattern.should ==
811
+ "http://example.com/?one=1{&two}&three=3/"
812
+ end
813
+ end
814
+ context "partial_expand with query string" do
815
+ subject{
816
+ Addressable::Template.new("http://example.com/{?two,one}/")
817
+ }
818
+ it "builds a new pattern" do
819
+ subject.partial_expand("one" => "1").pattern.should ==
820
+ "http://example.com/{?two}&one=1/"
821
+ end
822
+ end
823
+ context "partial_expand with path operator" do
824
+ subject{
825
+ Addressable::Template.new("http://example.com{/one,two}/")
826
+ }
827
+ it "builds a new pattern" do
828
+ subject.partial_expand("one" => "1").pattern.should ==
829
+ "http://example.com/1{/two}/"
830
+ end
831
+ end
832
+ end
833
+ describe "Expand" do
834
+ context "expand with a processor" do
835
+ subject{
836
+ Addressable::Template.new("http://example.com/search/{query}/")
837
+ }
838
+ it "processes spaces" do
839
+ subject.expand({"query" => "an example search query"},
840
+ ExampleTwoProcessor).to_str.should ==
841
+ "http://example.com/search/an+example+search+query/"
842
+ end
843
+ it "validates" do
844
+ lambda{
845
+ subject.expand({"query" => "Bogus!"},
846
+ ExampleTwoProcessor).to_str
847
+ }.should raise_error(Addressable::Template::InvalidTemplateValueError)
848
+ end
849
+ end
850
+ context "partial_expand query with missing param in middle" do
851
+ subject{
852
+ Addressable::Template.new("http://example.com/{?one,two,three}/")
853
+ }
854
+ it "builds a new pattern" do
855
+ subject.partial_expand("one" => "1", "three" => "3").pattern.should ==
856
+ "http://example.com/?one=1{&two}&three=3/"
857
+ end
858
+ end
859
+ context "partial_expand with query string" do
860
+ subject{
861
+ Addressable::Template.new("http://example.com/{?two,one}/")
862
+ }
863
+ it "builds a new pattern" do
864
+ subject.partial_expand("one" => "1").pattern.should ==
865
+ "http://example.com/{?two}&one=1/"
866
+ end
867
+ end
868
+ context "partial_expand with path operator" do
869
+ subject{
870
+ Addressable::Template.new("http://example.com{/one,two}/")
871
+ }
872
+ it "builds a new pattern" do
873
+ subject.partial_expand("one" => "1").pattern.should ==
874
+ "http://example.com/1{/two}/"
875
+ end
876
+ end
877
+ end
878
+ context "Matching with operators" do
879
+ describe "Level 1:" do
880
+ subject { Addressable::Template.new("foo{foo}/{bar}baz") }
881
+ it "can match" do
882
+ data = subject.match("foofoo/bananabaz")
883
+ data.mapping["foo"].should == "foo"
884
+ data.mapping["bar"].should == "banana"
885
+ end
886
+ it "can fail" do
887
+ subject.match("bar/foo").should be_nil
888
+ subject.match("foobaz").should be_nil
889
+ end
890
+ it "can match empty" do
891
+ data = subject.match("foo/baz")
892
+ data.mapping["foo"].should == ""
893
+ data.mapping["bar"].should == ""
894
+ end
895
+ it "lists vars" do
896
+ subject.variables.should == ["foo", "bar"]
897
+ end
898
+ end
899
+
900
+ describe "Level 2:" do
901
+ subject { Addressable::Template.new("foo{+foo}{#bar}baz") }
902
+ it "can match" do
903
+ data = subject.match("foo/test/banana#bazbaz")
904
+ data.mapping["foo"].should == "/test/banana"
905
+ data.mapping["bar"].should == "baz"
906
+ end
907
+ it "lists vars" do
908
+ subject.variables.should == ["foo", "bar"]
909
+ end
910
+ end
911
+
912
+ describe "Level 3:" do
913
+ context "no operator" do
914
+ subject { Addressable::Template.new("foo{foo,bar}baz") }
915
+ it "can match" do
916
+ data = subject.match("foofoo,barbaz")
917
+ data.mapping["foo"].should == "foo"
918
+ data.mapping["bar"].should == "bar"
919
+ end
920
+ it "lists vars" do
921
+ subject.variables.should == ["foo", "bar"]
922
+ end
923
+ end
924
+ context "+ operator" do
925
+ subject { Addressable::Template.new("foo{+foo,bar}baz") }
926
+ it "can match" do
927
+ data = subject.match("foofoo/bar,barbaz")
928
+ data.mapping["bar"].should == "foo/bar,bar"
929
+ data.mapping["foo"].should == ""
930
+ end
931
+ it "lists vars" do
932
+ subject.variables.should == ["foo", "bar"]
933
+ end
934
+ end
935
+ context ". operator" do
936
+ subject { Addressable::Template.new("foo{.foo,bar}baz") }
937
+ it "can match" do
938
+ data = subject.match("foo.foo.barbaz")
939
+ data.mapping["foo"].should == "foo"
940
+ data.mapping["bar"].should == "bar"
941
+ end
942
+ it "lists vars" do
943
+ subject.variables.should == ["foo", "bar"]
944
+ end
945
+ end
946
+ context "/ operator" do
947
+ subject { Addressable::Template.new("foo{/foo,bar}baz") }
948
+ it "can match" do
949
+ data = subject.match("foo/foo/barbaz")
950
+ data.mapping["foo"].should == "foo"
951
+ data.mapping["bar"].should == "bar"
952
+ end
953
+ it "lists vars" do
954
+ subject.variables.should == ["foo", "bar"]
955
+ end
956
+ end
957
+ context "; operator" do
958
+ subject { Addressable::Template.new("foo{;foo,bar,baz}baz") }
959
+ it "can match" do
960
+ data = subject.match("foo;foo=bar%20baz;bar=foo;bazbaz")
961
+ data.mapping["foo"].should == "bar baz"
962
+ data.mapping["bar"].should == "foo"
963
+ data.mapping["baz"].should == ""
964
+ end
965
+ it "lists vars" do
966
+ subject.variables.should == %w(foo bar baz)
967
+ end
968
+ end
969
+ context "? operator" do
970
+ context "test" do
971
+ subject { Addressable::Template.new("foo{?foo,bar}baz") }
972
+ it "can match" do
973
+ data = subject.match("foo?foo=bar%20baz&bar=foobaz")
974
+ data.mapping["foo"].should == "bar baz"
975
+ data.mapping["bar"].should == "foo"
976
+ end
977
+ it "lists vars" do
978
+ subject.variables.should == %w(foo bar)
979
+ end
980
+ end
981
+ context "issue #71" do
982
+ subject { Addressable::Template.new("http://cyberscore.dev/api/users{?username}") }
983
+ it "can match" do
984
+ data = subject.match("http://cyberscore.dev/api/users?username=foobaz")
985
+ data.mapping["username"].should == "foobaz"
986
+ end
987
+ it "lists vars" do
988
+ subject.variables.should == %w(username)
989
+ subject.keys.should == %w(username)
990
+ end
991
+ end
992
+ end
993
+ context "& operator" do
994
+ subject { Addressable::Template.new("foo{&foo,bar}baz") }
995
+ it "can match" do
996
+ data = subject.match("foo&foo=bar%20baz&bar=foobaz")
997
+ data.mapping["foo"].should == "bar baz"
998
+ data.mapping["bar"].should == "foo"
999
+ end
1000
+ it "lists vars" do
1001
+ subject.variables.should == %w(foo bar)
1002
+ end
1003
+ end
1004
+ end
1005
+ end
1006
+
1007
+ context "support regexes:" do
1008
+ context "EXPRESSION" do
1009
+ subject { Addressable::Template::EXPRESSION }
1010
+ it "should be able to match an expression" do
1011
+ subject.should match("{foo}")
1012
+ subject.should match("{foo,9}")
1013
+ subject.should match("{foo.bar,baz}")
1014
+ subject.should match("{+foo.bar,baz}")
1015
+ subject.should match("{foo,foo%20bar}")
1016
+ subject.should match("{#foo:20,baz*}")
1017
+ subject.should match("stuff{#foo:20,baz*}things")
1018
+ end
1019
+ it "should fail on non vars" do
1020
+ subject.should_not match("!{foo")
1021
+ subject.should_not match("{foo.bar.}")
1022
+ subject.should_not match("!{}")
1023
+ end
1024
+ end
1025
+ context "VARNAME" do
1026
+ subject { Addressable::Template::VARNAME }
1027
+ it "should be able to match a variable" do
1028
+ subject.should match("foo")
1029
+ subject.should match("9")
1030
+ subject.should match("foo.bar")
1031
+ subject.should match("foo_bar")
1032
+ subject.should match("foo_bar.baz")
1033
+ subject.should match("foo%20bar")
1034
+ subject.should match("foo%20bar.baz")
1035
+ end
1036
+ it "should fail on non vars" do
1037
+ subject.should_not match("!foo")
1038
+ subject.should_not match("foo.bar.")
1039
+ subject.should_not match("foo%2%00bar")
1040
+ subject.should_not match("foo_ba%r")
1041
+ subject.should_not match("foo_bar*")
1042
+ subject.should_not match("foo_bar:20")
1043
+ end
1044
+ end
1045
+ context "VARIABLE_LIST" do
1046
+ subject { Addressable::Template::VARIABLE_LIST }
1047
+ it "should be able to match a variable list" do
1048
+ subject.should match("foo,bar")
1049
+ subject.should match("foo")
1050
+ subject.should match("foo,bar*,baz")
1051
+ subject.should match("foo.bar,bar_baz*,baz:12")
1052
+ end
1053
+ it "should fail on non vars" do
1054
+ subject.should_not match(",foo,bar*,baz")
1055
+ subject.should_not match("foo,*bar,baz")
1056
+ subject.should_not match("foo,,bar*,baz")
1057
+ end
1058
+ end
1059
+ context "VARSPEC" do
1060
+ subject { Addressable::Template::VARSPEC }
1061
+ it "should be able to match a variable with modifier" do
1062
+ subject.should match("9:8")
1063
+ subject.should match("foo.bar*")
1064
+ subject.should match("foo_bar:12")
1065
+ subject.should match("foo_bar.baz*")
1066
+ subject.should match("foo%20bar:12")
1067
+ subject.should match("foo%20bar.baz*")
1068
+ end
1069
+ it "should fail on non vars" do
1070
+ subject.should_not match("!foo")
1071
+ subject.should_not match("*foo")
1072
+ subject.should_not match("fo*o")
1073
+ subject.should_not match("fo:o")
1074
+ subject.should_not match("foo:")
1075
+ end
1076
+ end
1077
+ end
1078
+ end
1079
+
1080
+ describe Addressable::Template::MatchData do
1081
+ let(:template) { Addressable::Template.new('{foo}/{bar}') }
1082
+ subject(:its) { template.match('ab/cd') }
1083
+ its(:uri) { should == Addressable::URI.parse('ab/cd') }
1084
+ its(:template) { should == template }
1085
+ its(:mapping) { should == { 'foo' => 'ab', 'bar' => 'cd' } }
1086
+ its(:variables) { should == ['foo', 'bar'] }
1087
+ its(:keys) { should == its.variables }
1088
+ its(:names) { should == its.variables }
1089
+ its(:values) { should == ['ab', 'cd'] }
1090
+ its(:captures) { should == its.values }
1091
+ its(:to_a) { should == ['ab/cd', 'ab', 'cd'] }
1092
+ its(:to_s) { should == 'ab/cd' }
1093
+ its(:string) { should == its.to_s }
1094
+ its(:pre_match) { should == "" }
1095
+ its(:post_match) { should == "" }
1096
+
1097
+ describe 'values_at' do
1098
+ it 'returns an array with the values' do
1099
+ its.values_at(0, 2).should == ['ab/cd', 'cd']
1100
+ end
1101
+ it 'allows mixing integer an string keys' do
1102
+ its.values_at('foo', 1).should == ['ab', 'ab']
1103
+ end
1104
+ it 'accepts unknown keys' do
1105
+ its.values_at('baz', 'foo').should == [nil, 'ab']
1106
+ end
1107
+ end
1108
+
1109
+ describe '[]' do
1110
+ context 'string key' do
1111
+ it 'returns the corresponding capture' do
1112
+ its['foo'].should == 'ab'
1113
+ its['bar'].should == 'cd'
1114
+ end
1115
+ it 'returns nil for unknown keys' do
1116
+ its['baz'].should be_nil
1117
+ end
1118
+ end
1119
+ context 'symbol key' do
1120
+ it 'returns the corresponding capture' do
1121
+ its[:foo].should == 'ab'
1122
+ its[:bar].should == 'cd'
1123
+ end
1124
+ it 'returns nil for unknown keys' do
1125
+ its[:baz].should be_nil
1126
+ end
1127
+ end
1128
+ context 'integer key' do
1129
+ it 'returns the full URI for index 0' do
1130
+ its[0].should == 'ab/cd'
1131
+ end
1132
+ it 'returns the corresponding capture' do
1133
+ its[1].should == 'ab'
1134
+ its[2].should == 'cd'
1135
+ end
1136
+ it 'returns nil for unknown keys' do
1137
+ its[3].should be_nil
1138
+ end
1139
+ end
1140
+ context 'other key' do
1141
+ it 'raises an exception' do
1142
+ expect { its[Object.new] }.to raise_error(TypeError)
1143
+ end
1144
+ end
1145
+ context 'with length' do
1146
+ it 'returns an array starting at index with given length' do
1147
+ its[0, 2].should == ['ab/cd', 'ab']
1148
+ its[2, 1].should == ['cd']
1149
+ end
1150
+ end
1151
+ end
1152
+ end