mustermann 0.3.1 → 0.4.0

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +429 -672
  3. data/lib/mustermann.rb +95 -20
  4. data/lib/mustermann/ast/boundaries.rb +44 -0
  5. data/lib/mustermann/ast/compiler.rb +13 -7
  6. data/lib/mustermann/ast/expander.rb +22 -12
  7. data/lib/mustermann/ast/node.rb +69 -5
  8. data/lib/mustermann/ast/param_scanner.rb +20 -0
  9. data/lib/mustermann/ast/parser.rb +138 -19
  10. data/lib/mustermann/ast/pattern.rb +59 -7
  11. data/lib/mustermann/ast/template_generator.rb +28 -0
  12. data/lib/mustermann/ast/transformer.rb +2 -2
  13. data/lib/mustermann/ast/translator.rb +20 -0
  14. data/lib/mustermann/ast/validation.rb +4 -3
  15. data/lib/mustermann/composite.rb +101 -0
  16. data/lib/mustermann/expander.rb +2 -2
  17. data/lib/mustermann/identity.rb +56 -0
  18. data/lib/mustermann/pattern.rb +185 -10
  19. data/lib/mustermann/pattern_cache.rb +49 -0
  20. data/lib/mustermann/regexp.rb +1 -0
  21. data/lib/mustermann/regexp_based.rb +18 -1
  22. data/lib/mustermann/regular.rb +4 -1
  23. data/lib/mustermann/simple_match.rb +5 -0
  24. data/lib/mustermann/sinatra.rb +22 -5
  25. data/lib/mustermann/to_pattern.rb +11 -6
  26. data/lib/mustermann/version.rb +1 -1
  27. data/mustermann.gemspec +1 -14
  28. data/spec/ast_spec.rb +14 -0
  29. data/spec/composite_spec.rb +147 -0
  30. data/spec/expander_spec.rb +15 -0
  31. data/spec/identity_spec.rb +44 -0
  32. data/spec/mustermann_spec.rb +17 -2
  33. data/spec/pattern_spec.rb +7 -3
  34. data/spec/regular_spec.rb +25 -0
  35. data/spec/sinatra_spec.rb +184 -9
  36. data/spec/to_pattern_spec.rb +49 -0
  37. metadata +15 -180
  38. data/.gitignore +0 -18
  39. data/.rspec +0 -2
  40. data/.travis.yml +0 -4
  41. data/.yardopts +0 -1
  42. data/Gemfile +0 -2
  43. data/LICENSE +0 -22
  44. data/Rakefile +0 -6
  45. data/internals.md +0 -64
  46. data/lib/mustermann/ast/tree_renderer.rb +0 -29
  47. data/lib/mustermann/rails.rb +0 -17
  48. data/lib/mustermann/shell.rb +0 -29
  49. data/lib/mustermann/simple.rb +0 -35
  50. data/lib/mustermann/template.rb +0 -47
  51. data/spec/rails_spec.rb +0 -521
  52. data/spec/shell_spec.rb +0 -108
  53. data/spec/simple_spec.rb +0 -236
  54. data/spec/support.rb +0 -5
  55. data/spec/support/coverage.rb +0 -16
  56. data/spec/support/env.rb +0 -16
  57. data/spec/support/expand_matcher.rb +0 -27
  58. data/spec/support/match_matcher.rb +0 -39
  59. data/spec/support/pattern.rb +0 -39
  60. data/spec/template_spec.rb +0 -814
@@ -1,39 +0,0 @@
1
- require 'timeout'
2
-
3
- module Support
4
- module Pattern
5
- def pattern(pattern, options = nil, &block)
6
- description = "pattern %p" % pattern
7
-
8
- if options
9
- description << " with options %p" % [options]
10
- instance = subject_for(pattern, options)
11
- else
12
- instance = subject_for(pattern)
13
- end
14
-
15
- context description do
16
- subject(:pattern, &instance)
17
- its(:to_s) { should be == pattern }
18
- its(:inspect) { should be == "#<#{described_class}:#{pattern.inspect}>" }
19
- its(:names) { should be_an(Array) }
20
-
21
- example 'string should be immune to external change' do
22
- subject.to_s.replace "NOT THE PATTERN"
23
- subject.to_s.should be == pattern
24
- end
25
-
26
- instance_eval(&block)
27
- end
28
- end
29
-
30
- def subject_for(pattern, *args)
31
- instance = Timeout.timeout(1) { described_class.new(pattern, *args) }
32
- proc { instance }
33
- rescue Timeout::Error => error
34
- proc { raise Timeout::Error, "could not compile #{pattern.inspect} in time", error.backtrace }
35
- rescue Exception => error
36
- proc { raise error }
37
- end
38
- end
39
- end
@@ -1,814 +0,0 @@
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
- end
11
-
12
- pattern '/' do
13
- it { should match('/') }
14
- it { should_not match('/foo') }
15
- end
16
-
17
- pattern '/foo' do
18
- it { should match('/foo') }
19
- it { should_not match('/bar') }
20
- it { should_not match('/foo.bar') }
21
- end
22
-
23
- pattern '/foo/bar' do
24
- it { should match('/foo/bar') }
25
- it { should_not match('/foo%2Fbar') }
26
- it { should_not match('/foo%2fbar') }
27
- end
28
-
29
- pattern '/:foo' do
30
- it { should match('/:foo') }
31
- it { should match('/%3Afoo') }
32
- it { should_not match('/foo') }
33
- it { should_not match('/foo?') }
34
- it { should_not match('/foo/bar') }
35
- it { should_not match('/') }
36
- it { should_not match('/foo/') }
37
- end
38
-
39
- pattern '/föö' do
40
- it { should match("/f%C3%B6%C3%B6") }
41
- end
42
-
43
- pattern '/test$/' do
44
- it { should match('/test$/') }
45
- end
46
-
47
- pattern '/te+st/' do
48
- it { should match('/te+st/') }
49
- it { should_not match('/test/') }
50
- it { should_not match('/teest/') }
51
- end
52
-
53
- pattern "/path with spaces" do
54
- it { should match('/path%20with%20spaces') }
55
- it { should match('/path%2Bwith%2Bspaces') }
56
- it { should match('/path+with+spaces') }
57
- end
58
-
59
- pattern '/foo&bar' do
60
- it { should match('/foo&bar') }
61
- end
62
-
63
- pattern '/test.bar' do
64
- it { should match('/test.bar') }
65
- it { should_not match('/test0bar') }
66
- end
67
-
68
- pattern "/path with spaces", space_matches_plus: false do
69
- it { should match('/path%20with%20spaces') }
70
- it { should_not match('/path%2Bwith%2Bspaces') }
71
- it { should_not match('/path+with+spaces') }
72
- end
73
-
74
- pattern "/path with spaces", uri_decode: false do
75
- it { should_not match('/path%20with%20spaces') }
76
- it { should_not match('/path%2Bwith%2Bspaces') }
77
- it { should_not match('/path+with+spaces') }
78
- end
79
-
80
- context 'level 1' do
81
- context 'without operator' do
82
- pattern '/hello/{person}' do
83
- it { should match('/hello/Frank').capturing person: 'Frank' }
84
- it { should match('/hello/a_b~c').capturing person: 'a_b~c' }
85
- it { should match('/hello/a.%20').capturing person: 'a.%20' }
86
-
87
- it { should_not match('/hello/:') }
88
- it { should_not match('/hello//') }
89
- it { should_not match('/hello/?') }
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
-
101
- example { pattern.params('/hello/Frank').should be == {'person' => 'Frank'} }
102
- end
103
-
104
- pattern "/{foo}/{bar}" do
105
- it { should match('/foo/bar') .capturing foo: 'foo', bar: 'bar' }
106
- it { should match('/foo.bar/bar.foo') .capturing foo: 'foo.bar', bar: 'bar.foo' }
107
- it { should match('/10.1/te.st') .capturing foo: '10.1', bar: 'te.st' }
108
- it { should match('/10.1.2/te.st') .capturing foo: '10.1.2', bar: 'te.st' }
109
-
110
- it { should_not match('/foo%2Fbar') }
111
- it { should_not match('/foo%2fbar') }
112
- end
113
- end
114
- end
115
-
116
- context 'level 2' do
117
- context 'operator +' do
118
- pattern '/hello/{+person}' do
119
- it { should match('/hello/Frank') .capturing person: 'Frank' }
120
- it { should match('/hello/a_b~c') .capturing person: 'a_b~c' }
121
- it { should match('/hello/a.%20') .capturing person: 'a.%20' }
122
- it { should match('/hello/a/%20') .capturing person: 'a/%20' }
123
- it { should match('/hello/:') .capturing person: ?: }
124
- it { should match('/hello//') .capturing person: ?/ }
125
- it { should match('/hello/?') .capturing person: ?? }
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
- end
137
-
138
- pattern "/{+foo}/{bar}" do
139
- it { should match('/foo/bar') .capturing foo: 'foo', bar: 'bar' }
140
- it { should match('/foo.bar/bar.foo') .capturing foo: 'foo.bar', bar: 'bar.foo' }
141
- it { should match('/foo/bar/bar.foo') .capturing foo: 'foo/bar', bar: 'bar.foo' }
142
- it { should match('/10.1/te.st') .capturing foo: '10.1', bar: 'te.st' }
143
- it { should match('/10.1.2/te.st') .capturing foo: '10.1.2', bar: 'te.st' }
144
-
145
- it { should_not match('/foo%2Fbar') }
146
- it { should_not match('/foo%2fbar') }
147
- end
148
- end
149
-
150
- context 'operator #' do
151
- pattern '/hello/{#person}' do
152
- it { should match('/hello/#Frank') .capturing person: 'Frank' }
153
- it { should match('/hello/#a_b~c') .capturing person: 'a_b~c' }
154
- it { should match('/hello/#a.%20') .capturing person: 'a.%20' }
155
- it { should match('/hello/#a/%20') .capturing person: 'a/%20' }
156
- it { should match('/hello/#:') .capturing person: ?: }
157
- it { should match('/hello/#/') .capturing person: ?/ }
158
- it { should match('/hello/#?') .capturing person: ?? }
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
-
170
-
171
- it { should_not match('/hello/Frank') }
172
- it { should_not match('/hello/a_b~c') }
173
- it { should_not match('/hello/a.%20') }
174
-
175
- it { should_not match('/hello/:') }
176
- it { should_not match('/hello//') }
177
- it { should_not match('/hello/?') }
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
-
189
-
190
- example { pattern.params('/hello/#Frank').should be == {'person' => 'Frank'} }
191
- end
192
-
193
- pattern "/{+foo}/{#bar}" do
194
- it { should match('/foo/#bar') .capturing foo: 'foo', bar: 'bar' }
195
- it { should match('/foo.bar/#bar.foo') .capturing foo: 'foo.bar', bar: 'bar.foo' }
196
- it { should match('/foo/bar/#bar.foo') .capturing foo: 'foo/bar', bar: 'bar.foo' }
197
- it { should match('/10.1/#te.st') .capturing foo: '10.1', bar: 'te.st' }
198
- it { should match('/10.1.2/#te.st') .capturing foo: '10.1.2', bar: 'te.st' }
199
-
200
- it { should_not match('/foo%2F#bar') }
201
- it { should_not match('/foo%2f#bar') }
202
-
203
- example { pattern.params('/hello/#Frank').should be == {'foo' => 'hello', 'bar' => 'Frank'} }
204
- end
205
- end
206
- end
207
-
208
- context 'level 3' do
209
- context 'without operator' do
210
- pattern "{a,b,c}" do
211
- it { should match("~x,42,_").capturing a: '~x', b: '42', c: '_' }
212
- it { should_not match("~x,42") }
213
- it { should_not match("~x/42") }
214
- it { should_not match("~x#42") }
215
- it { should_not match("~x,42,_#42") }
216
-
217
- example { pattern.params('d,f,g').should be == {'a' => 'd', 'b' => 'f', 'c' => 'g'} }
218
- end
219
- end
220
-
221
- context 'operator +' do
222
- pattern "{+a,b,c}" do
223
- it { should match("~x,42,_") .capturing a: '~x', b: '42', c: '_' }
224
- it { should match("~x,42,_#42") .capturing a: '~x', b: '42', c: '_#42' }
225
- it { should match("~/x,42,_/42") .capturing a: '~/x', b: '42', c: '_/42' }
226
-
227
- it { should_not match("~x,42") }
228
- it { should_not match("~x/42") }
229
- it { should_not match("~x#42") }
230
- end
231
- end
232
-
233
- context 'operator #' do
234
- pattern "{#a,b,c}" do
235
- it { should match("#~x,42,_") .capturing a: '~x', b: '42', c: '_' }
236
- it { should match("#~x,42,_#42") .capturing a: '~x', b: '42', c: '_#42' }
237
- it { should match("#~/x,42,_#42") .capturing a: '~/x', b: '42', c: '_#42' }
238
-
239
- it { should_not match("~x,42,_") }
240
- it { should_not match("~x,42,_#42") }
241
- it { should_not match("~/x,42,_#42") }
242
-
243
- it { should_not match("~x,42") }
244
- it { should_not match("~x/42") }
245
- it { should_not match("~x#42") }
246
- end
247
- end
248
-
249
- context 'operator .' do
250
- pattern '/hello/{.person}' do
251
- it { should match('/hello/.Frank') .capturing person: 'Frank' }
252
- it { should match('/hello/.a_b~c') .capturing person: 'a_b~c' }
253
-
254
- it { should_not match('/hello/.:') }
255
- it { should_not match('/hello/./') }
256
- it { should_not match('/hello/.?') }
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
-
268
- it { should_not match('/hello/Frank') }
269
- it { should_not match('/hello/a_b~c') }
270
- it { should_not match('/hello/a.%20') }
271
-
272
- it { should_not match('/hello/:') }
273
- it { should_not match('/hello//') }
274
- it { should_not match('/hello/?') }
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
- end
286
-
287
- pattern "{.a,b,c}" do
288
- it { should match(".~x.42._").capturing a: '~x', b: '42', c: '_' }
289
- it { should_not match(".~x,42") }
290
- it { should_not match(".~x/42") }
291
- it { should_not match(".~x#42") }
292
- it { should_not match(".~x,42,_") }
293
- it { should_not match("~x.42._") }
294
- end
295
- end
296
-
297
- context 'operator /' do
298
- pattern '/hello{/person}' do
299
- it { should match('/hello/Frank') .capturing person: 'Frank' }
300
- it { should match('/hello/a_b~c') .capturing person: 'a_b~c' }
301
-
302
- it { should_not match('/hello//:') }
303
- it { should_not match('/hello///') }
304
- it { should_not match('/hello//?') }
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
-
316
- it { should_not match('/hello/:') }
317
- it { should_not match('/hello//') }
318
- it { should_not match('/hello/?') }
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
- end
330
-
331
- pattern "{/a,b,c}" do
332
- it { should match("/~x/42/_").capturing a: '~x', b: '42', c: '_' }
333
- it { should_not match("/~x,42") }
334
- it { should_not match("/~x.42") }
335
- it { should_not match("/~x#42") }
336
- it { should_not match("/~x,42,_") }
337
- it { should_not match("~x/42/_") }
338
- end
339
- end
340
-
341
- context 'operator ;' do
342
- pattern '/hello/{;person}' do
343
- it { should match('/hello/;person=Frank') .capturing person: 'Frank' }
344
- it { should match('/hello/;person=a_b~c') .capturing person: 'a_b~c' }
345
- it { should match('/hello/;person') .capturing person: nil }
346
-
347
- it { should_not match('/hello/;persona=Frank') }
348
- it { should_not match('/hello/;persona=a_b~c') }
349
-
350
- it { should_not match('/hello/;person=:') }
351
- it { should_not match('/hello/;person=/') }
352
- it { should_not match('/hello/;person=?') }
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
-
364
- it { should_not match('/hello/;Frank') }
365
- it { should_not match('/hello/;a_b~c') }
366
- it { should_not match('/hello/;a.%20') }
367
-
368
- it { should_not match('/hello/:') }
369
- it { should_not match('/hello//') }
370
- it { should_not match('/hello/?') }
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
- end
382
-
383
- pattern "{;a,b,c}" do
384
- it { should match(";a=~x;b=42;c=_") .capturing a: '~x', b: '42', c: '_' }
385
- it { should match(";a=~x;b;c=_") .capturing a: '~x', b: nil, c: '_' }
386
-
387
- it { should_not match(";a=~x;c=_;b=42").capturing a: '~x', b: '42', c: '_' }
388
-
389
- it { should_not match(";a=~x;b=42") }
390
- it { should_not match("a=~x;b=42") }
391
- it { should_not match(";a=~x;b=#42;c") }
392
- it { should_not match(";a=~x,b=42,c=_") }
393
- it { should_not match("~x;b=42;c=_") }
394
- end
395
- end
396
-
397
- context 'operator ?' do
398
- pattern '/hello/{?person}' do
399
- it { should match('/hello/?person=Frank') .capturing person: 'Frank' }
400
- it { should match('/hello/?person=a_b~c') .capturing person: 'a_b~c' }
401
- it { should match('/hello/?person') .capturing person: nil }
402
-
403
- it { should_not match('/hello/?persona=Frank') }
404
- it { should_not match('/hello/?persona=a_b~c') }
405
-
406
- it { should_not match('/hello/?person=:') }
407
- it { should_not match('/hello/?person=/') }
408
- it { should_not match('/hello/?person=?') }
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
-
420
- it { should_not match('/hello/?Frank') }
421
- it { should_not match('/hello/?a_b~c') }
422
- it { should_not match('/hello/?a.%20') }
423
-
424
- it { should_not match('/hello/:') }
425
- it { should_not match('/hello//') }
426
- it { should_not match('/hello/?') }
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
- end
438
-
439
- pattern "{?a,b,c}" do
440
- it { should match("?a=~x&b=42&c=_") .capturing a: '~x', b: '42', c: '_' }
441
- it { should match("?a=~x&b&c=_") .capturing a: '~x', b: nil, c: '_' }
442
-
443
- it { should_not match("?a=~x&c=_&b=42").capturing a: '~x', b: '42', c: '_' }
444
-
445
- it { should_not match("?a=~x&b=42") }
446
- it { should_not match("a=~x&b=42") }
447
- it { should_not match("?a=~x&b=#42&c") }
448
- it { should_not match("?a=~x,b=42,c=_") }
449
- it { should_not match("~x&b=42&c=_") }
450
- end
451
- end
452
-
453
- context 'operator &' do
454
- pattern '/hello/{&person}' do
455
- it { should match('/hello/&person=Frank') .capturing person: 'Frank' }
456
- it { should match('/hello/&person=a_b~c') .capturing person: 'a_b~c' }
457
- it { should match('/hello/&person') .capturing person: nil }
458
-
459
- it { should_not match('/hello/&persona=Frank') }
460
- it { should_not match('/hello/&persona=a_b~c') }
461
-
462
- it { should_not match('/hello/&person=:') }
463
- it { should_not match('/hello/&person=/') }
464
- it { should_not match('/hello/&person=?') }
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
-
476
- it { should_not match('/hello/&Frank') }
477
- it { should_not match('/hello/&a_b~c') }
478
- it { should_not match('/hello/&a.%20') }
479
-
480
- it { should_not match('/hello/:') }
481
- it { should_not match('/hello//') }
482
- it { should_not match('/hello/?') }
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
- end
494
-
495
- pattern "{&a,b,c}" do
496
- it { should match("&a=~x&b=42&c=_") .capturing a: '~x', b: '42', c: '_' }
497
- it { should match("&a=~x&b&c=_") .capturing a: '~x', b: nil, c: '_' }
498
-
499
- it { should_not match("&a=~x&c=_&b=42").capturing a: '~x', b: '42', c: '_' }
500
-
501
- it { should_not match("&a=~x&b=42") }
502
- it { should_not match("a=~x&b=42") }
503
- it { should_not match("&a=~x&b=#42&c") }
504
- it { should_not match("&a=~x,b=42,c=_") }
505
- it { should_not match("~x&b=42&c=_") }
506
- end
507
- end
508
- end
509
-
510
- context 'level 4' do
511
- context 'without operator' do
512
- context 'prefix' do
513
- pattern '{a:3}/bar' do
514
- it { should match('foo/bar') .capturing a: 'foo' }
515
- it { should match('fo/bar') .capturing a: 'fo' }
516
- it { should match('f/bar') .capturing a: 'f' }
517
- it { should_not match('fooo/bar') }
518
- end
519
-
520
- pattern '{a:3}{b}' do
521
- it { should match('foobar') .capturing a: 'foo', b: 'bar' }
522
- end
523
- end
524
-
525
- context 'expand' do
526
- pattern '{a*}' do
527
- it { should match('a') .capturing a: 'a' }
528
- it { should match('a,b') .capturing a: 'a,b' }
529
- it { should match('a,b,c') .capturing a: 'a,b,c' }
530
- it { should_not match('a,b/c') }
531
- it { should_not match('a,') }
532
-
533
- example { pattern.params('a').should be == { 'a' => ['a'] }}
534
- example { pattern.params('a,b').should be == { 'a' => ['a', 'b'] }}
535
- end
536
-
537
- pattern '{a*},{b}' do
538
- it { should match('a,b') .capturing a: 'a', b: 'b' }
539
- it { should match('a,b,c') .capturing a: 'a,b', b: 'c' }
540
- it { should_not match('a,b/c') }
541
- it { should_not match('a,') }
542
-
543
- example { pattern.params('a,b').should be == { 'a' => ['a'], 'b' => 'b' }}
544
- example { pattern.params('a,b,c').should be == { 'a' => ['a', 'b'], 'b' => 'c' }}
545
- end
546
-
547
- pattern '{a*,b}' do
548
- it { should match('a,b') .capturing a: 'a', b: 'b' }
549
- it { should match('a,b,c') .capturing a: 'a,b', b: 'c' }
550
- it { should_not match('a,b/c') }
551
- it { should_not match('a,') }
552
-
553
- example { pattern.params('a,b').should be == { 'a' => ['a'], 'b' => 'b' }}
554
- example { pattern.params('a,b,c').should be == { 'a' => ['a', 'b'], 'b' => 'c' }}
555
- end
556
- end
557
- end
558
-
559
- context 'operator +' do
560
- context 'prefix' do
561
- pattern '{+a:3}/bar' do
562
- it { should match('foo/bar') .capturing a: 'foo' }
563
- it { should match('fo/bar') .capturing a: 'fo' }
564
- it { should match('f/bar') .capturing a: 'f' }
565
- it { should_not match('fooo/bar') }
566
- end
567
-
568
- pattern '{+a:3}{b}' do
569
- it { should match('foobar') .capturing a: 'foo', b: 'bar' }
570
- end
571
- end
572
-
573
- context 'expand' do
574
- pattern '{+a*}' do
575
- it { should match('a') .capturing a: 'a' }
576
- it { should match('a,b') .capturing a: 'a,b' }
577
- it { should match('a,b,c') .capturing a: 'a,b,c' }
578
- it { should match('a,b/c') .capturing a: 'a,b/c' }
579
- end
580
-
581
- pattern '{+a*},{b}' do
582
- it { should match('a,b') .capturing a: 'a', b: 'b' }
583
- it { should match('a,b,c') .capturing a: 'a,b', b: 'c' }
584
- it { should_not match('a,b/c') }
585
- it { should_not match('a,') }
586
-
587
- example { pattern.params('a,b').should be == { 'a' => ['a'], 'b' => 'b' }}
588
- example { pattern.params('a,b,c').should be == { 'a' => ['a', 'b'], 'b' => 'c' }}
589
- end
590
- end
591
- end
592
-
593
- context 'operator #' do
594
- context 'prefix' do
595
- pattern '{#a:3}/bar' do
596
- it { should match('#foo/bar') .capturing a: 'foo' }
597
- it { should match('#fo/bar') .capturing a: 'fo' }
598
- it { should match('#f/bar') .capturing a: 'f' }
599
- it { should_not match('#fooo/bar') }
600
- end
601
-
602
- pattern '{#a:3}{b}' do
603
- it { should match('#foobar') .capturing a: 'foo', b: 'bar' }
604
- end
605
- end
606
-
607
- context 'expand' do
608
- pattern '{#a*}' do
609
- it { should match('#a') .capturing a: 'a' }
610
- it { should match('#a,b') .capturing a: 'a,b' }
611
- it { should match('#a,b,c') .capturing a: 'a,b,c' }
612
- it { should match('#a,b/c') .capturing a: 'a,b/c' }
613
-
614
- example { pattern.params('#a,b').should be == { 'a' => ['a', 'b'] }}
615
- example { pattern.params('#a,b,c').should be == { 'a' => ['a', 'b', 'c'] }}
616
- end
617
-
618
- pattern '{#a*,b}' do
619
- it { should match('#a,b') .capturing a: 'a', b: 'b' }
620
- it { should match('#a,b,c') .capturing a: 'a,b', b: 'c' }
621
- it { should_not match('#a,') }
622
-
623
- example { pattern.params('#a,b').should be == { 'a' => ['a'], 'b' => 'b' }}
624
- example { pattern.params('#a,b,c').should be == { 'a' => ['a', 'b'], 'b' => 'c' }}
625
- end
626
- end
627
- end
628
-
629
- context 'operator .' do
630
- context 'prefix' do
631
- pattern '{.a:3}/bar' do
632
- it { should match('.foo/bar') .capturing a: 'foo' }
633
- it { should match('.fo/bar') .capturing a: 'fo' }
634
- it { should match('.f/bar') .capturing a: 'f' }
635
- it { should_not match('.fooo/bar') }
636
- end
637
-
638
- pattern '{.a:3}{b}' do
639
- it { should match('.foobar') .capturing a: 'foo', b: 'bar' }
640
- end
641
- end
642
-
643
- context 'expand' do
644
- pattern '{.a*}' do
645
- it { should match('.a') .capturing a: 'a' }
646
- it { should match('.a.b') .capturing a: 'a.b' }
647
- it { should match('.a.b.c') .capturing a: 'a.b.c' }
648
- it { should_not match('.a.b,c') }
649
- it { should_not match('.a,') }
650
- end
651
-
652
- pattern '{.a*,b}' do
653
- it { should match('.a.b') .capturing a: 'a', b: 'b' }
654
- it { should match('.a.b.c') .capturing a: 'a.b', b: 'c' }
655
- it { should_not match('.a.b/c') }
656
- it { should_not match('.a.') }
657
-
658
- example { pattern.params('.a.b').should be == { 'a' => ['a'], 'b' => 'b' }}
659
- example { pattern.params('.a.b.c').should be == { 'a' => ['a', 'b'], 'b' => 'c' }}
660
- end
661
- end
662
- end
663
-
664
- context 'operator /' do
665
- context 'prefix' do
666
- pattern '{/a:3}/bar' do
667
- it { should match('/foo/bar') .capturing a: 'foo' }
668
- it { should match('/fo/bar') .capturing a: 'fo' }
669
- it { should match('/f/bar') .capturing a: 'f' }
670
- it { should_not match('/fooo/bar') }
671
- end
672
-
673
- pattern '{/a:3}{b}' do
674
- it { should match('/foobar') .capturing a: 'foo', b: 'bar' }
675
- end
676
- end
677
-
678
- context 'expand' do
679
- pattern '{/a*}' do
680
- it { should match('/a') .capturing a: 'a' }
681
- it { should match('/a/b') .capturing a: 'a/b' }
682
- it { should match('/a/b/c') .capturing a: 'a/b/c' }
683
- it { should_not match('/a/b,c') }
684
- it { should_not match('/a,') }
685
- end
686
-
687
- pattern '{/a*,b}' do
688
- it { should match('/a/b') .capturing a: 'a', b: 'b' }
689
- it { should match('/a/b/c') .capturing a: 'a/b', b: 'c' }
690
- it { should_not match('/a/b,c') }
691
- it { should_not match('/a/') }
692
-
693
- example { pattern.params('/a/b').should be == { 'a' => ['a'], 'b' => 'b' }}
694
- example { pattern.params('/a/b/c').should be == { 'a' => ['a', 'b'], 'b' => 'c' }}
695
- end
696
- end
697
- end
698
-
699
- context 'operator ;' do
700
- context 'prefix' do
701
- pattern '{;a:3}/bar' do
702
- it { should match(';a=foo/bar') .capturing a: 'foo' }
703
- it { should match(';a=fo/bar') .capturing a: 'fo' }
704
- it { should match(';a=f/bar') .capturing a: 'f' }
705
- it { should_not match(';a=fooo/bar') }
706
- end
707
-
708
- pattern '{;a:3}{b}' do
709
- it { should match(';a=foobar') .capturing a: 'foo', b: 'bar' }
710
- end
711
- end
712
-
713
- context 'expand' do
714
- pattern '{;a*}' do
715
- it { should match(';a=1') .capturing a: 'a=1' }
716
- it { should match(';a=1;a=2') .capturing a: 'a=1;a=2' }
717
- it { should match(';a=1;a=2;a=3') .capturing a: 'a=1;a=2;a=3' }
718
- it { should_not match(';a=1;a=2;b=3') }
719
- it { should_not match(';a=1;a=2;a=3,') }
720
- end
721
-
722
- pattern '{;a*,b}' do
723
- it { should match(';a=1;b') .capturing a: 'a=1', b: nil }
724
- it { should match(';a=2;a=2;b=1') .capturing a: 'a=2;a=2', b: '1' }
725
- it { should_not match(';a;b;c') }
726
- it { should_not match(';a;') }
727
-
728
- example { pattern.params(';a=2;a=2;b').should be == { 'a' => ['2', '2'], 'b' => nil }}
729
- end
730
- end
731
- end
732
-
733
- context 'operator ?' do
734
- context 'prefix' do
735
- pattern '{?a:3}/bar' do
736
- it { should match('?a=foo/bar') .capturing a: 'foo' }
737
- it { should match('?a=fo/bar') .capturing a: 'fo' }
738
- it { should match('?a=f/bar') .capturing a: 'f' }
739
- it { should_not match('?a=fooo/bar') }
740
- end
741
-
742
- pattern '{?a:3}{b}' do
743
- it { should match('?a=foobar') .capturing a: 'foo', b: 'bar' }
744
- end
745
- end
746
-
747
- context 'expand' do
748
- pattern '{?a*}' do
749
- it { should match('?a=1') .capturing a: 'a=1' }
750
- it { should match('?a=1&a=2') .capturing a: 'a=1&a=2' }
751
- it { should match('?a=1&a=2&a=3') .capturing a: 'a=1&a=2&a=3' }
752
- it { should_not match('?a=1&a=2&b=3') }
753
- it { should_not match('?a=1&a=2&a=3,') }
754
- end
755
-
756
- pattern '{?a*,b}' do
757
- it { should match('?a=1&b') .capturing a: 'a=1', b: nil }
758
- it { should match('?a=2&a=2&b=1') .capturing a: 'a=2&a=2', b: '1' }
759
- it { should_not match('?a&b&c') }
760
- it { should_not match('?a&') }
761
-
762
- example { pattern.params('?a=2&a=2&b').should be == { 'a' => ['2', '2'], 'b' => nil }}
763
- end
764
- end
765
- end
766
-
767
- context 'operator &' do
768
- context 'prefix' do
769
- pattern '{&a:3}/bar' do
770
- it { should match('&a=foo/bar') .capturing a: 'foo' }
771
- it { should match('&a=fo/bar') .capturing a: 'fo' }
772
- it { should match('&a=f/bar') .capturing a: 'f' }
773
- it { should_not match('&a=fooo/bar') }
774
- end
775
-
776
- pattern '{&a:3}{b}' do
777
- it { should match('&a=foobar') .capturing a: 'foo', b: 'bar' }
778
- end
779
- end
780
-
781
- context 'expand' do
782
- pattern '{&a*}' do
783
- it { should match('&a=1') .capturing a: 'a=1' }
784
- it { should match('&a=1&a=2') .capturing a: 'a=1&a=2' }
785
- it { should match('&a=1&a=2&a=3') .capturing a: 'a=1&a=2&a=3' }
786
- it { should_not match('&a=1&a=2&b=3') }
787
- it { should_not match('&a=1&a=2&a=3,') }
788
- end
789
-
790
- pattern '{&a*,b}' do
791
- it { should match('&a=1&b') .capturing a: 'a=1', b: nil }
792
- it { should match('&a=2&a=2&b=1') .capturing a: 'a=2&a=2', b: '1' }
793
- it { should_not match('&a&b&c') }
794
- it { should_not match('&a&') }
795
-
796
- example { pattern.params('&a=2&a=2&b').should be == { 'a' => ['2', '2'], 'b' => nil }}
797
- example { pattern.params('&a=2&a=%20&b').should be == { 'a' => ['2', ' '], 'b' => nil }}
798
- end
799
- end
800
- end
801
- end
802
-
803
- context 'invalid syntax' do
804
- example 'unexpected closing bracket' do
805
- expect { Mustermann::Template.new('foo}bar') }.
806
- to raise_error(Mustermann::ParseError, 'unexpected } while parsing "foo}bar"')
807
- end
808
-
809
- example 'missing closing bracket' do
810
- expect { Mustermann::Template.new('foo{bar') }.
811
- to raise_error(Mustermann::ParseError, 'unexpected end of string while parsing "foo{bar"')
812
- end
813
- end
814
- end