mustermann19 0.3.1 → 0.3.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +4 -3
  3. data/README.md +680 -376
  4. data/lib/mustermann/ast/compiler.rb +13 -7
  5. data/lib/mustermann/ast/expander.rb +11 -5
  6. data/lib/mustermann/ast/node.rb +27 -1
  7. data/lib/mustermann/ast/param_scanner.rb +20 -0
  8. data/lib/mustermann/ast/parser.rb +131 -12
  9. data/lib/mustermann/ast/pattern.rb +45 -6
  10. data/lib/mustermann/ast/template_generator.rb +28 -0
  11. data/lib/mustermann/ast/validation.rb +5 -3
  12. data/lib/mustermann/composite.rb +103 -0
  13. data/lib/mustermann/expander.rb +1 -1
  14. data/lib/mustermann/express.rb +34 -0
  15. data/lib/mustermann/flask.rb +204 -0
  16. data/lib/mustermann/identity.rb +54 -0
  17. data/lib/mustermann/pattern.rb +186 -12
  18. data/lib/mustermann/pattern_cache.rb +49 -0
  19. data/lib/mustermann/pyramid.rb +25 -0
  20. data/lib/mustermann/regexp_based.rb +18 -1
  21. data/lib/mustermann/regular.rb +1 -1
  22. data/lib/mustermann/shell.rb +8 -0
  23. data/lib/mustermann/simple.rb +1 -1
  24. data/lib/mustermann/simple_match.rb +5 -0
  25. data/lib/mustermann/sinatra.rb +19 -5
  26. data/lib/mustermann/string_scanner.rb +314 -0
  27. data/lib/mustermann/template.rb +10 -0
  28. data/lib/mustermann/to_pattern.rb +11 -6
  29. data/lib/mustermann/version.rb +1 -1
  30. data/lib/mustermann.rb +52 -3
  31. data/mustermann.gemspec +1 -1
  32. data/spec/composite_spec.rb +147 -0
  33. data/spec/expander_spec.rb +15 -0
  34. data/spec/express_spec.rb +209 -0
  35. data/spec/flask_spec.rb +361 -0
  36. data/spec/flask_subclass_spec.rb +368 -0
  37. data/spec/identity_spec.rb +44 -0
  38. data/spec/mustermann_spec.rb +14 -0
  39. data/spec/pattern_spec.rb +7 -3
  40. data/spec/pyramid_spec.rb +101 -0
  41. data/spec/rails_spec.rb +76 -2
  42. data/spec/regular_spec.rb +25 -0
  43. data/spec/shell_spec.rb +33 -0
  44. data/spec/simple_spec.rb +25 -0
  45. data/spec/sinatra_spec.rb +184 -9
  46. data/spec/string_scanner_spec.rb +271 -0
  47. data/spec/support/expand_matcher.rb +7 -5
  48. data/spec/support/generate_template_matcher.rb +27 -0
  49. data/spec/support/pattern.rb +3 -0
  50. data/spec/support/scan_matcher.rb +63 -0
  51. data/spec/support.rb +2 -1
  52. data/spec/template_spec.rb +22 -0
  53. data/spec/to_pattern_spec.rb +49 -0
  54. metadata +47 -61
  55. data/internals.md +0 -64
data/spec/rails_spec.rb CHANGED
@@ -11,6 +11,11 @@ describe Mustermann::Rails do
11
11
 
12
12
  it { should expand.to('') }
13
13
  it { should_not expand(a: 1) }
14
+
15
+ it { should generate_template('') }
16
+
17
+ it { should respond_to(:expand) }
18
+ it { should respond_to(:to_templates) }
14
19
  end
15
20
 
16
21
  pattern '/' do
@@ -62,6 +67,8 @@ describe Mustermann::Rails do
62
67
  it { should_not expand(foo: 'foo', bar: 'bar') }
63
68
  it { should_not expand(bar: 'bar') }
64
69
  it { should_not expand }
70
+
71
+ it { should generate_template('/{foo}') }
65
72
  end
66
73
 
67
74
  pattern '/föö' do
@@ -86,23 +93,29 @@ describe Mustermann::Rails do
86
93
  it { should expand(foo: 'foo', bar: 'bar').to('/foo/bar') }
87
94
  it { should_not expand(foo: 'foo') }
88
95
  it { should_not expand(bar: 'bar') }
96
+
97
+ it { should generate_template('/{foo}/{bar}') }
89
98
  end
90
99
 
91
100
  pattern '/hello/:person' do
92
101
  it { should match('/hello/Frank').capturing person: 'Frank' }
93
102
  it { should expand(person: 'Frank') .to '/hello/Frank' }
94
103
  it { should expand(person: 'Frank?') .to '/hello/Frank%3F' }
104
+
105
+ it { should generate_template('/hello/{person}') }
95
106
  end
96
107
 
97
108
  pattern '/?:foo?/?:bar?' do
98
109
  it { should match('/?hello?/?world?').capturing foo: 'hello', bar: 'world' }
99
110
  it { should_not match('/hello/world/') }
100
111
  it { should expand(foo: 'hello', bar: 'world').to('/%3Fhello%3F/%3Fworld%3F') }
112
+ it { should generate_template('/?{foo}?/?{bar}?') }
101
113
  end
102
114
 
103
115
  pattern '/:foo_bar' do
104
116
  it { should match('/hello').capturing foo_bar: 'hello' }
105
117
  it { should expand(foo_bar: 'hello').to('/hello') }
118
+ it { should generate_template('/{foo_bar}') }
106
119
  end
107
120
 
108
121
  pattern '/*foo' do
@@ -116,6 +129,15 @@ describe Mustermann::Rails do
116
129
  it { should expand(foo: 'foo') .to('/foo') }
117
130
  it { should expand(foo: 'foo/bar') .to('/foo/bar') }
118
131
  it { should expand(foo: 'foo.bar') .to('/foo.bar') }
132
+
133
+ it { should generate_template('/{+foo}') }
134
+ end
135
+
136
+ pattern '/*splat' do
137
+ it { should match('/') .capturing splat: '' }
138
+ it { should match('/foo') .capturing splat: 'foo' }
139
+ it { should match('/foo/bar') .capturing splat: 'foo/bar' }
140
+ it { should generate_template('/{+splat}') }
119
141
  end
120
142
 
121
143
  pattern '/:foo/*bar' do
@@ -129,6 +151,8 @@ describe Mustermann::Rails do
129
151
  it { should expand(foo: 'foo', bar: 'bar') .to('/foo/bar') }
130
152
  it { should expand(foo: 'foo', bar: 'foo/bar') .to('/foo/foo/bar') }
131
153
  it { should expand(foo: 'foo/bar', bar: 'bar') .to('/foo%2Fbar/bar') }
154
+
155
+ it { should generate_template('/{foo}/{+bar}') }
132
156
  end
133
157
 
134
158
  pattern '/test$/' do
@@ -148,6 +172,8 @@ describe Mustermann::Rails do
148
172
  it { should match('/path%2Bwith%2Bspaces') }
149
173
  it { should match('/path+with+spaces') }
150
174
  it { should expand.to('/path%20with%20spaces') }
175
+
176
+ it { should generate_template('/path%20with%20spaces') }
151
177
  end
152
178
 
153
179
  pattern '/foo&bar' do
@@ -158,6 +184,7 @@ describe Mustermann::Rails do
158
184
  it { should match('/bar/foo/bling/baz/boom').capturing a: 'bar', foo: 'foo', b: 'bling', c: 'baz/boom' }
159
185
  example { pattern.params('/bar/foo/bling/baz/boom').should be == { "a" => 'bar', "foo" => 'foo', "b" => 'bling', "c" => 'baz/boom' } }
160
186
  it { should expand(a: 'bar', foo: 'foo', b: 'bling', c: 'baz/boom').to('/bar/foo/bling/baz/boom') }
187
+ it { should generate_template('/{+a}/{foo}/{+b}/{+c}') }
161
188
  end
162
189
 
163
190
  pattern '/test.bar' do
@@ -189,6 +216,9 @@ describe Mustermann::Rails do
189
216
  it { should match('/axaxx') .capturing a: 'axax' }
190
217
  it { should expand(a: 'x').to('/xx') }
191
218
  it { should expand(a: 'a').to('/ax') }
219
+
220
+ it { should generate_template('/{a}x') }
221
+ it { should generate_template('/{a}') }
192
222
  end
193
223
 
194
224
  pattern '/:user(@:host)' do
@@ -198,6 +228,9 @@ describe Mustermann::Rails do
198
228
 
199
229
  it { should expand(user: 'foo') .to('/foo') }
200
230
  it { should expand(user: 'foo', host: 'bar') .to('/foo@bar') }
231
+
232
+ it { should generate_template('/{user}') }
233
+ it { should generate_template('/{user}@{host}') }
201
234
  end
202
235
 
203
236
  pattern '/:file(.:ext)' do
@@ -211,6 +244,9 @@ describe Mustermann::Rails do
211
244
 
212
245
  it { should expand(file: 'pony') .to('/pony') }
213
246
  it { should expand(file: 'pony', ext: 'jpg') .to('/pony.jpg') }
247
+
248
+ it { should generate_template('/{file}') }
249
+ it { should generate_template('/{file}.{ext}') }
214
250
  end
215
251
 
216
252
  pattern '/:id/test.bar' do
@@ -245,6 +281,10 @@ describe Mustermann::Rails do
245
281
 
246
282
  it { should expand(a: ?a, b: ?b) .to('/a/b.') }
247
283
  it { should expand(a: ?a, b: ?b, c: ?c) .to('/a/b.c') }
284
+
285
+ it { should generate_template('/{a}/{b}') }
286
+ it { should generate_template('/{a}/{b}.') }
287
+ it { should generate_template('/{a}/{b}.{c}') }
248
288
  end
249
289
 
250
290
  pattern '/:a(foo:b)' do
@@ -255,6 +295,10 @@ describe Mustermann::Rails do
255
295
 
256
296
  it { should expand(a: ?a) .to('/a') }
257
297
  it { should expand(a: ?a, b: ?b) .to('/afoob') }
298
+
299
+ it { should generate_template('/{a}foo{b}') }
300
+ it { should generate_template('/{a}') }
301
+ it { should_not generate_template('/{a}foo') }
258
302
  end
259
303
 
260
304
  pattern '/fo(o)' do
@@ -512,11 +556,41 @@ describe Mustermann::Rails do
512
556
  end
513
557
 
514
558
  describe :to_regexp do
515
- example('empty pattern') { Mustermann::Rails.new('').to_regexp.should be == /\A\Z/ }
559
+ example('empty pattern') { Mustermann::Rails.new('').to_regexp.should be == /\A(?-mix:)\Z/ }
516
560
 
517
561
  context 'Regexp.try_convert' do
518
- example('empty pattern') { Regexp.try_convert(Mustermann::Rails.new('')).should be == /\A\Z/ }
562
+ example('empty pattern') { Regexp.try_convert(Mustermann::Rails.new('')).should be == /\A(?-mix:)\Z/ }
519
563
  end
520
564
  end
521
565
  end
566
+
567
+ context 'Proc compatibility' do
568
+ describe :to_proc do
569
+ example { Mustermann::Rails.new("/").to_proc.should be_a(Proc) }
570
+ example('non-matching') { Mustermann::Rails.new("/") .to_proc.call('/foo').should be == false }
571
+ example('matching') { Mustermann::Rails.new("/:foo") .to_proc.call('/foo').should be == true }
572
+ end
573
+ end
574
+
575
+ context "peeking" do
576
+ subject(:pattern) { Mustermann::Rails.new(":name") }
577
+
578
+ describe :peek_size do
579
+ example { pattern.peek_size("foo bar/blah") .should be == "foo bar".size }
580
+ example { pattern.peek_size("foo%20bar/blah") .should be == "foo%20bar".size }
581
+ example { pattern.peek_size("/foo bar") .should be_nil }
582
+ end
583
+
584
+ describe :peek_match do
585
+ example { pattern.peek_match("foo bar/blah") .to_s .should be == "foo bar" }
586
+ example { pattern.peek_match("foo%20bar/blah") .to_s .should be == "foo%20bar" }
587
+ example { pattern.peek_match("/foo bar") .should be_nil }
588
+ end
589
+
590
+ describe :peek_params do
591
+ example { pattern.peek_params("foo bar/blah") .should be == [{"name" => "foo bar"}, "foo bar".size] }
592
+ example { pattern.peek_params("foo%20bar/blah") .should be == [{"name" => "foo bar"}, "foo%20bar".size] }
593
+ example { pattern.peek_params("/foo bar") .should be_nil }
594
+ end
595
+ end
522
596
  end
data/spec/regular_spec.rb CHANGED
@@ -7,6 +7,9 @@ describe Mustermann::Regular do
7
7
  pattern '' do
8
8
  it { should match('') }
9
9
  it { should_not match('/') }
10
+
11
+ it { should_not respond_to(:expand) }
12
+ it { should_not respond_to(:to_templates) }
10
13
  end
11
14
 
12
15
  pattern '/' do
@@ -33,4 +36,26 @@ describe Mustermann::Regular do
33
36
  it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
34
37
  it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
35
38
  end
39
+
40
+ context "peeking" do
41
+ subject(:pattern) { Mustermann::Regular.new("(?<name>[^/]+)") }
42
+
43
+ describe :peek_size do
44
+ example { pattern.peek_size("foo bar/blah") .should be == "foo bar".size }
45
+ example { pattern.peek_size("foo%20bar/blah") .should be == "foo%20bar".size }
46
+ example { pattern.peek_size("/foo bar") .should be_nil }
47
+ end
48
+
49
+ describe :peek_match do
50
+ example { pattern.peek_match("foo bar/blah") .to_s .should be == "foo bar" }
51
+ example { pattern.peek_match("foo%20bar/blah") .to_s .should be == "foo%20bar" }
52
+ example { pattern.peek_match("/foo bar") .should be_nil }
53
+ end
54
+
55
+ describe :peek_params do
56
+ example { pattern.peek_params("foo bar/blah") .should be == [{"name" => "foo bar"}, "foo bar".size] }
57
+ example { pattern.peek_params("foo%20bar/blah") .should be == [{"name" => "foo bar"}, "foo%20bar".size] }
58
+ example { pattern.peek_params("/foo bar") .should be_nil }
59
+ end
60
+ end
36
61
  end
data/spec/shell_spec.rb CHANGED
@@ -8,6 +8,9 @@ describe Mustermann::Shell do
8
8
  pattern '' do
9
9
  it { should match('') }
10
10
  it { should_not match('/') }
11
+
12
+ it { should_not respond_to(:expand) }
13
+ it { should_not respond_to(:to_templates) }
11
14
  end
12
15
 
13
16
  pattern '/' do
@@ -106,4 +109,34 @@ describe Mustermann::Shell do
106
109
  describe :=~ do
107
110
  example { '/foo'.should be =~ Mustermann::Shell.new('/foo') }
108
111
  end
112
+
113
+ context "peeking", :skip => true do
114
+ subject(:pattern) { Mustermann::Shell.new("foo*/") }
115
+
116
+ describe :peek_size do
117
+ example { pattern.peek_size("foo bar/blah") .should be == "foo bar/".size }
118
+ example { pattern.peek_size("foo%20bar/blah") .should be == "foo%20bar/".size }
119
+ example { pattern.peek_size("/foo bar") .should be_nil }
120
+
121
+ context 'with just * as pattern' do
122
+ subject(:pattern) { Mustermann::Shell.new('*') }
123
+ example { pattern.peek_size('foo') .should be == 3 }
124
+ example { pattern.peek_size('foo/bar') .should be == 3 }
125
+ example { pattern.peek_size('foo/bar/baz') .should be == 3 }
126
+ example { pattern.peek_size('foo/bar/baz/blah') .should be == 3 }
127
+ end
128
+ end
129
+
130
+ describe :peek_match do
131
+ example { pattern.peek_match("foo bar/blah") .to_s .should be == "foo bar/" }
132
+ example { pattern.peek_match("foo%20bar/blah") .to_s .should be == "foo%20bar/" }
133
+ example { pattern.peek_match("/foo bar") .should be_nil }
134
+ end
135
+
136
+ describe :peek_params do
137
+ example { pattern.peek_params("foo bar/blah") .should be == [{}, "foo bar/".size] }
138
+ example { pattern.peek_params("foo%20bar/blah") .should be == [{}, "foo%20bar/".size] }
139
+ example { pattern.peek_params("/foo bar") .should be_nil }
140
+ end
141
+ end
109
142
  end
data/spec/simple_spec.rb CHANGED
@@ -8,6 +8,9 @@ describe Mustermann::Simple do
8
8
  pattern '' do
9
9
  it { should match('') }
