mustermann19 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: