mustermann-contrib 1.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +1239 -0
  3. data/examples/highlighting.rb +35 -0
  4. data/highlighting.png +0 -0
  5. data/irb.png +0 -0
  6. data/lib/mustermann/cake.rb +18 -0
  7. data/lib/mustermann/express.rb +37 -0
  8. data/lib/mustermann/file_utils.rb +217 -0
  9. data/lib/mustermann/file_utils/glob_pattern.rb +39 -0
  10. data/lib/mustermann/fileutils.rb +1 -0
  11. data/lib/mustermann/flask.rb +198 -0
  12. data/lib/mustermann/grape.rb +35 -0
  13. data/lib/mustermann/pyramid.rb +28 -0
  14. data/lib/mustermann/rails.rb +46 -0
  15. data/lib/mustermann/shell.rb +56 -0
  16. data/lib/mustermann/simple.rb +50 -0
  17. data/lib/mustermann/string_scanner.rb +313 -0
  18. data/lib/mustermann/strscan.rb +1 -0
  19. data/lib/mustermann/template.rb +62 -0
  20. data/lib/mustermann/uri_template.rb +1 -0
  21. data/lib/mustermann/versions.rb +46 -0
  22. data/lib/mustermann/visualizer.rb +38 -0
  23. data/lib/mustermann/visualizer/highlight.rb +137 -0
  24. data/lib/mustermann/visualizer/highlighter.rb +37 -0
  25. data/lib/mustermann/visualizer/highlighter/ad_hoc.rb +94 -0
  26. data/lib/mustermann/visualizer/highlighter/ast.rb +102 -0
  27. data/lib/mustermann/visualizer/highlighter/composite.rb +45 -0
  28. data/lib/mustermann/visualizer/highlighter/dummy.rb +18 -0
  29. data/lib/mustermann/visualizer/highlighter/regular.rb +104 -0
  30. data/lib/mustermann/visualizer/pattern_extension.rb +68 -0
  31. data/lib/mustermann/visualizer/renderer/ansi.rb +23 -0
  32. data/lib/mustermann/visualizer/renderer/generic.rb +46 -0
  33. data/lib/mustermann/visualizer/renderer/hansi_template.rb +34 -0
  34. data/lib/mustermann/visualizer/renderer/html.rb +50 -0
  35. data/lib/mustermann/visualizer/renderer/sexp.rb +37 -0
  36. data/lib/mustermann/visualizer/tree.rb +63 -0
  37. data/lib/mustermann/visualizer/tree_renderer.rb +78 -0
  38. data/mustermann-contrib.gemspec +19 -0
  39. data/spec/cake_spec.rb +90 -0
  40. data/spec/express_spec.rb +209 -0
  41. data/spec/file_utils_spec.rb +119 -0
  42. data/spec/flask_spec.rb +361 -0
  43. data/spec/flask_subclass_spec.rb +368 -0
  44. data/spec/grape_spec.rb +747 -0
  45. data/spec/pattern_extension_spec.rb +49 -0
  46. data/spec/pyramid_spec.rb +101 -0
  47. data/spec/rails_spec.rb +647 -0
  48. data/spec/shell_spec.rb +147 -0
  49. data/spec/simple_spec.rb +268 -0
  50. data/spec/string_scanner_spec.rb +271 -0
  51. data/spec/template_spec.rb +841 -0
  52. data/spec/visualizer_spec.rb +199 -0
  53. data/theme.png +0 -0
  54. data/tree.png +0 -0
  55. metadata +126 -0
@@ -0,0 +1,841 @@
1
+ require 'support'
2
+ require 'mustermann/template'
3
+
4
+ describe Mustermann::Template do
5
+ extend Support::Pattern
6
+
7
+ pattern '' do
8
+ it { should match('') }
9
+ it { should_not match('/') }
10
+
11
+ it { should respond_to(:expand) }
12
+ it { should respond_to(:to_templates) }
13
+ end
14
+
15
+ pattern '/' do
16
+ it { should match('/') }
17
+ it { should_not match('/foo') }
18
+ end
19
+
20
+ pattern '/foo' do
21
+ it { should match('/foo') }
22
+ it { should_not match('/bar') }
23
+ it { should_not match('/foo.bar') }
24
+ end
25
+
26
+ pattern '/foo/bar' do
27
+ it { should match('/foo/bar') }
28
+ it { should_not match('/foo%2Fbar') }
29
+ it { should_not match('/foo%2fbar') }
30
+ end
31
+
32
+ pattern '/:foo' do
33
+ it { should match('/:foo') }
34
+ it { should match('/%3Afoo') }
35
+ it { should_not match('/foo') }
36
+ it { should_not match('/foo?') }
37
+ it { should_not match('/foo/bar') }
38
+ it { should_not match('/') }
39
+ it { should_not match('/foo/') }
40
+ end
41
+
42
+ pattern '/föö' do
43
+ it { should match("/f%C3%B6%C3%B6") }
44
+ end
45
+
46
+ pattern '/test$/' do
47
+ it { should match('/test$/') }
48
+ end
49
+
50
+ pattern '/te+st/' do
51
+ it { should match('/te+st/') }
52
+ it { should_not match('/test/') }
53
+ it { should_not match('/teest/') }
54
+ end
55
+
56
+ pattern "/path with spaces" do
57
+ it { should match('/path%20with%20spaces') }
58
+ it { should match('/path%2Bwith%2Bspaces') }
59
+ it { should match('/path+with+spaces') }
60
+ end
61
+
62
+ pattern '/foo&bar' do
63
+ it { should match('/foo&bar') }
64
+ end
65
+
66
+ pattern '/test.bar' do
67
+ it { should match('/test.bar') }
68
+ it { should_not match('/test0bar') }
69
+ end
70
+
71
+ pattern "/path with spaces", space_matches_plus: false do
72
+ it { should match('/path%20with%20spaces') }
73
+ it { should_not match('/path%2Bwith%2Bspaces') }
74
+ it { should_not match('/path+with+spaces') }
75
+ end
76
+
77
+ pattern "/path with spaces", uri_decode: false do
78
+ it { should_not match('/path%20with%20spaces') }
79
+ it { should_not match('/path%2Bwith%2Bspaces') }
80
+ it { should_not match('/path+with+spaces') }
81
+ end
82
+
83
+ context 'level 1' do
84
+ context 'without operator' do
85
+ pattern '/hello/{person}' do
86
+ it { should match('/hello/Frank').capturing person: 'Frank' }
87
+ it { should match('/hello/a_b~c').capturing person: 'a_b~c' }
88
+ it { should match('/hello/a.%20').capturing person: 'a.%20' }
89
+
90
+ it { should_not match('/hello/:') }
91
+ it { should_not match('/hello//') }
92
+ it { should_not match('/hello/?') }
93
+ it { should_not match('/hello/#') }
94
+ it { should_not match('/hello/[') }
95
+ it { should_not match('/hello/]') }
96
+ it { should_not match('/hello/@') }
97
+ it { should_not match('/hello/!') }
98
+ it { should_not match('/hello/*') }
99
+ it { should_not match('/hello/+') }
100
+ it { should_not match('/hello/,') }
101
+ it { should_not match('/hello/;') }
102
+ it { should_not match('/hello/=') }
103
+
104
+ example { pattern.params('/hello/Frank').should be == {'person' => 'Frank'} }
105
+ end
106
+
107
+ pattern "/{foo}/{bar}" do
108
+ it { should match('/foo/bar') .capturing foo: 'foo', bar: 'bar' }
109
+ it { should match('/foo.bar/bar.foo') .capturing foo: 'foo.bar', bar: 'bar.foo' }
110
+ it { should match('/10.1/te.st') .capturing foo: '10.1', bar: 'te.st' }
111
+ it { should match('/10.1.2/te.st') .capturing foo: '10.1.2', bar: 'te.st' }
112
+
113
+ it { should_not match('/foo%2Fbar') }
114
+ it { should_not match('/foo%2fbar') }
115
+ end
116
+ end
117
+ end
118
+
119
+ context 'level 2' do
120
+ context 'operator +' do
121
+ pattern '/hello/{+person}' do
122
+ it { should match('/hello/Frank') .capturing person: 'Frank' }
123
+ it { should match('/hello/a_b~c') .capturing person: 'a_b~c' }
124
+ it { should match('/hello/a.%20') .capturing person: 'a.%20' }
125
+ it { should match('/hello/a/%20') .capturing person: 'a/%20' }
126
+ it { should match('/hello/:') .capturing person: ?: }
127
+ it { should match('/hello//') .capturing person: ?/ }
128
+ it { should match('/hello/?') .capturing person: ?? }
129
+ it { should match('/hello/#') .capturing person: ?# }
130
+ it { should match('/hello/[') .capturing person: ?[ }
131
+ it { should match('/hello/]') .capturing person: ?] }
132
+ it { should match('/hello/@') .capturing person: ?@ }
133
+ it { should match('/hello/!') .capturing person: ?! }
134
+ it { should match('/hello/*') .capturing person: ?* }
135
+ it { should match('/hello/+') .capturing person: ?+ }
136
+ it { should match('/hello/,') .capturing person: ?, }
137
+ it { should match('/hello/;') .capturing person: ?; }
138
+ it { should match('/hello/=') .capturing person: ?= }
139
+ end
140
+
141
+ pattern "/{+foo}/{bar}" do
142
+ it { should match('/foo/bar') .capturing foo: 'foo', bar: 'bar' }
143
+ it { should match('/foo.bar/bar.foo') .capturing foo: 'foo.bar', bar: 'bar.foo' }
144
+ it { should match('/foo/bar/bar.foo') .capturing foo: 'foo/bar', bar: 'bar.foo' }
145
+ it { should match('/10.1/te.st') .capturing foo: '10.1', bar: 'te.st' }
146
+ it { should match('/10.1.2/te.st') .capturing foo: '10.1.2', bar: 'te.st' }
147
+
148
+ it { should_not match('/foo%2Fbar') }
149
+ it { should_not match('/foo%2fbar') }
150
+ end
151
+ end
152
+
153
+ context 'operator #' do
154
+ pattern '/hello/{#person}' do
155
+ it { should match('/hello/#Frank') .capturing person: 'Frank' }
156
+ it { should match('/hello/#a_b~c') .capturing person: 'a_b~c' }
157
+ it { should match('/hello/#a.%20') .capturing person: 'a.%20' }
158
+ it { should match('/hello/#a/%20') .capturing person: 'a/%20' }
159
+ it { should match('/hello/#:') .capturing person: ?: }
160
+ it { should match('/hello/#/') .capturing person: ?/ }
161
+ it { should match('/hello/#?') .capturing person: ?? }
162
+ it { should match('/hello/##') .capturing person: ?# }
163
+ it { should match('/hello/#[') .capturing person: ?[ }
164
+ it { should match('/hello/#]') .capturing person: ?] }
165
+ it { should match('/hello/#@') .capturing person: ?@ }
166
+ it { should match('/hello/#!') .capturing person: ?! }
167
+ it { should match('/hello/#*') .capturing person: ?* }
168
+ it { should match('/hello/#+') .capturing person: ?+ }
169
+ it { should match('/hello/#,') .capturing person: ?, }
170
+ it { should match('/hello/#;') .capturing person: ?; }
171
+ it { should match('/hello/#=') .capturing person: ?= }
172
+
173
+
174
+ it { should_not match('/hello/Frank') }
175
+ it { should_not match('/hello/a_b~c') }
176
+ it { should_not match('/hello/a.%20') }
177
+
178
+ it { should_not match('/hello/:') }
179
+ it { should_not match('/hello//') }
180
+ it { should_not match('/hello/?') }
181
+ it { should_not match('/hello/#') }
182
+ it { should_not match('/hello/[') }
183
+ it { should_not match('/hello/]') }
184
+ it { should_not match('/hello/@') }
185
+ it { should_not match('/hello/!') }
186
+ it { should_not match('/hello/*') }
187
+ it { should_not match('/hello/+') }
188
+ it { should_not match('/hello/,') }
189
+ it { should_not match('/hello/;') }
190
+ it { should_not match('/hello/=') }
191
+
192
+
193
+ example { pattern.params('/hello/#Frank').should be == {'person' => 'Frank'} }
194
+ end
195
+
196
+ pattern "/{+foo}/{#bar}" do
197
+ it { should match('/foo/#bar') .capturing foo: 'foo', bar: 'bar' }
198
+ it { should match('/foo.bar/#bar.foo') .capturing foo: 'foo.bar', bar: 'bar.foo' }
199
+ it { should match('/foo/bar/#bar.foo') .capturing foo: 'foo/bar', bar: 'bar.foo' }
200
+ it { should match('/10.1/#te.st') .capturing foo: '10.1', bar: 'te.st' }
201
+ it { should match('/10.1.2/#te.st') .capturing foo: '10.1.2', bar: 'te.st' }
202
+
203
+ it { should_not match('/foo%2F#bar') }
204
+ it { should_not match('/foo%2f#bar') }
205
+
206
+ example { pattern.params('/hello/#Frank').should be == {'foo' => 'hello', 'bar' => 'Frank'} }
207
+ end
208
+ end
209
+ end
210
+
211
+ context 'level 3' do
212
+ context 'without operator' do
213
+ pattern "{a,b,c}" do
214
+ it { should match("~x,42,_").capturing a: '~x', b: '42', c: '_' }
215
+ it { should_not match("~x,42") }
216
+ it { should_not match("~x/42") }
217
+ it { should_not match("~x#42") }
218
+ it { should_not match("~x,42,_#42") }
219
+
220
+ example { pattern.params('d,f,g').should be == {'a' => 'd', 'b' => 'f', 'c' => 'g'} }
221
+ end
222
+ end
223
+
224
+ context 'operator +' do
225
+ pattern "{+a,b,c}" do
226
+ it { should match("~x,42,_") .capturing a: '~x', b: '42', c: '_' }
227
+ it { should match("~x,42,_#42") .capturing a: '~x', b: '42', c: '_#42' }
228
+ it { should match("~/x,42,_/42") .capturing a: '~/x', b: '42', c: '_/42' }
229
+
230
+ it { should_not match("~x,42") }
231
+ it { should_not match("~x/42") }
232
+ it { should_not match("~x#42") }
233
+ end
234
+ end
235
+
236
+ context 'operator #' do
237
+ pattern "{#a,b,c}" do
238
+ it { should match("#~x,42,_") .capturing a: '~x', b: '42', c: '_' }
239
+ it { should match("#~x,42,_#42") .capturing a: '~x', b: '42', c: '_#42' }
240
+ it { should match("#~/x,42,_#42") .capturing a: '~/x', b: '42', c: '_#42' }
241
+
242
+ it { should_not match("~x,42,_") }
243
+ it { should_not match("~x,42,_#42") }
244
+ it { should_not match("~/x,42,_#42") }
245
+
246
+ it { should_not match("~x,42") }
247
+ it { should_not match("~x/42") }
248
+ it { should_not match("~x#42") }
249
+ end
250
+ end
251
+
252
+ context 'operator .' do
253
+ pattern '/hello/{.person}' do
254
+ it { should match('/hello/.Frank') .capturing person: 'Frank' }
255
+ it { should match('/hello/.a_b~c') .capturing person: 'a_b~c' }
256
+
257
+ it { should_not match('/hello/.:') }
258
+ it { should_not match('/hello/./') }
259
+ it { should_not match('/hello/.?') }
260
+ it { should_not match('/hello/.#') }
261
+ it { should_not match('/hello/.[') }
262
+ it { should_not match('/hello/.]') }
263
+ it { should_not match('/hello/.@') }
264
+ it { should_not match('/hello/.!') }
265
+ it { should_not match('/hello/.*') }
266
+ it { should_not match('/hello/.+') }
267
+ it { should_not match('/hello/.,') }
268
+ it { should_not match('/hello/.;') }
269
+ it { should_not match('/hello/.=') }
270
+
271
+ it { should_not match('/hello/Frank') }
272
+ it { should_not match('/hello/a_b~c') }
273
+ it { should_not match('/hello/a.%20') }
274
+
275
+ it { should_not match('/hello/:') }
276
+ it { should_not match('/hello//') }
277
+ it { should_not match('/hello/?') }
278
+ it { should_not match('/hello/#') }
279
+ it { should_not match('/hello/[') }
280
+ it { should_not match('/hello/]') }
281
+ it { should_not match('/hello/@') }
282
+ it { should_not match('/hello/!') }
283
+ it { should_not match('/hello/*') }
284
+ it { should_not match('/hello/+') }
285
+ it { should_not match('/hello/,') }
286
+ it { should_not match('/hello/;') }
287
+ it { should_not match('/hello/=') }
288
+ end
289
+
290
+ pattern "{.a,b,c}" do
291
+ it { should match(".~x.42._").capturing a: '~x', b: '42', c: '_' }
292
+ it { should_not match(".~x,42") }
293
+ it { should_not match(".~x/42") }
294
+ it { should_not match(".~x#42") }
295
+ it { should_not match(".~x,42,_") }
296
+ it { should_not match("~x.42._") }
297
+ end
298
+ end
299
+
300
+ context 'operator /' do
301
+ pattern '/hello{/person}' do
302
+ it { should match('/hello/Frank') .capturing person: 'Frank' }
303
+ it { should match('/hello/a_b~c') .capturing person: 'a_b~c' }
304
+
305
+ it { should_not match('/hello//:') }
306
+ it { should_not match('/hello///') }
307
+ it { should_not match('/hello//?') }
308
+ it { should_not match('/hello//#') }
309
+ it { should_not match('/hello//[') }
310
+ it { should_not match('/hello//]') }
311
+ it { should_not match('/hello//@') }
312
+ it { should_not match('/hello//!') }
313
+ it { should_not match('/hello//*') }
314
+ it { should_not match('/hello//+') }
315
+ it { should_not match('/hello//,') }
316
+ it { should_not match('/hello//;') }
317
+ it { should_not match('/hello//=') }
318
+
319
+ it { should_not match('/hello/:') }
320
+ it { should_not match('/hello//') }
321
+ it { should_not match('/hello/?') }
322
+ it { should_not match('/hello/#') }
323
+ it { should_not match('/hello/[') }
324
+ it { should_not match('/hello/]') }
325
+ it { should_not match('/hello/@') }
326
+ it { should_not match('/hello/!') }
327
+ it { should_not match('/hello/*') }
328
+ it { should_not match('/hello/+') }
329
+ it { should_not match('/hello/,') }
330
+ it { should_not match('/hello/;') }
331
+ it { should_not match('/hello/=') }
332
+ end
333
+
334
+ pattern "{/a,b,c}" do
335
+ it { should match("/~x/42/_").capturing a: '~x', b: '42', c: '_' }
336
+ it { should_not match("/~x,42") }
337
+ it { should_not match("/~x.42") }
338
+ it { should_not match("/~x#42") }
339
+ it { should_not match("/~x,42,_") }
340
+ it { should_not match("~x/42/_") }
341
+ end
342
+ end
343
+
344
+ context 'operator ;' do
345
+ pattern '/hello/{;person}' do
346
+ it { should match('/hello/;person=Frank') .capturing person: 'Frank' }
347
+ it { should match('/hello/;person=a_b~c') .capturing person: 'a_b~c' }
348
+ it { should match('/hello/;person') .capturing person: nil }
349
+
350
+ it { should_not match('/hello/;persona=Frank') }
351
+ it { should_not match('/hello/;persona=a_b~c') }
352
+
353
+ it { should_not match('/hello/;person=:') }
354
+ it { should_not match('/hello/;person=/') }
355
+ it { should_not match('/hello/;person=?') }
356
+ it { should_not match('/hello/;person=#') }
357
+ it { should_not match('/hello/;person=[') }
358
+ it { should_not match('/hello/;person=]') }
359
+ it { should_not match('/hello/;person=@') }
360
+ it { should_not match('/hello/;person=!') }
361
+ it { should_not match('/hello/;person=*') }
362
+ it { should_not match('/hello/;person=+') }
363
+ it { should_not match('/hello/;person=,') }
364
+ it { should_not match('/hello/;person=;') }
365
+ it { should_not match('/hello/;person==') }
366
+
367
+ it { should_not match('/hello/;Frank') }
368
+ it { should_not match('/hello/;a_b~c') }
369
+ it { should_not match('/hello/;a.%20') }
370
+
371
+ it { should_not match('/hello/:') }
372
+ it { should_not match('/hello//') }
373
+ it { should_not match('/hello/?') }
374
+ it { should_not match('/hello/#') }
375
+ it { should_not match('/hello/[') }
376
+ it { should_not match('/hello/]') }
377
+ it { should_not match('/hello/@') }
378
+ it { should_not match('/hello/!') }
379
+ it { should_not match('/hello/*') }
380
+ it { should_not match('/hello/+') }
381
+ it { should_not match('/hello/,') }
382
+ it { should_not match('/hello/;') }
383
+ it { should_not match('/hello/=') }
384
+ end
385
+
386
+ pattern "{;a,b,c}" do
387
+ it { should match(";a=~x;b=42;c=_") .capturing a: '~x', b: '42', c: '_' }
388
+ it { should match(";a=~x;b;c=_") .capturing a: '~x', b: nil, c: '_' }
389
+
390
+ it { should_not match(";a=~x;c=_;b=42").capturing a: '~x', b: '42', c: '_' }
391
+
392
+ it { should_not match(";a=~x;b=42") }
393
+ it { should_not match("a=~x;b=42") }
394
+ it { should_not match(";a=~x;b=#42;c") }
395
+ it { should_not match(";a=~x,b=42,c=_") }
396
+ it { should_not match("~x;b=42;c=_") }
397
+ end
398
+ end
399
+
400
+ context 'operator ?' do
401
+ pattern '/hello/{?person}' do
402
+ it { should match('/hello/?person=Frank') .capturing person: 'Frank' }
403
+ it { should match('/hello/?person=a_b~c') .capturing person: 'a_b~c' }
404
+ it { should match('/hello/?person') .capturing person: nil }
405
+
406
+ it { should_not match('/hello/?persona=Frank') }
407
+ it { should_not match('/hello/?persona=a_b~c') }
408
+
409
+ it { should_not match('/hello/?person=:') }
410
+ it { should_not match('/hello/?person=/') }
411
+ it { should_not match('/hello/?person=?') }
412
+ it { should_not match('/hello/?person=#') }
413
+ it { should_not match('/hello/?person=[') }
414
+ it { should_not match('/hello/?person=]') }
415
+ it { should_not match('/hello/?person=@') }
416
+ it { should_not match('/hello/?person=!') }
417
+ it { should_not match('/hello/?person=*') }
418
+ it { should_not match('/hello/?person=+') }
419
+ it { should_not match('/hello/?person=,') }
420
+ it { should_not match('/hello/?person=;') }
421
+ it { should_not match('/hello/?person==') }
422
+
423
+ it { should_not match('/hello/?Frank') }
424
+ it { should_not match('/hello/?a_b~c') }
425
+ it { should_not match('/hello/?a.%20') }
426
+
427
+ it { should_not match('/hello/:') }
428
+ it { should_not match('/hello//') }
429
+ it { should_not match('/hello/?') }
430
+ it { should_not match('/hello/#') }
431
+ it { should_not match('/hello/[') }
432
+ it { should_not match('/hello/]') }
433
+ it { should_not match('/hello/@') }
434
+ it { should_not match('/hello/!') }
435
+ it { should_not match('/hello/*') }
436
+ it { should_not match('/hello/+') }
437
+ it { should_not match('/hello/,') }
438
+ it { should_not match('/hello/;') }
439
+ it { should_not match('/hello/=') }
440
+ end
441
+
442
+ pattern "{?a,b,c}" do
443
+ it { should match("?a=~x&b=42&c=_") .capturing a: '~x', b: '42', c: '_' }
444
+ it { should match("?a=~x&b&c=_") .capturing a: '~x', b: nil, c: '_' }
445
+
446
+ it { should_not match("?a=~x&c=_&b=42").capturing a: '~x', b: '42', c: '_' }
447
+
448
+ it { should_not match("?a=~x&b=42") }
449
+ it { should_not match("a=~x&b=42") }
450
+ it { should_not match("?a=~x&b=#42&c") }
451
+ it { should_not match("?a=~x,b=42,c=_") }
452
+ it { should_not match("~x&b=42&c=_") }
453
+ end
454
+ end
455
+
456
+ context 'operator &' do
457
+ pattern '/hello/{&person}' do
458
+ it { should match('/hello/&person=Frank') .capturing person: 'Frank' }
459
+ it { should match('/hello/&person=a_b~c') .capturing person: 'a_b~c' }
460
+ it { should match('/hello/&person') .capturing person: nil }
461
+
462
+ it { should_not match('/hello/&persona=Frank') }
463
+ it { should_not match('/hello/&persona=a_b~c') }
464
+
465
+ it { should_not match('/hello/&person=:') }
466
+ it { should_not match('/hello/&person=/') }
467
+ it { should_not match('/hello/&person=?') }
468
+ it { should_not match('/hello/&person=#') }
469
+ it { should_not match('/hello/&person=[') }
470
+ it { should_not match('/hello/&person=]') }
471
+ it { should_not match('/hello/&person=@') }
472
+ it { should_not match('/hello/&person=!') }
473
+ it { should_not match('/hello/&person=*') }
474
+ it { should_not match('/hello/&person=+') }
475
+ it { should_not match('/hello/&person=,') }
476
+ it { should_not match('/hello/&person=;') }
477
+ it { should_not match('/hello/&person==') }
478
+
479
+ it { should_not match('/hello/&Frank') }
480
+ it { should_not match('/hello/&a_b~c') }
481
+ it { should_not match('/hello/&a.%20') }
482
+
483
+ it { should_not match('/hello/:') }
484
+ it { should_not match('/hello//') }
485
+ it { should_not match('/hello/?') }
486
+ it { should_not match('/hello/#') }
487
+ it { should_not match('/hello/[') }
488
+ it { should_not match('/hello/]') }
489
+ it { should_not match('/hello/@') }
490
+ it { should_not match('/hello/!') }
491
+ it { should_not match('/hello/*') }
492
+ it { should_not match('/hello/+') }
493
+ it { should_not match('/hello/,') }
494
+ it { should_not match('/hello/;') }
495
+ it { should_not match('/hello/=') }
496
+ end
497
+
498
+ pattern "{&a,b,c}" do
499
+ it { should match("&a=~x&b=42&c=_") .capturing a: '~x', b: '42', c: '_' }
500
+ it { should match("&a=~x&b&c=_") .capturing a: '~x', b: nil, c: '_' }
501
+
502
+ it { should_not match("&a=~x&c=_&b=42").capturing a: '~x', b: '42', c: '_' }
503
+
504
+ it { should_not match("&a=~x&b=42") }
505
+ it { should_not match("a=~x&b=42") }
506
+ it { should_not match("&a=~x&b=#42&c") }
507
+ it { should_not match("&a=~x,b=42,c=_") }
508
+ it { should_not match("~x&b=42&c=_") }
509
+ end
510
+ end
511
+ end
512
+
513
+ context 'level 4' do
514
+ context 'without operator' do
515
+ context 'prefix' do
516
+ pattern '{a:3}/bar' do
517
+ it { should match('foo/bar') .capturing a: 'foo' }
518
+ it { should match('fo/bar') .capturing a: 'fo' }
519
+ it { should match('f/bar') .capturing a: 'f' }
520
+ it { should_not match('fooo/bar') }
521
+ end
522
+
523
+ pattern '{a:3}{b}' do
524
+ it { should match('foobar') .capturing a: 'foo', b: 'bar' }
525
+ end
526
+ end
527
+
528
+ context 'expand' do
529
+ pattern '{a*}' do
530
+ it { should match('a') .capturing a: 'a' }
531
+ it { should match('a,b') .capturing a: 'a,b' }
532
+ it { should match('a,b,c') .capturing a: 'a,b,c' }
533
+ it { should_not match('a,b/c') }
534
+ it { should_not match('a,') }
535
+
536
+ example { pattern.params('a').should be == { 'a' => ['a'] }}
537
+ example { pattern.params('a,b').should be == { 'a' => ['a', 'b'] }}
538
+ end
539
+
540
+ pattern '{a*},{b}' do
541
+ it { should match('a,b') .capturing a: 'a', b: 'b' }
542
+ it { should match('a,b,c') .capturing a: 'a,b', b: 'c' }
543
+ it { should_not match('a,b/c') }
544
+ it { should_not match('a,') }
545
+
546
+ example { pattern.params('a,b').should be == { 'a' => ['a'], 'b' => 'b' }}
547
+ example { pattern.params('a,b,c').should be == { 'a' => ['a', 'b'], 'b' => 'c' }}
548
+ end
549
+
550
+ pattern '{a*,b}' do
551
+ it { should match('a,b') .capturing a: 'a', b: 'b' }
552
+ it { should match('a,b,c') .capturing a: 'a,b', b: 'c' }
553
+ it { should_not match('a,b/c') }
554
+ it { should_not match('a,') }
555
+
556
+ example { pattern.params('a,b').should be == { 'a' => ['a'], 'b' => 'b' }}
557
+ example { pattern.params('a,b,c').should be == { 'a' => ['a', 'b'], 'b' => 'c' }}
558
+ end
559
+ end
560
+ end
561
+
562
+ context 'operator +' do
563
+ pattern '/{a}/{+b}' do
564
+ it { should match('/foo/bar/baz').capturing(a: 'foo', b: 'bar/baz') }
565
+ it { should expand(a: 'foo/bar', b: 'foo/bar').to('/foo%2Fbar/foo/bar') }
566
+ end
567
+
568
+ context 'prefix' do
569
+ pattern '{+a:3}/bar' do
570
+ it { should match('foo/bar') .capturing a: 'foo' }
571
+ it { should match('fo/bar') .capturing a: 'fo' }
572
+ it { should match('f/bar') .capturing a: 'f' }
573
+ it { should_not match('fooo/bar') }
574
+ end
575
+
576
+ pattern '{+a:3}{b}' do
577
+ it { should match('foobar') .capturing a: 'foo', b: 'bar' }
578
+ end
579
+ end
580
+
581
+ context 'expand' do
582
+ pattern '{+a*}' do
583
+ it { should match('a') .capturing a: 'a' }
584
+ it { should match('a,b') .capturing a: 'a,b' }
585
+ it { should match('a,b,c') .capturing a: 'a,b,c' }
586
+ it { should match('a,b/c') .capturing a: 'a,b/c' }
587
+ end
588
+
589
+ pattern '{+a*},{b}' do
590
+ it { should match('a,b') .capturing a: 'a', b: 'b' }
591
+ it { should match('a,b,c') .capturing a: 'a,b', b: 'c' }
592
+ it { should_not match('a,b/c') }
593
+ it { should_not match('a,') }
594
+
595
+ example { pattern.params('a,b').should be == { 'a' => ['a'], 'b' => 'b' }}
596
+ example { pattern.params('a,b,c').should be == { 'a' => ['a', 'b'], 'b' => 'c' }}
597
+ end
598
+ end
599
+ end
600
+
601
+ context 'operator #' do
602
+ context 'prefix' do
603
+ pattern '{#a:3}/bar' do
604
+ it { should match('#foo/bar') .capturing a: 'foo' }
605
+ it { should match('#fo/bar') .capturing a: 'fo' }
606
+ it { should match('#f/bar') .capturing a: 'f' }
607
+ it { should_not match('#fooo/bar') }
608
+ end
609
+
610
+ pattern '{#a:3}{b}' do
611
+ it { should match('#foobar') .capturing a: 'foo', b: 'bar' }
612
+ end
613
+ end
614
+
615
+ context 'expand' do
616
+ pattern '{#a*}' do
617
+ it { should match('#a') .capturing a: 'a' }
618
+ it { should match('#a,b') .capturing a: 'a,b' }
619
+ it { should match('#a,b,c') .capturing a: 'a,b,c' }
620
+ it { should match('#a,b/c') .capturing a: 'a,b/c' }
621
+
622
+ example { pattern.params('#a,b').should be == { 'a' => ['a', 'b'] }}
623
+ example { pattern.params('#a,b,c').should be == { 'a' => ['a', 'b', 'c'] }}
624
+ end
625
+
626
+ pattern '{#a*,b}' do
627
+ it { should match('#a,b') .capturing a: 'a', b: 'b' }
628
+ it { should match('#a,b,c') .capturing a: 'a,b', b: 'c' }
629
+ it { should_not match('#a,') }
630
+
631
+ example { pattern.params('#a,b').should be == { 'a' => ['a'], 'b' => 'b' }}
632
+ example { pattern.params('#a,b,c').should be == { 'a' => ['a', 'b'], 'b' => 'c' }}
633
+ end
634
+ end
635
+ end
636
+
637
+ context 'operator .' do
638
+ context 'prefix' do
639
+ pattern '{.a:3}/bar' do
640
+ it { should match('.foo/bar') .capturing a: 'foo' }
641
+ it { should match('.fo/bar') .capturing a: 'fo' }
642
+ it { should match('.f/bar') .capturing a: 'f' }
643
+ it { should_not match('.fooo/bar') }
644
+ end
645
+
646
+ pattern '{.a:3}{b}' do
647
+ it { should match('.foobar') .capturing a: 'foo', b: 'bar' }
648
+ end
649
+ end
650
+
651
+ context 'expand' do
652
+ pattern '{.a*}' do
653
+ it { should match('.a') .capturing a: 'a' }
654
+ it { should match('.a.b') .capturing a: 'a.b' }
655
+ it { should match('.a.b.c') .capturing a: 'a.b.c' }
656
+ it { should_not match('.a.b,c') }
657
+ it { should_not match('.a,') }
658
+ end
659
+
660
+ pattern '{.a*,b}' do
661
+ it { should match('.a.b') .capturing a: 'a', b: 'b' }
662
+ it { should match('.a.b.c') .capturing a: 'a.b', b: 'c' }
663
+ it { should_not match('.a.b/c') }
664
+ it { should_not match('.a.') }
665
+
666
+ example { pattern.params('.a.b').should be == { 'a' => ['a'], 'b' => 'b' }}
667
+ example { pattern.params('.a.b.c').should be == { 'a' => ['a', 'b'], 'b' => 'c' }}
668
+ end
669
+ end
670
+ end
671
+
672
+ context 'operator /' do
673
+ context 'prefix' do
674
+ pattern '{/a:3}/bar' do
675
+ it { should match('/foo/bar') .capturing a: 'foo' }
676
+ it { should match('/fo/bar') .capturing a: 'fo' }
677
+ it { should match('/f/bar') .capturing a: 'f' }
678
+ it { should_not match('/fooo/bar') }
679
+ end
680
+
681
+ pattern '{/a:3}{b}' do
682
+ it { should match('/foobar') .capturing a: 'foo', b: 'bar' }
683
+ end
684
+ end
685
+
686
+ context 'expand' do
687
+ pattern '{/a*}' do
688
+ it { should match('/a') .capturing a: 'a' }
689
+ it { should match('/a/b') .capturing a: 'a/b' }
690
+ it { should match('/a/b/c') .capturing a: 'a/b/c' }
691
+ it { should_not match('/a/b,c') }
692
+ it { should_not match('/a,') }
693
+ end
694
+
695
+ pattern '{/a*,b}' do
696
+ it { should match('/a/b') .capturing a: 'a', b: 'b' }
697
+ it { should match('/a/b/c') .capturing a: 'a/b', b: 'c' }
698
+ it { should_not match('/a/b,c') }
699
+ it { should_not match('/a/') }
700
+
701
+ example { pattern.params('/a/b').should be == { 'a' => ['a'], 'b' => 'b' }}
702
+ example { pattern.params('/a/b/c').should be == { 'a' => ['a', 'b'], 'b' => 'c' }}
703
+ end
704
+ end
705
+ end
706
+
707
+ context 'operator ;' do
708
+ context 'prefix' do
709
+ pattern '{;a:3}/bar' do
710
+ it { should match(';a=foo/bar') .capturing a: 'foo' }
711
+ it { should match(';a=fo/bar') .capturing a: 'fo' }
712
+ it { should match(';a=f/bar') .capturing a: 'f' }
713
+ it { should_not match(';a=fooo/bar') }
714
+ end
715
+
716
+ pattern '{;a:3}{b}' do
717
+ it { should match(';a=foobar') .capturing a: 'foo', b: 'bar' }
718
+ end
719
+ end
720
+
721
+ context 'expand' do
722
+ pattern '{;a*}' do
723
+ it { should match(';a=1') .capturing a: 'a=1' }
724
+ it { should match(';a=1;a=2') .capturing a: 'a=1;a=2' }
725
+ it { should match(';a=1;a=2;a=3') .capturing a: 'a=1;a=2;a=3' }
726
+ it { should_not match(';a=1;a=2;b=3') }
727
+ it { should_not match(';a=1;a=2;a=3,') }
728
+ end
729
+
730
+ pattern '{;a*,b}' do
731
+ it { should match(';a=1;b') .capturing a: 'a=1', b: nil }
732
+ it { should match(';a=2;a=2;b=1') .capturing a: 'a=2;a=2', b: '1' }
733
+ it { should_not match(';a;b;c') }
734
+ it { should_not match(';a;') }
735
+
736
+ example { pattern.params(';a=2;a=2;b').should be == { 'a' => ['2', '2'], 'b' => nil }}
737
+ end
738
+ end
739
+ end
740
+
741
+ context 'operator ?' do
742
+ context 'prefix' do
743
+ pattern '{?a:3}/bar' do
744
+ it { should match('?a=foo/bar') .capturing a: 'foo' }
745
+ it { should match('?a=fo/bar') .capturing a: 'fo' }
746
+ it { should match('?a=f/bar') .capturing a: 'f' }
747
+ it { should_not match('?a=fooo/bar') }
748
+ end
749
+
750
+ pattern '{?a:3}{b}' do
751
+ it { should match('?a=foobar') .capturing a: 'foo', b: 'bar' }
752
+ end
753
+ end
754
+
755
+ context 'expand' do
756
+ pattern '{?a*}' do
757
+ it { should match('?a=1') .capturing a: 'a=1' }
758
+ it { should match('?a=1&a=2') .capturing a: 'a=1&a=2' }
759
+ it { should match('?a=1&a=2&a=3') .capturing a: 'a=1&a=2&a=3' }
760
+ it { should_not match('?a=1&a=2&b=3') }
761
+ it { should_not match('?a=1&a=2&a=3,') }
762
+ end
763
+
764
+ pattern '{?a*,b}' do
765
+ it { should match('?a=1&b') .capturing a: 'a=1', b: nil }
766
+ it { should match('?a=2&a=2&b=1') .capturing a: 'a=2&a=2', b: '1' }
767
+ it { should_not match('?a&b&c') }
768
+ it { should_not match('?a&') }
769
+
770
+ example { pattern.params('?a=2&a=2&b').should be == { 'a' => ['2', '2'], 'b' => nil }}
771
+ end
772
+ end
773
+ end
774
+
775
+ context 'operator &' do
776
+ context 'prefix' do
777
+ pattern '{&a:3}/bar' do
778
+ it { should match('&a=foo/bar') .capturing a: 'foo' }
779
+ it { should match('&a=fo/bar') .capturing a: 'fo' }
780
+ it { should match('&a=f/bar') .capturing a: 'f' }
781
+ it { should_not match('&a=fooo/bar') }
782
+ end
783
+
784
+ pattern '{&a:3}{b}' do
785
+ it { should match('&a=foobar') .capturing a: 'foo', b: 'bar' }
786
+ end
787
+ end
788
+
789
+ context 'expand' do
790
+ pattern '{&a*}' do
791
+ it { should match('&a=1') .capturing a: 'a=1' }
792
+ it { should match('&a=1&a=2') .capturing a: 'a=1&a=2' }
793
+ it { should match('&a=1&a=2&a=3') .capturing a: 'a=1&a=2&a=3' }
794
+ it { should_not match('&a=1&a=2&b=3') }
795
+ it { should_not match('&a=1&a=2&a=3,') }
796
+ end
797
+
798
+ pattern '{&a*,b}' do
799
+ it { should match('&a=1&b') .capturing a: 'a=1', b: nil }
800
+ it { should match('&a=2&a=2&b=1') .capturing a: 'a=2&a=2', b: '1' }
801
+ it { should_not match('&a&b&c') }
802
+ it { should_not match('&a&') }
803
+
804
+ example { pattern.params('&a=2&a=2&b').should be == { 'a' => ['2', '2'], 'b' => nil }}
805
+ example { pattern.params('&a=2&a=%20&b').should be == { 'a' => ['2', ' '], 'b' => nil }}
806
+ end
807
+ end
808
+ end
809
+ end
810
+
811
+ context 'invalid syntax' do
812
+ example 'unexpected closing bracket' do
813
+ expect { Mustermann::Template.new('foo}bar') }.
814
+ to raise_error(Mustermann::ParseError, 'unexpected } while parsing "foo}bar"')
815
+ end
816
+
817
+ example 'missing closing bracket' do
818
+ expect { Mustermann::Template.new('foo{bar') }.
819
+ to raise_error(Mustermann::ParseError, 'unexpected end of string while parsing "foo{bar"')
820
+ end
821
+ end
822
+
823
+ context "peeking" do
824
+ subject(:pattern) { Mustermann::Template.new("{name}bar") }
825
+
826
+ describe :peek_size do
827
+ example { pattern.peek_size("foo%20bar/blah") .should be == "foo%20bar".size }
828
+ example { pattern.peek_size("/foo bar") .should be_nil }
829
+ end
830
+
831
+ describe :peek_match do
832
+ example { pattern.peek_match("foo%20bar/blah") .to_s .should be == "foo%20bar" }
833
+ example { pattern.peek_match("/foo bar") .should be_nil }
834
+ end
835
+
836
+ describe :peek_params do
837
+ example { pattern.peek_params("foo%20bar/blah") .should be == [{"name" => "foo "}, "foo%20bar".size] }
838
+ example { pattern.peek_params("/foo bar") .should be_nil }
839
+ end
840
+ end
841
+ end