10
10
  it { should_not match('/') }
11
+
12
+ it { should_not respond_to(:expand) }
13
+ it { should_not respond_to(:to_templates) }
11
14
  end
12
15
 
13
16
  pattern '/' do
@@ -234,4 +237,26 @@ describe Mustermann::Simple do
234
237
  to raise_error(Mustermann::CompileError)
235
238
  end
236
239
  end
240
+
241
+ context "peeking" do
242
+ subject(:pattern) { Mustermann::Simple.new(":name") }
243
+
244
+ describe :peek_size do
245
+ example { pattern.peek_size("foo bar/blah") .should be == "foo bar".size }
246
+ example { pattern.peek_size("foo%20bar/blah") .should be == "foo%20bar".size }
247
+ example { pattern.peek_size("/foo bar") .should be_nil }
248
+ end
249
+
250
+ describe :peek_match do
251
+ example { pattern.peek_match("foo bar/blah") .to_s .should be == "foo bar" }
252
+ example { pattern.peek_match("foo%20bar/blah") .to_s .should be == "foo%20bar" }
253
+ example { pattern.peek_match("/foo bar") .should be_nil }
254
+ end
255
+
256
+ describe :peek_params do
257
+ example { pattern.peek_params("foo bar/blah") .should be == [{"name" => "foo bar"}, "foo bar".size] }
258
+ example { pattern.peek_params("foo%20bar/blah") .should be == [{"name" => "foo bar"}, "foo%20bar".size] }
259
+ example { pattern.peek_params("/foo bar") .should be_nil }
260
+ end
261
+ end
237
262
  end
data/spec/sinatra_spec.rb CHANGED
@@ -8,6 +8,11 @@ describe Mustermann::Sinatra do
8
8
  pattern '' do
9
9
  it { should match('') }
10
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) }
11
16
  end
12
17
 
13
18
  pattern '/' do
@@ -44,6 +49,8 @@ describe Mustermann::Sinatra do
44
49
  it { should_not match('/foo/bar') }
45
50
  it { should_not match('/') }
46
51
  it { should_not match('/foo/') }
52
+
53
+ it { should generate_template('/{foo}') }
47
54
  end
48
55
 
49
56
  pattern '/föö' do
@@ -62,10 +69,34 @@ describe Mustermann::Sinatra do
62
69
 
63
70
  example { pattern.params('/bar/foo').should be == {"foo" => "bar", "bar" => "foo"} }
64
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}') }
65
90
  end
66
91
 
67
92
  pattern '/hello/:person' do
68
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}') }
69
100
  end
70
101
 
71
102
  pattern '/?:foo?/?:bar?' do
@@ -74,23 +105,40 @@ describe Mustermann::Sinatra do
74
105
  it { should match('/') .capturing foo: nil, bar: nil }
75
106
  it { should match('') .capturing foo: nil, bar: nil }
76
107
 
77
- it { should_not match('/hello/world/') }
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: '') }
78
113
 
79
- # it { should expand(foo: 'hello') .to('/hello') }
80
- # it { should expand(foo: 'hello', bar: 'world') .to('/hello/world') }
81
- # it { should expand(bar: 'world') .to('//world') }
82
- # it { should expand .to('') }
83
- # it { should_not expand(baz: '') }
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}") }
84
116
  end
85
117
 
86
118
  pattern '/:foo_bar' do
87
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}') }
88
126
  end
89
127
 
90
128
  pattern '/*' do
91
129
  it { should match('/') .capturing splat: '' }
92
130
  it { should match('/foo') .capturing splat: 'foo' }
93
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}') }
94
142
 
95
143
  example { pattern.params('/foo').should be == {"splat" => ["foo"]} }
96
144
  end
@@ -99,6 +147,17 @@ describe Mustermann::Sinatra do
99
147
  it { should match('/') .capturing foo: '' }
100
148
  it { should match('/foo') .capturing foo: 'foo' }
101
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}') }
102
161
 
103
162
  example { pattern.params('/foo') .should be == {"foo" => "foo" } }
104
163
  example { pattern.params('/foo/bar') .should be == {"foo" => "foo/bar" } }
@@ -106,6 +165,12 @@ describe Mustermann::Sinatra do
106
165
 
107
166
  pattern '/*foo/*bar' do
