mustermann19 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7c18eae7550233eced7217aec1439b9a51737fc1
4
- data.tar.gz: e242e2fc80b76b0fd6842c6e801b69aa79958354
3
+ metadata.gz: b657d781342db968ae5f3aa2870ae038b2dcbe6d
4
+ data.tar.gz: 7e3f3769de07852525cf560cc1b053f31ff90be9
5
5
  SHA512:
6
- metadata.gz: a201dc17c06299c6b7f49749e7ec6add20bea0db6efe5c438044e72209ad0be2f394ac5b59d1eca9b6c9706b73b9dd7dde61c951c37fba8396b5ed30940fc435
7
- data.tar.gz: e60038818b219dcf23c58334df578d0d101ce49f609c0cc93e950f0eb804e38a8cccc9835014cee05c1d91b49bb522d35549923aef32238bff620015b7d70aae
6
+ metadata.gz: 82059db05fce899ea10ec6d729d4411a7d5073052c66dba13710df0f17a7472fc79d0345225f1b760bd9cf16c3d4e32d822502fe8271e2efd78bac449d7acbcc
7
+ data.tar.gz: ddab562925edbfe2f4fe85af92d7e86945077965b6407cce6960796945fad76234b1c158dca4dd06442fd3dc66f805fef532005f8007613d1251dc652832985e
data/README.md CHANGED
@@ -1007,6 +1007,17 @@ This comes with a few trade-offs:
1007
1007
  [`uri_decode`](#-available-options--uri_decode),
1008
1008
  [`ignore_unknown_options`](#-available-options--ignore_unknown_options).
1009
1009
 
1010
+ <a name="-pattern-details-grape"></a>
1011
+ ### `grape`
1012
+
1013
+ **Supported options:**
1014
+ [`capture`](#-available-options--capture),
1015
+ [`except`](#-available-options--except),
1016
+ [`greedy`](#-available-options--greedy),
1017
+ [`space_matches_plus`](#-available-options--space_matches_plus),
1018
+ [`uri_decode`](#-available-options--uri_decode),
1019
+ [`ignore_unknown_options`](#-available-options--ignore_unknown_options).
1020
+
1010
1021
  <a name="-pattern-details-rails"></a>
1011
1022
  ### `rails`
1012
1023
 
@@ -0,0 +1,34 @@
1
+ require 'mustermann/ast/pattern'
2
+
3
+ module Mustermann
4
+ # Grape style pattern implementation.
5
+ #
6
+ # @example
7
+ # Mustermann.new('/:foo', type: :grape) === '/bar' # => true
8
+ #
9
+ # @see Mustermann::Pattern
10
+ # @see file:README.md#grape Syntax description in the README
11
+ class Grape < AST::Pattern
12
+ register :grape
13
+
14
+ on(nil, ??, ?)) { |c| unexpected(c) }
15
+
16
+ on(?*) { |c| scan(/\w+/) ? node(:named_splat, buffer.matched) : node(:splat) }
17
+ on(?:) { |c| node(:capture) { scan(/\w+/) } }
18
+ on(?\\) { |c| node(:char, expect(/./)) }
19
+ on(?() { |c| node(:optional, node(:group) { read unless scan(?)) }) }
20
+ on(?|) { |c| node(:or) }
21
+
22
+ on ?{ do |char|
23
+ type = scan(?+) ? :named_splat : :capture
24
+ name = expect(/[\w\.]+/)
25
+ type = :splat if type == :named_splat and name == 'splat'
26
+ expect(?})
27
+ node(type, name)
28
+ end
29
+
30
+ suffix ?? do |char, element|
31
+ node(:optional, element)
32
+ end
33
+ end
34
+ end
@@ -1,3 +1,3 @@
1
1
  module Mustermann
2
- VERSION ||= '0.4.0'
2
+ VERSION ||= '0.4.1'
3
3
  end
@@ -0,0 +1,747 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'support'
3
+ require 'mustermann/grape'
4
+
5
+ describe Mustermann::Grape do
6
+ extend Support::Pattern
7
+
8
+ pattern '' do
9
+ it { should match('') }
10
+ it { should_not match('/') }
11
+
12
+ it { should generate_template('') }
13
+
14
+ it { should respond_to(:expand) }
15
+ it { should respond_to(:to_templates) }
16
+ end
17
+
18
+ pattern '/' do
19
+ it { should match('/') }
20
+ it { should_not match('/foo') }
21
+ end
22
+
23
+ pattern '/foo' do
24
+ it { should match('/foo') }
25
+ it { should_not match('/bar') }
26
+ it { should_not match('/foo.bar') }
27
+ end
28
+
29
+ pattern '/foo/bar' do
30
+ it { should match('/foo/bar') }
31
+ it { should_not match('/foo%2Fbar') }
32
+ it { should_not match('/foo%2fbar') }
33
+ end
34
+
35
+ pattern '/foo\/bar' do
36
+ it { should match('/foo/bar') }
37
+ it { should match('/foo%2Fbar') }
38
+ it { should match('/foo%2fbar') }
39
+ end
40
+
41
+ pattern '/:foo' do
42
+ it { should match('/foo') .capturing foo: 'foo' }
43
+ it { should match('/bar') .capturing foo: 'bar' }
44
+ it { should match('/foo.bar') .capturing foo: 'foo.bar' }
45
+ it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
46
+ it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
47
+
48
+ it { should_not match('/foo?') }
49
+ it { should_not match('/foo/bar') }
50
+ it { should_not match('/') }
51
+ it { should_not match('/foo/') }
52
+
53
+ it { should generate_template('/{foo}') }
54
+ end
55
+
56
+ pattern '/föö' do
57
+ it { should match("/f%C3%B6%C3%B6") }
58
+ end
59
+
60
+ pattern "/:foo/:bar" do
61
+ it { should match('/foo/bar') .capturing foo: 'foo', bar: 'bar' }
62
+ it { should match('/foo.bar/bar.foo') .capturing foo: 'foo.bar', bar: 'bar.foo' }
63
+ it { should match('/user@example.com/name') .capturing foo: 'user@example.com', bar: 'name' }
64
+ it { should match('/10.1/te.st') .capturing foo: '10.1', bar: 'te.st' }
65
+ it { should match('/10.1.2/te.st') .capturing foo: '10.1.2', bar: 'te.st' }
66
+
67
+ it { should_not match('/foo%2Fbar') }
68
+ it { should_not match('/foo%2fbar') }
69
+
70
+ example { pattern.params('/bar/foo').should be == {"foo" => "bar", "bar" => "foo"} }
71
+ example { pattern.params('').should be_nil }
72
+
73
+ it { should generate_template('/{foo}/{bar}') }
74
+ end
75
+
76
+ pattern "/{foo}/{bar}" do
77
+ it { should match('/foo/bar') .capturing foo: 'foo', bar: 'bar' }
78
+ it { should match('/foo.bar/bar.foo') .capturing foo: 'foo.bar', bar: 'bar.foo' }
79
+ it { should match('/user@example.com/name') .capturing foo: 'user@example.com', bar: 'name' }
80
+ it { should match('/10.1/te.st') .capturing foo: '10.1', bar: 'te.st' }
81
+ it { should match('/10.1.2/te.st') .capturing foo: '10.1.2', bar: 'te.st' }
82
+
83
+ it { should_not match('/foo%2Fbar') }
84
+ it { should_not match('/foo%2fbar') }
85
+
86
+ example { pattern.params('/bar/foo').should be == {"foo" => "bar", "bar" => "foo"} }
87
+ example { pattern.params('').should be_nil }
88
+
89
+ it { should generate_template('/{foo}/{bar}') }
90
+ end
91
+
92
+ pattern '/hello/:person' do
93
+ it { should match('/hello/Frank').capturing person: 'Frank' }
94
+ it { should generate_template('/hello/{person}') }
95
+ end
96
+
97
+ pattern '/hello/{person}' do
98
+ it { should match('/hello/Frank').capturing person: 'Frank' }
99
+ it { should generate_template('/hello/{person}') }
100
+ end
101
+
102
+ pattern '/?:foo?/?:bar?' do
103
+ it { should match('/hello/world') .capturing foo: 'hello', bar: 'world' }
104
+ it { should match('/hello') .capturing foo: 'hello', bar: nil }
105
+ it { should match('/') .capturing foo: nil, bar: nil }
106
+ it { should match('') .capturing foo: nil, bar: nil }
107
+
108
+ it { should expand(foo: 'hello') .to('/hello/') }
109
+ it { should expand(foo: 'hello', bar: 'world') .to('/hello/world') }
110
+ it { should expand(bar: 'world') .to('//world') }
111
+ it { should expand .to('//') }
112
+ it { should_not expand(baz: '') }
113
+
114
+ it { should_not match('/hello/world/') }
115
+ it { should generate_templates("", "/", "//", "//{bar}", "/{bar}", "/{foo}", "/{foo}/", "/{foo}/{bar}", "/{foo}{bar}", "{bar}", "{foo}", "{foo}/", "{foo}/{bar}", "{foo}{bar}") }
116
+ end
117
+
118
+ pattern '/:foo_bar' do
119
+ it { should match('/hello').capturing foo_bar: 'hello' }
120
+ it { should generate_template('/{foo_bar}') }
121
+ end
122
+
123
+ pattern '/{foo.bar}' do
124
+ it { should match('/hello').capturing :"foo.bar" => 'hello' }
125
+ it { should generate_template('/{foo.bar}') }
126
+ end
127
+
128
+ pattern '/*' do
129
+ it { should match('/') .capturing splat: '' }
130
+ it { should match('/foo') .capturing splat: 'foo' }
131
+ it { should match('/foo/bar') .capturing splat: 'foo/bar' }
132
+ it { should generate_template('/{+splat}') }
133
+
134
+ example { pattern.params('/foo').should be == {"splat" => ["foo"]} }
135
+ end
136
+
137
+ pattern '/{+splat}' do
138
+ it { should match('/') .capturing splat: '' }
139
+ it { should match('/foo') .capturing splat: 'foo' }
140
+ it { should match('/foo/bar') .capturing splat: 'foo/bar' }
141
+ it { should generate_template('/{+splat}') }
142
+
143
+ example { pattern.params('/foo').should be == {"splat" => ["foo"]} }
144
+ end
145
+
146
+ pattern '/*foo' do
147
+ it { should match('/') .capturing foo: '' }
148
+ it { should match('/foo') .capturing foo: 'foo' }
149
+ it { should match('/foo/bar') .capturing foo: 'foo/bar' }
150
+ it { should generate_template('/{+foo}') }
151
+
152
+ example { pattern.params('/foo') .should be == {"foo" => "foo" } }
153
+ example { pattern.params('/foo/bar') .should be == {"foo" => "foo/bar" } }
154
+ end
155
+
156
+ pattern '/{+foo}' do
157
+ it { should match('/') .capturing foo: '' }
158
+ it { should match('/foo') .capturing foo: 'foo' }
159
+ it { should match('/foo/bar') .capturing foo: 'foo/bar' }
160
+ it { should generate_template('/{+foo}') }
161
+
162
+ example { pattern.params('/foo') .should be == {"foo" => "foo" } }
163
+ example { pattern.params('/foo/bar') .should be == {"foo" => "foo/bar" } }
164
+ end
165
+
166
+ pattern '/*foo/*bar' do
167
+ it { should match('/foo/bar') .capturing foo: 'foo', bar: 'bar' }
168
+ it { should generate_template('/{+foo}/{+bar}') }
169
+ end
170
+
171
+ pattern '/{+foo}/{+bar}' do
172
+ it { should match('/foo/bar') .capturing foo: 'foo', bar: 'bar' }
173
+ it { should generate_template('/{+foo}/{+bar}') }
174
+ end
175
+
176
+ pattern '/:foo/*' do
177
+ it { should match("/foo/bar/baz") .capturing foo: 'foo', splat: 'bar/baz' }
178
+ it { should match("/foo/") .capturing foo: 'foo', splat: '' }
179
+ it { should match('/h%20w/h%20a%20y') .capturing foo: 'h%20w', splat: 'h%20a%20y' }
180
+ it { should_not match('/foo') }
181
+ it { should generate_template('/{foo}/{+splat}') }
182
+
183
+ example { pattern.params('/bar/foo').should be == {"splat" => ["foo"], "foo" => "bar"} }
184
+ example { pattern.params('/bar/foo/f%20o').should be == {"splat" => ["foo/f o"], "foo" => "bar"} }
185
+ end
186
+
187
+ pattern '/{foo}/*' do
188
+ it { should match("/foo/bar/baz") .capturing foo: 'foo', splat: 'bar/baz' }
189
+ it { should match("/foo/") .capturing foo: 'foo', splat: '' }
190
+ it { should match('/h%20w/h%20a%20y') .capturing foo: 'h%20w', splat: 'h%20a%20y' }
191
+ it { should_not match('/foo') }
192
+ it { should generate_template('/{foo}/{+splat}') }
193
+
194
+ example { pattern.params('/bar/foo').should be == {"splat" => ["foo"], "foo" => "bar"} }
195
+ example { pattern.params('/bar/foo/f%20o').should be == {"splat" => ["foo/f o"], "foo" => "bar"} }
196
+ end
197
+
198
+ pattern '/test$/' do
199
+ it { should match('/test$/') }
200
+ end
201
+
202
+ pattern '/te+st/' do
203
+ it { should match('/te+st/') }
204
+ it { should_not match('/test/') }
205
+ it { should_not match('/teest/') }
206
+ end
207
+
208
+ pattern "/path with spaces" do
209
+ it { should match('/path%20with%20spaces') }
210
+ it { should match('/path%2Bwith%2Bspaces') }
211
+ it { should match('/path+with+spaces') }
212
+
213
+ it { should generate_template('/path%20with%20spaces') }
214
+ end
215
+
216
+ pattern '/foo&bar' do
217
+ it { should match('/foo&bar') }
218
+ end
219
+
220
+ pattern '/foo\{bar' do
221
+ it { should match('/foo%7Bbar') }
222
+ end
223
+
224
+ pattern '/*/:foo/*/*' do
225
+ it { should match('/bar/foo/bling/baz/boom') }
226
+
227
+ it "should capture all splat parts" do
228
+ match = pattern.match('/bar/foo/bling/baz/boom')
229
+ match.captures.should be == ['bar', 'foo', 'bling', 'baz/boom']
230
+ match.names.should be == ['splat', 'foo']
231
+ end
232
+
233
+ it 'should map to proper params' do
234
+ pattern.params('/bar/foo/bling/baz/boom').should be == {
235
+ "foo" => "foo", "splat" => ['bar', 'bling', 'baz/boom']
236
+ }
237
+ end
238
+ end
239
+
240
+ pattern '/{+splat}/{foo}/{+splat}/{+splat}' do
241
+ it { should match('/bar/foo/bling/baz/boom') }
242
+
243
+ it "should capture all splat parts" do
244
+ match = pattern.match('/bar/foo/bling/baz/boom')
245
+ match.captures.should be == ['bar', 'foo', 'bling', 'baz/boom']
246
+ match.names.should be == ['splat', 'foo']
247
+ end
248
+
249
+ it 'should map to proper params' do
250
+ pattern.params('/bar/foo/bling/baz/boom').should be == {
251
+ "foo" => "foo", "splat" => ['bar', 'bling', 'baz/boom']
252
+ }
253
+ end
254
+ end
255
+
256
+ pattern '/test.bar' do
257
+ it { should match('/test.bar') }
258
+ it { should_not match('/test0bar') }
259
+ end
260
+
261
+ pattern '/:file.:ext' do
262
+ it { should match('/pony.jpg') .capturing file: 'pony', ext: 'jpg' }
263
+ it { should match('/pony%2Ejpg') .capturing file: 'pony', ext: 'jpg' }
264
+ it { should match('/pony%2ejpg') .capturing file: 'pony', ext: 'jpg' }
265
+
266
+ it { should match('/pony%E6%AD%A3%2Ejpg') .capturing file: 'pony%E6%AD%A3', ext: 'jpg' }
267
+ it { should match('/pony%e6%ad%a3%2ejpg') .capturing file: 'pony%e6%ad%a3', ext: 'jpg' }
268
+ it { should match('/pony正%2Ejpg') .capturing file: 'pony正', ext: 'jpg' }
269
+ it { should match('/pony正%2ejpg') .capturing file: 'pony正', ext: 'jpg' }
270
+ it { should match('/pony正..jpg') .capturing file: 'pony正.', ext: 'jpg' }
271
+
272
+ it { should_not match('/.jpg') }
273
+ end
274
+
275
+ pattern '/(:a)x?' do
276
+ it { should match('/a') .capturing a: 'a' }
277
+ it { should match('/xa') .capturing a: 'xa' }
278
+ it { should match('/axa') .capturing a: 'axa' }
279
+
280
+ it { should generate_template('/{a}x') }
281
+ it { should generate_template('/{a}') }
282
+ end
283
+
284
+ pattern '/:user(@:host)?' do
285
+ it { should match('/foo@bar') .capturing user: 'foo', host: 'bar' }
286
+ it { should match('/foo.foo@bar') .capturing user: 'foo.foo', host: 'bar' }
287
+ it { should match('/foo@bar.bar') .capturing user: 'foo', host: 'bar.bar' }
288
+
289
+ it { should generate_template('/{user}') }
290
+ it { should generate_template('/{user}@{host}') }
291
+ end
292
+
293
+ pattern '/:file(.:ext)?' do
294
+ it { should match('/pony') .capturing file: 'pony', ext: nil }
295
+ it { should match('/pony.jpg') .capturing file: 'pony', ext: 'jpg' }
296
+ it { should match('/pony%2Ejpg') .capturing file: 'pony', ext: 'jpg' }
297
+ it { should match('/pony%2ejpg') .capturing file: 'pony', ext: 'jpg' }
298
+ it { should match('/pony.png.jpg') .capturing file: 'pony.png', ext: 'jpg' }
299
+ it { should match('/pony.') .capturing file: 'pony.' }
300
+ it { should_not match('/.jpg') }
301
+
302
+ it { should generate_template('/{file}') }
303
+ it { should generate_template('/{file}.{ext}') }
304
+ it { should_not generate_template('/{file}.') }
305
+ end
306
+
307
+ pattern '/:id/test.bar' do
308
+ it { should match('/3/test.bar') .capturing id: '3' }
309
+ it { should match('/2/test.bar') .capturing id: '2' }
310
+ it { should match('/2E/test.bar') .capturing id: '2E' }
311
+ it { should match('/2e/test.bar') .capturing id: '2e' }
312
+ it { should match('/%2E/test.bar') .capturing id: '%2E' }
313
+ end
314
+
315
+ pattern '/10/:id' do
316
+ it { should match('/10/test') .capturing id: 'test' }
317
+ it { should match('/10/te.st') .capturing id: 'te.st' }
318
+ end
319
+
320
+ pattern '/10.1/:id' do
321
+ it { should match('/10.1/test') .capturing id: 'test' }
322
+ it { should match('/10.1/te.st') .capturing id: 'te.st' }
323
+ end
324
+
325
+ pattern '/:foo.:bar/:id' do
326
+ it { should match('/10.1/te.st') .capturing foo: "10", bar: "1", id: "te.st" }
327
+ it { should match('/10.1.2/te.st') .capturing foo: "10.1", bar: "2", id: "te.st" }
328
+ end
329
+
330
+ pattern '/:a/:b.?:c?' do
331
+ it { should match('/a/b') .capturing a: 'a', b: 'b', c: nil }
332
+ it { should match('/a/b.c') .capturing a: 'a', b: 'b', c: 'c' }
333
+ it { should match('/a.b/c') .capturing a: 'a.b', b: 'c', c: nil }
334
+ it { should match('/a.b/c.d') .capturing a: 'a.b', b: 'c', c: 'd' }
335
+ it { should_not match('/a.b/c.d/e') }
336
+ end
337
+
338
+ pattern '/:a(foo:b)?' do
339
+ it { should match('/barfoobar') .capturing a: 'bar', b: 'bar' }
340
+ it { should match('/barfoobarfoobar') .capturing a: 'barfoobar', b: 'bar' }
341
+ it { should match('/bar') .capturing a: 'bar', b: nil }
342
+ it { should_not match('/') }
343
+ end
344
+
345
+ pattern '/foo?' do
346
+ it { should match('/fo') }
347
+ it { should match('/foo') }
348
+ it { should_not match('') }
349
+ it { should_not match('/') }
350
+ it { should_not match('/f') }
351
+ it { should_not match('/fooo') }
352
+ end
353
+
354
+ pattern '/foo\?' do
355
+ it { should match('/foo?') }
356
+ it { should_not match('/foo\?') }
357
+ it { should_not match('/fo') }
358
+ it { should_not match('/foo') }
359
+ it { should_not match('') }
360
+ it { should_not match('/') }
361
+ it { should_not match('/f') }
362
+ it { should_not match('/fooo') }
363
+ end
364
+
365
+ pattern '/foo\\\?' do
366
+ it { should match('/foo%5c') }
367
+ it { should match('/foo') }
368
+ it { should_not match('/foo\?') }
369
+ it { should_not match('/fo') }
370
+ it { should_not match('') }
371
+ it { should_not match('/') }
372
+ it { should_not match('/f') }
373
+ it { should_not match('/fooo') }
374
+ end
375
+
376
+ pattern '/\(' do
377
+ it { should match('/(') }
378
+ end
379
+
380
+ pattern '/\(?' do
381
+ it { should match('/(') }
382
+ it { should match('/') }
383
+ end
384
+
385
+ pattern '/(\()?' do
386
+ it { should match('/(') }
387
+ it { should match('/') }
388
+ end
389
+
390
+ pattern '/(\(\))?' do
391
+ it { should match('/') }
392
+ it { should match('/()') }
393
+ it { should_not match('/(') }
394
+ end
395
+
396
+ pattern '/\(\)?' do
397
+ it { should match('/(') }
398
+ it { should match('/()') }
399
+ it { should_not match('/') }
400
+ end
401
+
402
+ pattern '/\*' do
403
+ it { should match('/*') }
404
+ it { should_not match('/a') }
405
+ end
406
+
407
+ pattern '/\*/*' do
408
+ it { should match('/*/b/c') }
409
+ it { should_not match('/a/b/c') }
410
+ end
411
+
412
+ pattern '/\:foo' do
413
+ it { should match('/:foo') }
414
+ it { should_not match('/foo') }
415
+ end
416
+
417
+ pattern '/:fOO' do
418
+ it { should match('/a').capturing fOO: 'a' }
419
+ end
420
+
421
+ pattern '/:_X' do
422
+ it { should match('/a').capturing _X: 'a' }
423
+ end
424
+
425
+ pattern '/:f00' do
426
+ it { should match('/a').capturing f00: 'a' }
427
+ end
428
+
429
+ pattern '/:foo(/:bar)?/:baz?' do
430
+ it { should match('/foo/bar/baz').capturing foo: 'foo', bar: 'bar', baz: 'baz' }
431
+ end
432
+
433
+ pattern "/(foo|bar)" do
434
+ it { should match("/foo") }
435
+ it { should match("/bar") }
436
+ end
437
+
438
+ pattern "/(foo\\|bar)" do
439
+ it { should match "/foo%7Cbar" }
440
+ it { should generate_template "/foo%7Cbar" }
441
+
442
+ it { should_not match("/foo") }
443
+ it { should_not match("/bar") }
444
+
445
+ it { should_not generate_template('/foo') }
446
+ it { should_not generate_template('/bar') }
447
+ end
448
+
449
+ pattern "/(:a/:b|:c)" do
450
+ it { should match("/foo") .capturing c: 'foo' }
451
+ it { should match("/foo/bar") .capturing a: 'foo', b: 'bar' }
452
+
453
+ it { should expand(a: 'foo', b: 'bar') .to('/foo/bar') }
454
+ it { should expand(c: 'foo') .to('/foo') }
455
+ it { should_not expand(a: 'foo', b: 'bar', c: 'baz') }
456
+ end
457
+
458
+ pattern "/:a/:b|:c" do
459
+ it { should match("foo") .capturing c: 'foo' }
460
+ it { should match("/foo/bar") .capturing a: 'foo', b: 'bar' }
461
+
462
+ it { should generate_template('/{a}/{b}') }
463
+ it { should generate_template('{c}') }
464
+
465
+ it { should expand(a: 'foo', b: 'bar') .to('/foo/bar') }
466
+ it { should expand(c: 'foo') .to('foo') }
467
+ it { should_not expand(a: 'foo', b: 'bar', c: 'baz') }
468
+ end
469
+
470
+ pattern '/:foo', capture: /\d+/ do
471
+ it { should match('/1') .capturing foo: '1' }
472
+ it { should match('/123') .capturing foo: '123' }
473
+
474
+ it { should_not match('/') }
475
+ it { should_not match('/foo') }
476
+ end
477
+
478
+ pattern '/:foo', capture: /\d+/ do
479
+ it { should match('/1') .capturing foo: '1' }
480
+ it { should match('/123') .capturing foo: '123' }
481
+
482
+ it { should_not match('/') }
483
+ it { should_not match('/foo') }
484
+ end
485
+
486
+ pattern '/:foo', capture: '1' do
487
+ it { should match('/1').capturing foo: '1' }
488
+
489
+ it { should_not match('/') }
490
+ it { should_not match('/foo') }
491
+ it { should_not match('/123') }
492
+ end
493
+
494
+ pattern '/:foo', capture: 'a.b' do
495
+ it { should match('/a.b') .capturing foo: 'a.b' }
496
+ it { should match('/a%2Eb') .capturing foo: 'a%2Eb' }
497
+ it { should match('/a%2eb') .capturing foo: 'a%2eb' }
498
+
499
+ it { should_not match('/ab') }
500
+ it { should_not match('/afb') }
501
+ it { should_not match('/a1b') }
502
+ it { should_not match('/a.bc') }
503
+ end
504
+
505
+ pattern '/:foo(/:bar)?', capture: :alpha do
506
+ it { should match('/abc') .capturing foo: 'abc', bar: nil }
507
+ it { should match('/a/b') .capturing foo: 'a', bar: 'b' }
508
+ it { should match('/a') .capturing foo: 'a', bar: nil }
509
+
510
+ it { should_not match('/1/2') }
511
+ it { should_not match('/a/2') }
512
+ it { should_not match('/1/b') }
513
+ it { should_not match('/1') }
514
+ it { should_not match('/1/') }
515
+ it { should_not match('/a/') }
516
+ it { should_not match('//a') }
517
+ end
518
+
519
+ pattern '/:foo', capture: ['foo', 'bar', /\d+/] do
520
+ it { should match('/1') .capturing foo: '1' }
521
+ it { should match('/123') .capturing foo: '123' }
522
+ it { should match('/foo') .capturing foo: 'foo' }
523
+ it { should match('/bar') .capturing foo: 'bar' }
524
+
525
+ it { should_not match('/') }
526
+ it { should_not match('/baz') }
527
+ it { should_not match('/foo1') }
528
+ end
529
+
530
+ pattern '/:foo:bar:baz', capture: { foo: :alpha, bar: /\d+/ } do
531
+ it { should match('/ab123xy-1') .capturing foo: 'ab', bar: '123', baz: 'xy-1' }
532
+ it { should match('/ab123') .capturing foo: 'ab', bar: '12', baz: '3' }
533
+ it { should_not match('/123abcxy-1') }
534
+ it { should_not match('/abcxy-1') }
535
+ it { should_not match('/abc1') }
536
+ end
537
+
538
+ pattern '/:foo', capture: { foo: ['foo', 'bar', /\d+/] } do
539
+ it { should match('/1') .capturing foo: '1' }
540
+ it { should match('/123') .capturing foo: '123' }
541
+ it { should match('/foo') .capturing foo: 'foo' }
542
+ it { should match('/bar') .capturing foo: 'bar' }
543
+
544
+ it { should_not match('/') }
545
+ it { should_not match('/baz') }
546
+ it { should_not match('/foo1') }
547
+ end
548
+
549
+ pattern '/:file(.:ext)?', capture: { ext: ['jpg', 'png'] } do
550
+ it { should match('/pony') .capturing file: 'pony', ext: nil }
551
+ it { should match('/pony.jpg') .capturing file: 'pony', ext: 'jpg' }
552
+ it { should match('/pony%2Ejpg') .capturing file: 'pony', ext: 'jpg' }
553
+ it { should match('/pony%2ejpg') .capturing file: 'pony', ext: 'jpg' }
554
+ it { should match('/pony.png') .capturing file: 'pony', ext: 'png' }
555
+ it { should match('/pony%2Epng') .capturing file: 'pony', ext: 'png' }
556
+ it { should match('/pony%2epng') .capturing file: 'pony', ext: 'png' }
557
+ it { should match('/pony.png.jpg') .capturing file: 'pony.png', ext: 'jpg' }
558
+ it { should match('/pony.jpg.png') .capturing file: 'pony.jpg', ext: 'png' }
559
+ it { should match('/pony.gif') .capturing file: 'pony.gif', ext: nil }
560
+ it { should match('/pony.') .capturing file: 'pony.', ext: nil }
561
+ it { should_not match('.jpg') }
562
+ end
563
+
564
+ pattern '/:file:ext?', capture: { ext: ['.jpg', '.png', '.tar.gz'] } do
565
+ it { should match('/pony') .capturing file: 'pony', ext: nil }
566
+ it { should match('/pony.jpg') .capturing file: 'pony', ext: '.jpg' }
567
+ it { should match('/pony.png') .capturing file: 'pony', ext: '.png' }
568
+ it { should match('/pony.png.jpg') .capturing file: 'pony.png', ext: '.jpg' }
569
+ it { should match('/pony.jpg.png') .capturing file: 'pony.jpg', ext: '.png' }
570
+ it { should match('/pony.tar.gz') .capturing file: 'pony', ext: '.tar.gz' }
571
+ it { should match('/pony.gif') .capturing file: 'pony.gif', ext: nil }
572
+ it { should match('/pony.') .capturing file: 'pony.', ext: nil }
573
+ it { should_not match('/.jpg') }
574
+ end
575
+
576
+ pattern '/:a(@:b)?', capture: { b: /\d+/ } do
577
+ it { should match('/a') .capturing a: 'a', b: nil }
578
+ it { should match('/a@1') .capturing a: 'a', b: '1' }
579
+ it { should match('/a@b') .capturing a: 'a@b', b: nil }
580
+ it { should match('/a@1@2') .capturing a: 'a@1', b: '2' }
581
+ end
582
+
583
+ pattern '/(:a)b?', greedy: false do
584
+ it { should match('/ab').capturing a: 'a' }
585
+ end
586
+
587
+ pattern '/:file(.:ext)?', greedy: false do
588
+ it { should match('/pony') .capturing file: 'pony', ext: nil }
589
+ it { should match('/pony.jpg') .capturing file: 'pony', ext: 'jpg' }
590
+ it { should match('/pony.png.jpg') .capturing file: 'pony', ext: 'png.jpg' }
591
+ end
592
+
593
+ pattern '/auth/*', except: '/auth/login' do
594
+ it { should match('/auth/admin') }
595
+ it { should match('/auth/foobar') }
596
+ it { should_not match('/auth/login') }
597
+ end
598
+
599
+ pattern '/:foo/:bar', except: '/:bar/20' do
600
+ it { should match('/foo/bar').capturing foo: 'foo', bar: 'bar' }
601
+ it { should_not match('/20/20') }
602
+ end
603
+
604
+ pattern '/foo?', uri_decode: false do
605
+ it { should match('/foo') }
606
+ it { should match('/fo') }
607
+ it { should_not match('/foo?') }
608
+ end
609
+
610
+ pattern '/foo/bar', uri_decode: false do
611
+ it { should match('/foo/bar') }
612
+ it { should_not match('/foo%2Fbar') }
613
+ it { should_not match('/foo%2fbar') }
614
+ end
615
+
616
+ pattern "/path with spaces", uri_decode: false do
617
+ it { should match('/path with spaces') }
618
+ it { should_not match('/path%20with%20spaces') }
619
+ it { should_not match('/path%2Bwith%2Bspaces') }
620
+ it { should_not match('/path+with+spaces') }
621
+ end
622
+
623
+ pattern "/path with spaces", space_matches_plus: false do
624
+ it { should match('/path%20with%20spaces') }
625
+ it { should_not match('/path%2Bwith%2Bspaces') }
626
+ it { should_not match('/path+with+spaces') }
627
+ end
628
+
629
+ context 'invalid syntax' do
630
+ example 'unexpected closing parenthesis' do
631
+ expect { Mustermann::Grape.new('foo)bar') }.
632
+ to raise_error(Mustermann::ParseError, 'unexpected ) while parsing "foo)bar"')
633
+ end
634
+
635
+ example 'missing closing parenthesis' do
636
+ expect { Mustermann::Grape.new('foo(bar') }.
637
+ to raise_error(Mustermann::ParseError, 'unexpected end of string while parsing "foo(bar"')
638
+ end
639
+
640
+ example 'missing unescaped closing parenthesis' do
641
+ expect { Mustermann::Grape.new('foo(bar\)') }.
642
+ to raise_error(Mustermann::ParseError, 'unexpected end of string while parsing "foo(bar\\\\)"')
643
+ end
644
+
645
+ example '? at beginning of route' do
646
+ expect { Mustermann::Grape.new('?foobar') }.
647
+ to raise_error(Mustermann::ParseError, 'unexpected ? while parsing "?foobar"')
648
+ end
649
+
650
+ example 'double ?' do
651
+ expect { Mustermann::Grape.new('foo??bar') }.
652
+ to raise_error(Mustermann::ParseError, 'unexpected ? while parsing "foo??bar"')
653
+ end
654
+
655
+ example 'dangling escape' do
656
+ expect { Mustermann::Grape.new('foo\\') }.
657
+ to raise_error(Mustermann::ParseError, 'unexpected end of string while parsing "foo\\\\"')
658
+ end
659
+ end
660
+
661
+ context 'invalid capture names' do
662
+ example 'empty name' do
663
+ expect { Mustermann::Grape.new('/:/') }.
664
+ to raise_error(Mustermann::CompileError, "capture name can't be empty: \"/:/\"")
665
+ end
666
+
667
+ example 'named splat' do
668
+ expect { Mustermann::Grape.new('/:splat/') }.
669
+ to raise_error(Mustermann::CompileError, "capture name can't be splat: \"/:splat/\"")
670
+ end
671
+
672
+ example 'named captures' do
673
+ expect { Mustermann::Grape.new('/:captures/') }.
674
+ to raise_error(Mustermann::CompileError, "capture name can't be captures: \"/:captures/\"")
675
+ end
676
+
677
+ example 'with capital letter' do
678
+ expect { Mustermann::Grape.new('/:Foo/') }.
679
+ to raise_error(Mustermann::CompileError, "capture name must start with underscore or lower case letter: \"/:Foo/\"")
680
+ end
681
+
682
+ example 'with integer' do
683
+ expect { Mustermann::Grape.new('/:1a/') }.
684
+ to raise_error(Mustermann::CompileError, "capture name must start with underscore or lower case letter: \"/:1a/\"")
685
+ end
686
+
687
+ example 'same name twice' do
688
+ expect { Mustermann::Grape.new('/:foo(/:bar)?/:bar?') }.
689
+ to raise_error(Mustermann::CompileError, "can't use the same capture name twice: \"/:foo(/:bar)?/:bar?\"")
690
+ end
691
+ end
692
+
693
+ context 'Regexp compatibility' do
694
+ describe :=== do
695
+ example('non-matching') { Mustermann::Grape.new("/") .should_not be === '/foo' }
696
+ example('matching') { Mustermann::Grape.new("/:foo") .should be === '/foo' }
697
+ end
698
+
699
+ describe :=~ do
700
+ example('non-matching') { Mustermann::Grape.new("/") .should_not be =~ '/foo' }
701
+ example('matching') { Mustermann::Grape.new("/:foo") .should be =~ '/foo' }
702
+
703
+ context 'String#=~' do
704
+ example('non-matching') { "/foo".should_not be =~ Mustermann::Grape.new("/") }
705
+ example('matching') { "/foo".should be =~ Mustermann::Grape.new("/:foo") }
706
+ end
707
+ end
708
+
709
+ describe :to_regexp do
710
+ example('empty pattern') { Mustermann::Grape.new('').to_regexp.should be == /\A(?-mix:)\Z/ }
711
+
712
+ context 'Regexp.try_convert' do
713
+ example('empty pattern') { Regexp.try_convert(Mustermann::Grape.new('')).should be == /\A(?-mix:)\Z/ }
714
+ end
715
+ end
716
+ end
717
+
718
+ context 'Proc compatibility' do
719
+ describe :to_proc do
720
+ example { Mustermann::Grape.new("/").to_proc.should be_a(Proc) }
721
+ example('non-matching') { Mustermann::Grape.new("/") .to_proc.call('/foo').should be == false }
722
+ example('matching') { Mustermann::Grape.new("/:foo") .to_proc.call('/foo').should be == true }
723
+ end
724
+ end
725
+
726
+ context "peeking" do
727
+ subject(:pattern) { Mustermann::Grape.new(":name") }
728
+
729
+ describe :peek_size do
730
+ example { pattern.peek_size("foo bar/blah") .should be == "foo bar".size }
731
+ example { pattern.peek_size("foo%20bar/blah") .should be == "foo%20bar".size }
732
+ example { pattern.peek_size("/foo bar") .should be_nil }
733
+ end
734
+
735
+ describe :peek_match do
736
+ example { pattern.peek_match("foo bar/blah") .to_s .should be == "foo bar" }
737
+ example { pattern.peek_match("foo%20bar/blah") .to_s .should be == "foo%20bar" }
738
+ example { pattern.peek_match("/foo bar") .should be_nil }
739
+ end
740
+
741
+ describe :peek_params do
742
+ example { pattern.peek_params("foo bar/blah") .should be == [{"name" => "foo bar"}, "foo bar".size] }
743
+ example { pattern.peek_params("foo%20bar/blah") .should be == [{"name" => "foo bar"}, "foo%20bar".size] }
744
+ example { pattern.peek_params("/foo bar") .should be_nil }
745
+ end
746
+ end
747
+ end
metadata CHANGED
@@ -1,167 +1,168 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mustermann19
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Konstantin Haase
8
8
  - namusyaka
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-11-29 00:00:00.000000000 Z
12
+ date: 2016-02-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
+ name: enumerable-lazy
15
16
  requirement: !ruby/object:Gem::Requirement
16
17
  requirements:
17
- - - '>='
18
+ - - ">="
18
19
  - !ruby/object:Gem::Version
19
20
  version: '0'
20
- name: enumerable-lazy
21
- prerelease: false
22
21
  type: :runtime
22
+ prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - '>='
25
+ - - ">="
26
26
  - !ruby/object:Gem::Version
27
27
  version: '0'
28
28
  - !ruby/object:Gem::Dependency
29
+ name: rspec
29
30
  requirement: !ruby/object:Gem::Requirement
30
31
  requirements:
31
- - - '>='
32
+ - - ">="
32
33
  - !ruby/object:Gem::Version
33
34
  version: '0'
34
- name: rspec
35
- prerelease: false
36
35
  type: :development
36
+ prerelease: false
37
37
  version_requirements: !ruby/object:Gem::Requirement
38
38
  requirements:
39
- - - '>='
39
+ - - ">="
40
40
  - !ruby/object:Gem::Version
41
41
  version: '0'
42
42
  - !ruby/object:Gem::Dependency
43
+ name: rspec-its
43
44
  requirement: !ruby/object:Gem::Requirement
44
45
  requirements:
45
- - - '>='
46
+ - - ">="
46
47
  - !ruby/object:Gem::Version
47
48
  version: '0'
48
- name: rspec-its
49
- prerelease: false
50
49
  type: :development
50
+ prerelease: false
51
51
  version_requirements: !ruby/object:Gem::Requirement
52
52
  requirements:
53
- - - '>='
53
+ - - ">="
54
54
  - !ruby/object:Gem::Version
55
55
  version: '0'
56
56
  - !ruby/object:Gem::Dependency
57
+ name: addressable
57
58
  requirement: !ruby/object:Gem::Requirement
58
59
  requirements:
59
- - - '>='
60
+ - - ">="
60
61
  - !ruby/object:Gem::Version
61
62
  version: '0'
62
- name: addressable
63
- prerelease: false
64
63
  type: :development
64
+ prerelease: false
65
65
  version_requirements: !ruby/object:Gem::Requirement
66
66
  requirements:
67
- - - '>='
67
+ - - ">="
68
68
  - !ruby/object:Gem::Version
69
69
  version: '0'
70
70
  - !ruby/object:Gem::Dependency
71
+ name: sinatra
71
72
  requirement: !ruby/object:Gem::Requirement
72
73
  requirements:
73
- - - ~>
74
+ - - "~>"
74
75
  - !ruby/object:Gem::Version
75
76
  version: '1.4'
76
- name: sinatra
77
- prerelease: false
78
77
  type: :development
78
+ prerelease: false
79
79
  version_requirements: !ruby/object:Gem::Requirement
80
80
  requirements:
81
- - - ~>
81
+ - - "~>"
82
82
  - !ruby/object:Gem::Version
83
83
  version: '1.4'
84
84
  - !ruby/object:Gem::Dependency
85
+ name: rack-test
85
86
  requirement: !ruby/object:Gem::Requirement
86
87
  requirements:
87
- - - '>='
88
+ - - ">="
88
89
  - !ruby/object:Gem::Version
89
90
  version: '0'
90
- name: rack-test
91
- prerelease: false
92
91
  type: :development
92
+ prerelease: false
93
93
  version_requirements: !ruby/object:Gem::Requirement
94
94
  requirements:
95
- - - '>='
95
+ - - ">="
96
96
  - !ruby/object:Gem::Version
97
97
  version: '0'
98
98
  - !ruby/object:Gem::Dependency
99
+ name: rake
99
100
  requirement: !ruby/object:Gem::Requirement
100
101
  requirements:
101
- - - '>='
102
+ - - ">="
102
103
  - !ruby/object:Gem::Version
103
104
  version: '0'
104
- name: rake
105
- prerelease: false
106
105
  type: :development
106
+ prerelease: false
107
107
  version_requirements: !ruby/object:Gem::Requirement
108
108
  requirements:
109
- - - '>='
109
+ - - ">="
110
110
  - !ruby/object:Gem::Version
111
111
  version: '0'
112
112
  - !ruby/object:Gem::Dependency
113
+ name: yard
113
114
  requirement: !ruby/object:Gem::Requirement
114
115
  requirements:
115
- - - '>='
116
+ - - ">="
116
117
  - !ruby/object:Gem::Version
117
118
  version: '0'
118
- name: yard
119
- prerelease: false
120
119
  type: :development
120
+ prerelease: false
121
121
  version_requirements: !ruby/object:Gem::Requirement
122
122
  requirements:
123
- - - '>='
123
+ - - ">="
124
124
  - !ruby/object:Gem::Version
125
125
  version: '0'
126
126
  - !ruby/object:Gem::Dependency
127
+ name: simplecov
127
128
  requirement: !ruby/object:Gem::Requirement
128
129
  requirements:
129
- - - '>='
130
+ - - ">="
130
131
  - !ruby/object:Gem::Version
131
132
  version: '0'
132
- name: simplecov
133
- prerelease: false
134
133
  type: :development
134
+ prerelease: false
135
135
  version_requirements: !ruby/object:Gem::Requirement
136
136
  requirements:
137
- - - '>='
137
+ - - ">="
138
138
  - !ruby/object:Gem::Version
139
139
  version: '0'
140
140
  - !ruby/object:Gem::Dependency
141
+ name: coveralls
141
142
  requirement: !ruby/object:Gem::Requirement
142
143
  requirements:
143
- - - '>='
144
+ - - ">="
144
145
  - !ruby/object:Gem::Version
145
146
  version: '0'
146
- name: coveralls
147
- prerelease: false
148
147
  type: :development
148
+ prerelease: false
149
149
  version_requirements: !ruby/object:Gem::Requirement
150
150
  requirements:
151
- - - '>='
151
+ - - ">="
152
152
  - !ruby/object:Gem::Version
153
153
  version: '0'
154
- description: library implementing patterns that behave like regular expressions for use in Ruby 1.9
154
+ description: library implementing patterns that behave like regular expressions for
155
+ use in Ruby 1.9
155
156
  email: namusyaka@gmail.com
156
157
  executables: []
157
158
  extensions: []
158
159
  extra_rdoc_files:
159
160
  - README.md
160
161
  files:
161
- - .gitignore
162
- - .rspec
163
- - .travis.yml
164
- - .yardopts
162
+ - ".gitignore"
163
+ - ".rspec"
164
+ - ".travis.yml"
165
+ - ".yardopts"
165
166
  - Gemfile
166
167
  - LICENSE
167
168
  - README.md
@@ -191,6 +192,7 @@ files:
191
192
  - lib/mustermann/express.rb
192
193
  - lib/mustermann/extension.rb
193
194
  - lib/mustermann/flask.rb
195
+ - lib/mustermann/grape.rb
194
196
  - lib/mustermann/identity.rb
195
197
  - lib/mustermann/mapper.rb
196
198
  - lib/mustermann/pattern.rb
@@ -223,6 +225,7 @@ files:
223
225
  - spec/extension_spec.rb
224
226
  - spec/flask_spec.rb
225
227
  - spec/flask_subclass_spec.rb
228
+ - spec/grape_spec.rb
226
229
  - spec/identity_spec.rb
227
230
  - spec/mapper_spec.rb
228
231
  - spec/mustermann_spec.rb
@@ -252,24 +255,24 @@ homepage: https://github.com/namusyaka/mustermann
252
255
  licenses:
253
256
  - MIT
254
257
  metadata: {}
255
- post_install_message:
258
+ post_install_message:
256
259
  rdoc_options: []
257
260
  require_paths:
258
261
  - lib
259
262
  required_ruby_version: !ruby/object:Gem::Requirement
260
263
  requirements:
261
- - - '>='
264
+ - - ">="
262
265
  - !ruby/object:Gem::Version
263
266
  version: 1.9.2
264
267
  required_rubygems_version: !ruby/object:Gem::Requirement
265
268
  requirements:
266
- - - '>='
269
+ - - ">="
267
270
  - !ruby/object:Gem::Version
268
271
  version: '0'
269
272
  requirements: []
270
- rubyforge_project:
271
- rubygems_version: 2.1.9
272
- signing_key:
273
+ rubyforge_project:
274
+ rubygems_version: 2.5.1
275
+ signing_key:
273
276
  specification_version: 4
274
277
  summary: use patterns like regular expressions
275
278
  test_files:
@@ -280,6 +283,7 @@ test_files:
280
283
  - spec/extension_spec.rb
281
284
  - spec/flask_spec.rb
282
285
  - spec/flask_subclass_spec.rb
286
+ - spec/grape_spec.rb
283
287
  - spec/identity_spec.rb
284
288
  - spec/mapper_spec.rb
285
289
  - spec/mustermann_spec.rb
@@ -305,4 +309,4 @@ test_files:
305
309
  - spec/support/scan_matcher.rb
306
310
  - spec/template_spec.rb
307
311
  - spec/to_pattern_spec.rb
308
- has_rdoc:
312
+ has_rdoc: