mustermann-contrib 1.0.0.beta2

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.
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