108
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}') }
109
174
  end
110
175
 
111
176
  pattern '/:foo/*' do
@@ -113,6 +178,18 @@ describe Mustermann::Sinatra do
113
178
  it { should match("/foo/") .capturing foo: 'foo', splat: '' }
114
179
  it { should match('/h%20w/h%20a%20y') .capturing foo: 'h%20w', splat: 'h%20a%20y' }
115
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}') }
116
193
 
117
194
  example { pattern.params('/bar/foo').should be == {"splat" => ["foo"], "foo" => "bar"} }
118
195
  example { pattern.params('/bar/foo/f%20o').should be == {"splat" => ["foo/f o"], "foo" => "bar"} }
@@ -132,12 +209,18 @@ describe Mustermann::Sinatra do
132
209
  it { should match('/path%20with%20spaces') }
133
210
  it { should match('/path%2Bwith%2Bspaces') }
134
211
  it { should match('/path+with+spaces') }
212
+
213
+ it { should generate_template('/path%20with%20spaces') }
135
214
  end
136
215
 
137
216
  pattern '/foo&bar' do
138
217
  it { should match('/foo&bar') }
139
218
  end
140
219
 
220
+ pattern '/foo\{bar' do
221
+ it { should match('/foo%7Bbar') }
222
+ end
223
+
141
224
  pattern '/*/:foo/*/*' do
142
225
  it { should match('/bar/foo/bling/baz/boom') }
143
226
 
@@ -154,13 +237,29 @@ describe Mustermann::Sinatra do
154
237
  end
155
238
  end
156
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
+
157
256
  pattern '/test.bar' do
158
257
  it { should match('/test.bar') }
159
258
  it { should_not match('/test0bar') }
160
259
  end
161
260
 
162
261
  pattern '/:file.:ext' do
163
- it { should match('/pony.jpg') .capturing file: 'pony', ext: 'jpg' }
262
+ it { should match('/pony.jpg') .capturing file: 'pony', ext: 'jpg' }
164
263
  it { should match('/pony%2Ejpg') .capturing file: 'pony', ext: 'jpg' }
165
264
  it { should match('/pony%2ejpg') .capturing file: 'pony', ext: 'jpg' }
166
265
 
@@ -180,12 +279,18 @@ describe Mustermann::Sinatra do
180
279
  it { should match('/ax') .capturing a: 'a' }
181
280
  it { should match('/axax') .capturing a: 'axa' }
182
281
  it { should match('/axaxx') .capturing a: 'axax' }
282
+
283
+ it { should generate_template('/{a}x') }
284
+ it { should generate_template('/{a}') }
183
285
  end
184
286
 
185
287
  pattern '/:user(@:host)?' do
186
288
  it { should match('/foo@bar') .capturing user: 'foo', host: 'bar' }
187
289
  it { should match('/foo.foo@bar') .capturing user: 'foo.foo', host: 'bar' }
188
290
  it { should match('/foo@bar.bar') .capturing user: 'foo', host: 'bar.bar' }
291
+
292
+ it { should generate_template('/{user}') }
293
+ it { should generate_template('/{user}@{host}') }
189
294
  end
190
295
 
191
296
  pattern '/:file(.:ext)?' do
@@ -196,6 +301,10 @@ describe Mustermann::Sinatra do
196
301
  it { should match('/pony.png.jpg') .capturing file: 'pony.png', ext: 'jpg' }
197
302
  it { should match('/pony.') .capturing file: 'pony.' }
198
303
  it { should_not match('/.jpg') }
304
+
305
+ it { should generate_template('/{file}') }
306
+ it { should generate_template('/{file}.{ext}') }
307
+ it { should_not generate_template('/{file}.') }
199
308
  end
200
309
 
201
310
  pattern '/:id/test.bar' do
@@ -324,6 +433,37 @@ describe Mustermann::Sinatra do
324
433
  it { should match('/foo/bar/baz').capturing foo: 'foo', bar: 'bar', baz: 'baz' }
325
434
  end
326
435
 
436
+ pattern "/(foo|bar)" do
437
+ it { should match("/foo") }
438
+ it { should match("/bar") }
439
+
440
+ it { should generate_template('/foo') }
441
+ it { should generate_template('/bar') }
442
+ end
443
+
444
+ pattern "/(foo\\|bar)" do
445
+ it { should match "/foo%7Cbar" }
446
+ it { should generate_template "/foo%7Cbar" }
447
+
448
+ it { should_not match("/foo") }
449
+ it { should_not match("/bar") }
450
+
451
+ it { should_not generate_template('/foo') }
452
+ it { should_not generate_template('/bar') }
453
+ end
454
+
455
+ pattern "/(:a/:b|:c)" do
456
+ it { should match("/foo") .capturing c: 'foo' }
457
+ it { should match("/foo/bar") .capturing a: 'foo', b: 'bar' }
458
+
459
+ it { should generate_template('/{a}/{b}') }
460
+ it { should generate_template('/{c}') }
461
+
462
+ it { should expand(a: 'foo', b: 'bar') .to('/foo/bar') }
463
+ it { should expand(c: 'foo') .to('/foo') }
464
+ it { should_not expand(a: 'foo', b: 'bar', c: 'baz') }
465
+ end
466
+
327
467
  pattern '/:foo', capture: /\d+/ do
328
468
  it { should match('/1') .capturing foo: '1' }
329
469
  it { should match('/123') .capturing foo: '123' }
@@ -509,6 +649,11 @@ describe Mustermann::Sinatra do
509
649
  to raise_error(Mustermann::ParseError, 'unexpected ? while parsing "foo??bar"')
510
650
  end
511
651
 
652
+ example '| outside of group' do
653
+ expect { Mustermann::Sinatra.new('foo|bar') }.
654
+ to raise_error(Mustermann::ParseError, 'unexpected | while parsing "foo|bar"')
655
+ end
656
+
512
657
  example 'dangling escape' do
513
658
  expect { Mustermann::Sinatra.new('foo\\') }.
514
659
  to raise_error(Mustermann::ParseError, 'unexpected end of string while parsing "foo\\\\"')
@@ -564,11 +709,41 @@ describe Mustermann::Sinatra do
564
709
  end
565
710
 
566
711
  describe :to_regexp do
567
- example('empty pattern') { Mustermann::Sinatra.new('').to_regexp.should be == /\A\Z/ }
712
+ example('empty pattern') { Mustermann::Sinatra.new('').to_regexp.should be == /\A(?-mix:)\Z/ }
568
713
 
569
714
  context 'Regexp.try_convert' do
570
- example('empty pattern') { Regexp.try_convert(Mustermann::Sinatra.new('')).should be == /\A\Z/ }
715
+ example('empty pattern') { Regexp.try_convert(Mustermann::Sinatra.new('')).should be == /\A(?-mix:)\Z/ }
571
716
  end
572
717
  end
573
718
  end
719
+
720
+ context 'Proc compatibility' do
721
+ describe :to_proc do
722
+ example { Mustermann::Sinatra.new("/").to_proc.should be_a(Proc) }
723
+ example('non-matching') { Mustermann::Sinatra.new("/") .to_proc.call('/foo').should be == false }
724
+ example('matching') { Mustermann::Sinatra.new("/:foo") .to_proc.call('/foo').should be == true }
725
+ end
726
+ end
727
+
728
+ context "peeking" do
729
+ subject(:pattern) { Mustermann::Sinatra.new(":name") }
730
+
731
+ describe :peek_size do
732
+ example { pattern.peek_size("foo bar/blah") .should be == "foo bar".size }
733
+ example { pattern.peek_size("foo%20bar/blah") .should be == "foo%20bar".size }
734
+ example { pattern.peek_size("/foo bar") .should be_nil }
735
+ end
736
+
737
+ describe :peek_match do
738
+ example { pattern.peek_match("foo bar/blah") .to_s .should be == "foo bar" }
739
+ example { pattern.peek_match("foo%20bar/blah") .to_s .should be == "foo%20bar" }
740
+ example { pattern.peek_match("/foo bar") .should be_nil }
741
+ end
742
+
743
+ describe :peek_params do
744
+ example { pattern.peek_params("foo bar/blah") .should be == [{"name" => "foo bar"}, "foo bar".size] }
745
+ example { pattern.peek_params("foo%20bar/blah") .should be == [{"name" => "foo bar"}, "foo%20bar".size] }
746
+ example { pattern.peek_params("/foo bar") .should be_nil }
747
+ end
748
+ end
574
749
  end