mustermann-contrib 1.0.0.beta2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +1239 -0
- data/examples/highlighting.rb +35 -0
- data/highlighting.png +0 -0
- data/irb.png +0 -0
- data/lib/mustermann/cake.rb +18 -0
- data/lib/mustermann/express.rb +37 -0
- data/lib/mustermann/file_utils.rb +217 -0
- data/lib/mustermann/file_utils/glob_pattern.rb +39 -0
- data/lib/mustermann/fileutils.rb +1 -0
- data/lib/mustermann/flask.rb +198 -0
- data/lib/mustermann/grape.rb +35 -0
- data/lib/mustermann/pyramid.rb +28 -0
- data/lib/mustermann/rails.rb +46 -0
- data/lib/mustermann/shell.rb +56 -0
- data/lib/mustermann/simple.rb +50 -0
- data/lib/mustermann/string_scanner.rb +313 -0
- data/lib/mustermann/strscan.rb +1 -0
- data/lib/mustermann/template.rb +62 -0
- data/lib/mustermann/uri_template.rb +1 -0
- data/lib/mustermann/versions.rb +46 -0
- data/lib/mustermann/visualizer.rb +38 -0
- data/lib/mustermann/visualizer/highlight.rb +137 -0
- data/lib/mustermann/visualizer/highlighter.rb +37 -0
- data/lib/mustermann/visualizer/highlighter/ad_hoc.rb +94 -0
- data/lib/mustermann/visualizer/highlighter/ast.rb +102 -0
- data/lib/mustermann/visualizer/highlighter/composite.rb +45 -0
- data/lib/mustermann/visualizer/highlighter/dummy.rb +18 -0
- data/lib/mustermann/visualizer/highlighter/regular.rb +104 -0
- data/lib/mustermann/visualizer/pattern_extension.rb +68 -0
- data/lib/mustermann/visualizer/renderer/ansi.rb +23 -0
- data/lib/mustermann/visualizer/renderer/generic.rb +46 -0
- data/lib/mustermann/visualizer/renderer/hansi_template.rb +34 -0
- data/lib/mustermann/visualizer/renderer/html.rb +50 -0
- data/lib/mustermann/visualizer/renderer/sexp.rb +37 -0
- data/lib/mustermann/visualizer/tree.rb +63 -0
- data/lib/mustermann/visualizer/tree_renderer.rb +78 -0
- data/mustermann-contrib.gemspec +19 -0
- data/spec/cake_spec.rb +90 -0
- data/spec/express_spec.rb +209 -0
- data/spec/file_utils_spec.rb +119 -0
- data/spec/flask_spec.rb +361 -0
- data/spec/flask_subclass_spec.rb +368 -0
- data/spec/grape_spec.rb +747 -0
- data/spec/pattern_extension_spec.rb +49 -0
- data/spec/pyramid_spec.rb +101 -0
- data/spec/rails_spec.rb +647 -0
- data/spec/shell_spec.rb +147 -0
- data/spec/simple_spec.rb +268 -0
- data/spec/string_scanner_spec.rb +271 -0
- data/spec/template_spec.rb +841 -0
- data/spec/visualizer_spec.rb +199 -0
- data/theme.png +0 -0
- data/tree.png +0 -0
- metadata +126 -0
data/spec/flask_spec.rb
ADDED
@@ -0,0 +1,361 @@
|
|
1
|
+
require 'support'
|
2
|
+
require 'mustermann/flask'
|
3
|
+
|
4
|
+
describe Mustermann::Flask do
|
5
|
+
extend Support::Pattern
|
6
|
+
|
7
|
+
pattern '' do
|
8
|
+
it { should match('') }
|
9
|
+
it { should_not match('/') }
|
10
|
+
|
11
|
+
it { should expand.to('') }
|
12
|
+
it { should_not expand(a: 1) }
|
13
|
+
|
14
|
+
it { should generate_template('') }
|
15
|
+
|
16
|
+
it { should respond_to(:expand) }
|
17
|
+
it { should respond_to(:to_templates) }
|
18
|
+
end
|
19
|
+
|
20
|
+
pattern '/' do
|
21
|
+
it { should match('/') }
|
22
|
+
it { should_not match('/foo') }
|
23
|
+
|
24
|
+
it { should expand.to('/') }
|
25
|
+
it { should_not expand(a: 1) }
|
26
|
+
end
|
27
|
+
|
28
|
+
pattern '/foo' do
|
29
|
+
it { should match('/foo') }
|
30
|
+
it { should_not match('/bar') }
|
31
|
+
it { should_not match('/foo.bar') }
|
32
|
+
|
33
|
+
it { should expand.to('/foo') }
|
34
|
+
it { should_not expand(a: 1) }
|
35
|
+
end
|
36
|
+
|
37
|
+
pattern '/foo/bar' do
|
38
|
+
it { should match('/foo/bar') }
|
39
|
+
it { should_not match('/foo%2Fbar') }
|
40
|
+
it { should_not match('/foo%2fbar') }
|
41
|
+
|
42
|
+
it { should expand.to('/foo/bar') }
|
43
|
+
it { should_not expand(a: 1) }
|
44
|
+
end
|
45
|
+
|
46
|
+
pattern '/<foo>' do
|
47
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
48
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
49
|
+
it { should match('/foo.bar') .capturing foo: 'foo.bar' }
|
50
|
+
it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
|
51
|
+
it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
|
52
|
+
|
53
|
+
it { should_not match('/foo?') }
|
54
|
+
it { should_not match('/foo/bar') }
|
55
|
+
it { should_not match('/') }
|
56
|
+
it { should_not match('/foo/') }
|
57
|
+
|
58
|
+
example { pattern.params('/foo') .should be == {"foo" => "foo"} }
|
59
|
+
example { pattern.params('/f%20o') .should be == {"foo" => "f o"} }
|
60
|
+
example { pattern.params('').should be_nil }
|
61
|
+
|
62
|
+
it { should expand(foo: 'bar') .to('/bar') }
|
63
|
+
it { should expand(foo: 'b r') .to('/b%20r') }
|
64
|
+
it { should expand(foo: 'foo/bar') .to('/foo%2Fbar') }
|
65
|
+
|
66
|
+
it { should_not expand(foo: 'foo', bar: 'bar') }
|
67
|
+
it { should_not expand(bar: 'bar') }
|
68
|
+
it { should_not expand }
|
69
|
+
|
70
|
+
it { should generate_template('/{foo}') }
|
71
|
+
end
|
72
|
+
|
73
|
+
pattern '/<string:foo>' do
|
74
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
75
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
76
|
+
it { should match('/foo.bar') .capturing foo: 'foo.bar' }
|
77
|
+
it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
|
78
|
+
it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
|
79
|
+
|
80
|
+
it { should_not match('/foo?') }
|
81
|
+
it { should_not match('/foo/bar') }
|
82
|
+
it { should_not match('/') }
|
83
|
+
it { should_not match('/foo/') }
|
84
|
+
|
85
|
+
example { pattern.params('/foo') .should be == {"foo" => "foo"} }
|
86
|
+
example { pattern.params('/f%20o') .should be == {"foo" => "f o"} }
|
87
|
+
example { pattern.params('').should be_nil }
|
88
|
+
|
89
|
+
it { should expand(foo: 'bar') .to('/bar') }
|
90
|
+
it { should expand(foo: 'b r') .to('/b%20r') }
|
91
|
+
it { should expand(foo: 'foo/bar') .to('/foo%2Fbar') }
|
92
|
+
|
93
|
+
it { should_not expand(foo: 'foo', bar: 'bar') }
|
94
|
+
it { should_not expand(bar: 'bar') }
|
95
|
+
it { should_not expand }
|
96
|
+
|
97
|
+
it { should generate_template('/{foo}') }
|
98
|
+
end
|
99
|
+
|
100
|
+
pattern '/<string(minlength=2):foo>' do
|
101
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
102
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
103
|
+
it { should match('/foo.bar') .capturing foo: 'foo.bar' }
|
104
|
+
it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
|
105
|
+
it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
|
106
|
+
|
107
|
+
it { should_not match('/f') }
|
108
|
+
it { should_not match('/foo?') }
|
109
|
+
it { should_not match('/foo/bar') }
|
110
|
+
it { should_not match('/') }
|
111
|
+
it { should_not match('/foo/') }
|
112
|
+
|
113
|
+
it { should generate_template('/{foo}') }
|
114
|
+
end
|
115
|
+
|
116
|
+
pattern '/<string(maxlength=3):foo>' do
|
117
|
+
it { should match('/f') .capturing foo: 'f' }
|
118
|
+
it { should match('/fo') .capturing foo: 'fo' }
|
119
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
120
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
121
|
+
|
122
|
+
it { should_not match('/fooo') }
|
123
|
+
it { should_not match('/foo.bar') }
|
124
|
+
it { should_not match('/foo?') }
|
125
|
+
it { should_not match('/foo/bar') }
|
126
|
+
it { should_not match('/') }
|
127
|
+
it { should_not match('/foo/') }
|
128
|
+
|
129
|
+
it { should generate_template('/{foo}') }
|
130
|
+
end
|
131
|
+
|
132
|
+
pattern '/<string(length=3):foo>' do
|
133
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
134
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
135
|
+
|
136
|
+
it { should_not match('/f') }
|
137
|
+
it { should_not match('/fo') }
|
138
|
+
it { should_not match('/fooo') }
|
139
|
+
it { should_not match('/foo.bar') }
|
140
|
+
it { should_not match('/foo?') }
|
141
|
+
it { should_not match('/foo/bar') }
|
142
|
+
it { should_not match('/') }
|
143
|
+
it { should_not match('/foo/') }
|
144
|
+
|
145
|
+
it { should generate_template('/{foo}') }
|
146
|
+
end
|
147
|
+
|
148
|
+
pattern '/<int:foo>' do
|
149
|
+
it { should match('/42').capturing foo: '42' }
|
150
|
+
|
151
|
+
it { should_not match('/1.0') }
|
152
|
+
it { should_not match('/.5') }
|
153
|
+
it { should_not match('/foo') }
|
154
|
+
it { should_not match('/bar') }
|
155
|
+
it { should_not match('/foo.bar') }
|
156
|
+
it { should_not match('/%0Afoo') }
|
157
|
+
it { should_not match('/foo%2Fbar') }
|
158
|
+
|
159
|
+
it { should_not match('/foo?') }
|
160
|
+
it { should_not match('/foo/bar') }
|
161
|
+
it { should_not match('/') }
|
162
|
+
it { should_not match('/foo/') }
|
163
|
+
|
164
|
+
example { pattern.params('/42').should be == {"foo" => 42} }
|
165
|
+
it { should expand(foo: 12).to('/12') }
|
166
|
+
it { should generate_template('/{foo}') }
|
167
|
+
end
|
168
|
+
|
169
|
+
pattern '/<int:foo>' do
|
170
|
+
it { should match('/42').capturing foo: '42' }
|
171
|
+
|
172
|
+
it { should_not match('/1.0') }
|
173
|
+
it { should_not match('/.5') }
|
174
|
+
it { should_not match('/foo') }
|
175
|
+
it { should_not match('/bar') }
|
176
|
+
it { should_not match('/foo.bar') }
|
177
|
+
it { should_not match('/%0Afoo') }
|
178
|
+
it { should_not match('/foo%2Fbar') }
|
179
|
+
|
180
|
+
it { should_not match('/foo?') }
|
181
|
+
it { should_not match('/foo/bar') }
|
182
|
+
it { should_not match('/') }
|
183
|
+
it { should_not match('/foo/') }
|
184
|
+
|
185
|
+
example { pattern.params('/42').should be == {"foo" => 42} }
|
186
|
+
it { should expand(foo: 12).to('/12') }
|
187
|
+
it { should generate_template('/{foo}') }
|
188
|
+
end
|
189
|
+
|
190
|
+
pattern '/<any(foo,bar):foo>' do
|
191
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
192
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
193
|
+
|
194
|
+
it { should_not match('/f') }
|
195
|
+
it { should_not match('/fo') }
|
196
|
+
it { should_not match('/fooo') }
|
197
|
+
it { should_not match('/foo.bar') }
|
198
|
+
it { should_not match('/foo?') }
|
199
|
+
it { should_not match('/foo/bar') }
|
200
|
+
it { should_not match('/') }
|
201
|
+
it { should_not match('/foo/') }
|
202
|
+
it { should_not match('/baz') }
|
203
|
+
|
204
|
+
it { should generate_template('/{foo}') }
|
205
|
+
end
|
206
|
+
|
207
|
+
pattern '/<any( foo, bar ):foo>' do
|
208
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
209
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
210
|
+
|
211
|
+
it { should_not match('/f') }
|
212
|
+
it { should_not match('/fo') }
|
213
|
+
it { should_not match('/fooo') }
|
214
|
+
it { should_not match('/foo.bar') }
|
215
|
+
it { should_not match('/foo?') }
|
216
|
+
it { should_not match('/foo/bar') }
|
217
|
+
it { should_not match('/') }
|
218
|
+
it { should_not match('/foo/') }
|
219
|
+
it { should_not match('/baz') }
|
220
|
+
|
221
|
+
it { should generate_template('/{foo}') }
|
222
|
+
end
|
223
|
+
|
224
|
+
pattern '/<any(foo, bar, "foo,bar"):foo>' do
|
225
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
226
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
227
|
+
it { should match('/foo,bar') .capturing foo: 'foo,bar' }
|
228
|
+
|
229
|
+
it { should_not match('/f') }
|
230
|
+
it { should_not match('/fo') }
|
231
|
+
it { should_not match('/fooo') }
|
232
|
+
it { should_not match('/foo.bar') }
|
233
|
+
it { should_not match('/foo?') }
|
234
|
+
it { should_not match('/foo/bar') }
|
235
|
+
it { should_not match('/') }
|
236
|
+
it { should_not match('/foo/') }
|
237
|
+
it { should_not match('/baz') }
|
238
|
+
|
239
|
+
it { should generate_template('/{foo}') }
|
240
|
+
end
|
241
|
+
|
242
|
+
pattern '/<any(foo, bar, foo\,bar):foo>' do
|
243
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
244
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
245
|
+
it { should match('/foo,bar') .capturing foo: 'foo,bar' }
|
246
|
+
|
247
|
+
it { should_not match('/f') }
|
248
|
+
it { should_not match('/fo') }
|
249
|
+
it { should_not match('/fooo') }
|
250
|
+
it { should_not match('/foo.bar') }
|
251
|
+
it { should_not match('/foo?') }
|
252
|
+
it { should_not match('/foo/bar') }
|
253
|
+
it { should_not match('/') }
|
254
|
+
it { should_not match('/foo/') }
|
255
|
+
it { should_not match('/baz') }
|
256
|
+
|
257
|
+
it { should generate_template('/{foo}') }
|
258
|
+
end
|
259
|
+
|
260
|
+
pattern '/<any(foo, bar, "foo\,bar"):foo>' do
|
261
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
262
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
263
|
+
it { should match('/foo,bar') .capturing foo: 'foo,bar' }
|
264
|
+
|
265
|
+
it { should_not match('/f') }
|
266
|
+
it { should_not match('/fo') }
|
267
|
+
it { should_not match('/fooo') }
|
268
|
+
it { should_not match('/foo.bar') }
|
269
|
+
it { should_not match('/foo?') }
|
270
|
+
it { should_not match('/foo/bar') }
|
271
|
+
it { should_not match('/') }
|
272
|
+
it { should_not match('/foo/') }
|
273
|
+
it { should_not match('/baz') }
|
274
|
+
|
275
|
+
it { should generate_template('/{foo}') }
|
276
|
+
end
|
277
|
+
|
278
|
+
pattern '/<int(min=5,max=50):foo>' do
|
279
|
+
example { pattern.params('/42').should be == {"foo" => 42} }
|
280
|
+
example { pattern.params('/52').should be == {"foo" => 50} }
|
281
|
+
example { pattern.params('/2').should be == {"foo" => 5} }
|
282
|
+
end
|
283
|
+
|
284
|
+
pattern '/<float(min=5,max=50.5):foo>' do
|
285
|
+
example { pattern.params('/42.5').should be == {"foo" => 42.5} }
|
286
|
+
example { pattern.params('/52.5').should be == {"foo" => 50.5} }
|
287
|
+
example { pattern.params('/2.5').should be == {"foo" => 5.0} }
|
288
|
+
end
|
289
|
+
|
290
|
+
pattern '/<prefix>/<float:foo>/<int:bar>' do
|
291
|
+
it { should match('/foo/42/42') .capturing foo: '42', bar: '42' }
|
292
|
+
it { should match('/foo/1.0/1') .capturing foo: '1.0', bar: '1' }
|
293
|
+
it { should match('/foo/.5/0') .capturing foo: '.5', bar: '0' }
|
294
|
+
|
295
|
+
it { should_not match('/foo/1/1.0') }
|
296
|
+
it { should_not match('/foo/1.0/1.0') }
|
297
|
+
|
298
|
+
it { should generate_template('/{prefix}/{foo}/{bar}') }
|
299
|
+
|
300
|
+
example do
|
301
|
+
pattern.params('/foo/1.0/1').should be == {
|
302
|
+
"prefix" => "foo",
|
303
|
+
"foo" => 1.0,
|
304
|
+
"bar" => 1
|
305
|
+
}
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
pattern '/<path:foo>' do
|
310
|
+
it { should match('/') .capturing foo: '' }
|
311
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
312
|
+
it { should match('/foo/bar') .capturing foo: 'foo/bar' }
|
313
|
+
|
314
|
+
it { should expand .to('/') }
|
315
|
+
it { should expand(foo: nil) .to('/') }
|
316
|
+
it { should expand(foo: '') .to('/') }
|
317
|
+
it { should expand(foo: 'foo') .to('/foo') }
|
318
|
+
it { should expand(foo: 'foo/bar') .to('/foo/bar') }
|
319
|
+
it { should expand(foo: 'foo.bar') .to('/foo.bar') }
|
320
|
+
|
321
|
+
it { should generate_template('/{+foo}') }
|
322
|
+
end
|
323
|
+
|
324
|
+
converter = Struct.new(:convert).new(:upcase.to_proc)
|
325
|
+
pattern '/<foo:bar>', converters: { foo: converter } do
|
326
|
+
it { should match('/foo').capturing bar: 'foo' }
|
327
|
+
example { pattern.params('/foo').should be == {"bar" => "FOO"} }
|
328
|
+
end
|
329
|
+
|
330
|
+
context 'invalid syntax' do
|
331
|
+
example 'unexpected end of capture' do
|
332
|
+
expect { Mustermann::Flask.new('foo>bar') }.
|
333
|
+
to raise_error(Mustermann::ParseError, 'unexpected > while parsing "foo>bar"')
|
334
|
+
end
|
335
|
+
|
336
|
+
example 'missing end of capture' do
|
337
|
+
expect { Mustermann::Flask.new('foo<bar') }.
|
338
|
+
to raise_error(Mustermann::ParseError, 'unexpected end of string while parsing "foo<bar"')
|
339
|
+
end
|
340
|
+
|
341
|
+
example 'unknown converter' do
|
342
|
+
expect { Mustermann::Flask.new('foo<bar:name>') }.
|
343
|
+
to raise_error(Mustermann::ParseError, 'unexpected converter "bar" while parsing "foo<bar:name>"')
|
344
|
+
end
|
345
|
+
|
346
|
+
example 'broken argument synax' do
|
347
|
+
expect { Mustermann::Flask.new('<string(length=3=2):foo>') }.
|
348
|
+
to raise_error(Mustermann::ParseError, 'unexpected = while parsing "<string(length=3=2):foo>"')
|
349
|
+
end
|
350
|
+
|
351
|
+
example 'missing )' do
|
352
|
+
expect { Mustermann::Flask.new('<string(foo') }.
|
353
|
+
to raise_error(Mustermann::ParseError, 'unexpected end of string while parsing "<string(foo"')
|
354
|
+
end
|
355
|
+
|
356
|
+
example 'missing ""' do
|
357
|
+
expect { Mustermann::Flask.new('<string("foo') }.
|
358
|
+
to raise_error(Mustermann::ParseError, 'unexpected end of string while parsing "<string(\\"foo"')
|
359
|
+
end
|
360
|
+
end
|
361
|
+
end
|
@@ -0,0 +1,368 @@
|
|
1
|
+
require 'support'
|
2
|
+
require 'mustermann/flask'
|
3
|
+
|
4
|
+
FlaskSubclass ||= Class.new(Mustermann::Flask)
|
5
|
+
FlaskSubclass.register_converter(:foo) {}
|
6
|
+
|
7
|
+
describe FlaskSubclass do
|
8
|
+
extend Support::Pattern
|
9
|
+
|
10
|
+
pattern '' do
|
11
|
+
it { should match('') }
|
12
|
+
it { should_not match('/') }
|
13
|
+
|
14
|
+
it { should expand.to('') }
|
15
|
+
it { should_not expand(a: 1) }
|
16
|
+
|
17
|
+
it { should generate_template('') }
|
18
|
+
|
19
|
+
it { should respond_to(:expand) }
|
20
|
+
it { should respond_to(:to_templates) }
|
21
|
+
end
|
22
|
+
|
23
|
+
pattern '/' do
|
24
|
+
it { should match('/') }
|
25
|
+
it { should_not match('/foo') }
|
26
|
+
|
27
|
+
it { should expand.to('/') }
|
28
|
+
it { should_not expand(a: 1) }
|
29
|
+
end
|
30
|
+
|
31
|
+
pattern '/foo' do
|
32
|
+
it { should match('/foo') }
|
33
|
+
it { should_not match('/bar') }
|
34
|
+
it { should_not match('/foo.bar') }
|
35
|
+
|
36
|
+
it { should expand.to('/foo') }
|
37
|
+
it { should_not expand(a: 1) }
|
38
|
+
end
|
39
|
+
|
40
|
+
pattern '/foo/bar' do
|
41
|
+
it { should match('/foo/bar') }
|
42
|
+
it { should_not match('/foo%2Fbar') }
|
43
|
+
it { should_not match('/foo%2fbar') }
|
44
|
+
|
45
|
+
it { should expand.to('/foo/bar') }
|
46
|
+
it { should_not expand(a: 1) }
|
47
|
+
end
|
48
|
+
|
49
|
+
pattern '/<foo>' do
|
50
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
51
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
52
|
+
it { should match('/foo.bar') .capturing foo: 'foo.bar' }
|
53
|
+
it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
|
54
|
+
it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
|
55
|
+
|
56
|
+
it { should_not match('/foo?') }
|
57
|
+
it { should_not match('/foo/bar') }
|
58
|
+
it { should_not match('/') }
|
59
|
+
it { should_not match('/foo/') }
|
60
|
+
|
61
|
+
example { pattern.params('/foo') .should be == {"foo" => "foo"} }
|
62
|
+
example { pattern.params('/f%20o') .should be == {"foo" => "f o"} }
|
63
|
+
example { pattern.params('').should be_nil }
|
64
|
+
|
65
|
+
it { should expand(foo: 'bar') .to('/bar') }
|
66
|
+
it { should expand(foo: 'b r') .to('/b%20r') }
|
67
|
+
it { should expand(foo: 'foo/bar') .to('/foo%2Fbar') }
|
68
|
+
|
69
|
+
it { should_not expand(foo: 'foo', bar: 'bar') }
|
70
|
+
it { should_not expand(bar: 'bar') }
|
71
|
+
it { should_not expand }
|
72
|
+
|
73
|
+
it { should generate_template('/{foo}') }
|
74
|
+
end
|
75
|
+
|
76
|
+
pattern '/<string:foo>' do
|
77
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
78
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
79
|
+
it { should match('/foo.bar') .capturing foo: 'foo.bar' }
|
80
|
+
it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
|
81
|
+
it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
|
82
|
+
|
83
|
+
it { should_not match('/foo?') }
|
84
|
+
it { should_not match('/foo/bar') }
|
85
|
+
it { should_not match('/') }
|
86
|
+
it { should_not match('/foo/') }
|
87
|
+
|
88
|
+
example { pattern.params('/foo') .should be == {"foo" => "foo"} }
|
89
|
+
example { pattern.params('/f%20o') .should be == {"foo" => "f o"} }
|
90
|
+
example { pattern.params('').should be_nil }
|
91
|
+
|
92
|
+
it { should expand(foo: 'bar') .to('/bar') }
|
93
|
+
it { should expand(foo: 'b r') .to('/b%20r') }
|
94
|
+
it { should expand(foo: 'foo/bar') .to('/foo%2Fbar') }
|
95
|
+
|
96
|
+
it { should_not expand(foo: 'foo', bar: 'bar') }
|
97
|
+
it { should_not expand(bar: 'bar') }
|
98
|
+
it { should_not expand }
|
99
|
+
|
100
|
+
it { should generate_template('/{foo}') }
|
101
|
+
end
|
102
|
+
|
103
|
+
pattern '/<string(minlength=2):foo>' do
|
104
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
105
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
106
|
+
it { should match('/foo.bar') .capturing foo: 'foo.bar' }
|
107
|
+
it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
|
108
|
+
it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
|
109
|
+
|
110
|
+
it { should_not match('/f') }
|
111
|
+
it { should_not match('/foo?') }
|
112
|
+
it { should_not match('/foo/bar') }
|
113
|
+
it { should_not match('/') }
|
114
|
+
it { should_not match('/foo/') }
|
115
|
+
|
116
|
+
it { should generate_template('/{foo}') }
|
117
|
+
end
|
118
|
+
|
119
|
+
pattern '/<string(maxlength=3):foo>' do
|
120
|
+
it { should match('/f') .capturing foo: 'f' }
|
121
|
+
it { should match('/fo') .capturing foo: 'fo' }
|
122
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
123
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
124
|
+
|
125
|
+
it { should_not match('/fooo') }
|
126
|
+
it { should_not match('/foo.bar') }
|
127
|
+
it { should_not match('/foo?') }
|
128
|
+
it { should_not match('/foo/bar') }
|
129
|
+
it { should_not match('/') }
|
130
|
+
it { should_not match('/foo/') }
|
131
|
+
|
132
|
+
it { should generate_template('/{foo}') }
|
133
|
+
end
|
134
|
+
|
135
|
+
pattern '/<string(length=3):foo>' do
|
136
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
137
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
138
|
+
|
139
|
+
it { should_not match('/f') }
|
140
|
+
it { should_not match('/fo') }
|
141
|
+
it { should_not match('/fooo') }
|
142
|
+
it { should_not match('/foo.bar') }
|
143
|
+
it { should_not match('/foo?') }
|
144
|
+
it { should_not match('/foo/bar') }
|
145
|
+
it { should_not match('/') }
|
146
|
+
it { should_not match('/foo/') }
|
147
|
+
|
148
|
+
it { should generate_template('/{foo}') }
|
149
|
+
end
|
150
|
+
|
151
|
+
pattern '/<int:foo>' do
|
152
|
+
it { should match('/42').capturing foo: '42' }
|
153
|
+
|
154
|
+
it { should_not match('/1.0') }
|
155
|
+
it { should_not match('/.5') }
|
156
|
+
it { should_not match('/foo') }
|
157
|
+
it { should_not match('/bar') }
|
158
|
+
it { should_not match('/foo.bar') }
|
159
|
+
it { should_not match('/%0Afoo') }
|
160
|
+
it { should_not match('/foo%2Fbar') }
|
161
|
+
|
162
|
+
it { should_not match('/foo?') }
|
163
|
+
it { should_not match('/foo/bar') }
|
164
|
+
it { should_not match('/') }
|
165
|
+
it { should_not match('/foo/') }
|
166
|
+
|
167
|
+
example { pattern.params('/42').should be == {"foo" => 42} }
|
168
|
+
it { should expand(foo: 12).to('/12') }
|
169
|
+
it { should generate_template('/{foo}') }
|
170
|
+
end
|
171
|
+
|
172
|
+
pattern '/<int:foo>' do
|
173
|
+
it { should match('/42').capturing foo: '42' }
|
174
|
+
|
175
|
+
it { should_not match('/1.0') }
|
176
|
+
it { should_not match('/.5') }
|
177
|
+
it { should_not match('/foo') }
|
178
|
+
it { should_not match('/bar') }
|
179
|
+
it { should_not match('/foo.bar') }
|
180
|
+
it { should_not match('/%0Afoo') }
|
181
|
+
it { should_not match('/foo%2Fbar') }
|
182
|
+
|
183
|
+
it { should_not match('/foo?') }
|
184
|
+
it { should_not match('/foo/bar') }
|
185
|
+
it { should_not match('/') }
|
186
|
+
it { should_not match('/foo/') }
|
187
|
+
|
188
|
+
example { pattern.params('/42').should be == {"foo" => 42} }
|
189
|
+
it { should expand(foo: 12).to('/12') }
|
190
|
+
it { should generate_template('/{foo}') }
|
191
|
+
end
|
192
|
+
|
193
|
+
pattern '/<any(foo,bar):foo>' do
|
194
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
195
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
196
|
+
|
197
|
+
it { should_not match('/f') }
|
198
|
+
it { should_not match('/fo') }
|
199
|
+
it { should_not match('/fooo') }
|
200
|
+
it { should_not match('/foo.bar') }
|
201
|
+
it { should_not match('/foo?') }
|
202
|
+
it { should_not match('/foo/bar') }
|
203
|
+
it { should_not match('/') }
|
204
|
+
it { should_not match('/foo/') }
|
205
|
+
it { should_not match('/baz') }
|
206
|
+
|
207
|
+
it { should generate_template('/{foo}') }
|
208
|
+
end
|
209
|
+
|
210
|
+
pattern '/<any( foo, bar ):foo>' do
|
211
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
212
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
213
|
+
|
214
|
+
it { should_not match('/f') }
|
215
|
+
it { should_not match('/fo') }
|
216
|
+
it { should_not match('/fooo') }
|
217
|
+
it { should_not match('/foo.bar') }
|
218
|
+
it { should_not match('/foo?') }
|
219
|
+
it { should_not match('/foo/bar') }
|
220
|
+
it { should_not match('/') }
|
221
|
+
it { should_not match('/foo/') }
|
222
|
+
it { should_not match('/baz') }
|
223
|
+
|
224
|
+
it { should generate_template('/{foo}') }
|
225
|
+
end
|
226
|
+
|
227
|
+
pattern '/<any(foo, bar, "foo,bar"):foo>' do
|
228
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
229
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
230
|
+
it { should match('/foo,bar') .capturing foo: 'foo,bar' }
|
231
|
+
|
232
|
+
it { should_not match('/f') }
|
233
|
+
it { should_not match('/fo') }
|
234
|
+
it { should_not match('/fooo') }
|
235
|
+
it { should_not match('/foo.bar') }
|
236
|
+
it { should_not match('/foo?') }
|
237
|
+
it { should_not match('/foo/bar') }
|
238
|
+
it { should_not match('/') }
|
239
|
+
it { should_not match('/foo/') }
|
240
|
+
it { should_not match('/baz') }
|
241
|
+
|
242
|
+
it { should generate_template('/{foo}') }
|
243
|
+
end
|
244
|
+
|
245
|
+
pattern '/<any(foo, bar, foo\,bar):foo>' do
|
246
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
247
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
248
|
+
it { should match('/foo,bar') .capturing foo: 'foo,bar' }
|
249
|
+
|
250
|
+
it { should_not match('/f') }
|
251
|
+
it { should_not match('/fo') }
|
252
|
+
it { should_not match('/fooo') }
|
253
|
+
it { should_not match('/foo.bar') }
|
254
|
+
it { should_not match('/foo?') }
|
255
|
+
it { should_not match('/foo/bar') }
|
256
|
+
it { should_not match('/') }
|
257
|
+
it { should_not match('/foo/') }
|
258
|
+
it { should_not match('/baz') }
|
259
|
+
|
260
|
+
it { should generate_template('/{foo}') }
|
261
|
+
end
|
262
|
+
|
263
|
+
pattern '/<any(foo, bar, "foo\,bar"):foo>' do
|
264
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
265
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
266
|
+
it { should match('/foo,bar') .capturing foo: 'foo,bar' }
|
267
|
+
|
268
|
+
it { should_not match('/f') }
|
269
|
+
it { should_not match('/fo') }
|
270
|
+
it { should_not match('/fooo') }
|
271
|
+
it { should_not match('/foo.bar') }
|
272
|
+
it { should_not match('/foo?') }
|
273
|
+
it { should_not match('/foo/bar') }
|
274
|
+
it { should_not match('/') }
|
275
|
+
it { should_not match('/foo/') }
|
276
|
+
it { should_not match('/baz') }
|
277
|
+
|
278
|
+
it { should generate_template('/{foo}') }
|
279
|
+
end
|
280
|
+
|
281
|
+
pattern '/<int(min=5,max=50):foo>' do
|
282
|
+
example { pattern.params('/42').should be == {"foo" => 42} }
|
283
|
+
example { pattern.params('/52').should be == {"foo" => 50} }
|
284
|
+
example { pattern.params('/2').should be == {"foo" => 5} }
|
285
|
+
end
|
286
|
+
|
287
|
+
pattern '/<float(min=5,max=50.5):foo>' do
|
288
|
+
example { pattern.params('/42.5').should be == {"foo" => 42.5} }
|
289
|
+
example { pattern.params('/52.5').should be == {"foo" => 50.5} }
|
290
|
+
example { pattern.params('/2.5').should be == {"foo" => 5.0} }
|
291
|
+
end
|
292
|
+
|
293
|
+
pattern '/<prefix>/<float:foo>/<int:bar>' do
|
294
|
+
it { should match('/foo/42/42') .capturing foo: '42', bar: '42' }
|
295
|
+
it { should match('/foo/1.0/1') .capturing foo: '1.0', bar: '1' }
|
296
|
+
it { should match('/foo/.5/0') .capturing foo: '.5', bar: '0' }
|
297
|
+
|
298
|
+
it { should_not match('/foo/1/1.0') }
|
299
|
+
it { should_not match('/foo/1.0/1.0') }
|
300
|
+
|
301
|
+
it { should generate_template('/{prefix}/{foo}/{bar}') }
|
302
|
+
|
303
|
+
example do
|
304
|
+
pattern.params('/foo/1.0/1').should be == {
|
305
|
+
"prefix" => "foo",
|
306
|
+
"foo" => 1.0,
|
307
|
+
"bar" => 1
|
308
|
+
}
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
pattern '/<path:foo>' do
|
313
|
+
it { should match('/') .capturing foo: '' }
|
314
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
315
|
+
it { should match('/foo/bar') .capturing foo: 'foo/bar' }
|
316
|
+
|
317
|
+
it { should expand .to('/') }
|
318
|
+
it { should expand(foo: nil) .to('/') }
|
319
|
+
it { should expand(foo: '') .to('/') }
|
320
|
+
it { should expand(foo: 'foo') .to('/foo') }
|
321
|
+
it { should expand(foo: 'foo/bar') .to('/foo/bar') }
|
322
|
+
it { should expand(foo: 'foo.bar') .to('/foo.bar') }
|
323
|
+
|
324
|
+
it { should generate_template('/{+foo}') }
|
325
|
+
end
|
326
|
+
|
327
|
+
pattern '/<foo:bar>' do
|
328
|
+
it { should match('/foo').capturing bar: 'foo' }
|
329
|
+
it { should generate_template('/{bar}') }
|
330
|
+
|
331
|
+
example do
|
332
|
+
expect { Mustermann::Flask.new('/<foo:bar>') }.to \
|
333
|
+
raise_error(Mustermann::ParseError, 'unexpected converter "foo" while parsing "/<foo:bar>"')
|
334
|
+
end
|
335
|
+
end
|
336
|
+
|
337
|
+
context 'invalid syntax' do
|
338
|
+
example 'unexpected end of capture' do
|
339
|
+
expect { FlaskSubclass.new('foo>bar') }.
|
340
|
+
to raise_error(Mustermann::ParseError, 'unexpected > while parsing "foo>bar"')
|
341
|
+
end
|
342
|
+
|
343
|
+
example 'missing end of capture' do
|
344
|
+
expect { FlaskSubclass.new('foo<bar') }.
|
345
|
+
to raise_error(Mustermann::ParseError, 'unexpected end of string while parsing "foo<bar"')
|
346
|
+
end
|
347
|
+
|
348
|
+
example 'unknown converter' do
|
349
|
+
expect { FlaskSubclass.new('foo<bar:name>') }.
|
350
|
+
to raise_error(Mustermann::ParseError, 'unexpected converter "bar" while parsing "foo<bar:name>"')
|
351
|
+
end
|
352
|
+
|
353
|
+
example 'broken argument synax' do
|
354
|
+
expect { FlaskSubclass.new('<string(length=3=2):foo>') }.
|
355
|
+
to raise_error(Mustermann::ParseError, 'unexpected = while parsing "<string(length=3=2):foo>"')
|
356
|
+
end
|
357
|
+
|
358
|
+
example 'missing )' do
|
359
|
+
expect { FlaskSubclass.new('<string(foo') }.
|
360
|
+
to raise_error(Mustermann::ParseError, 'unexpected end of string while parsing "<string(foo"')
|
361
|
+
end
|
362
|
+
|
363
|
+
example 'missing ""' do
|
364
|
+
expect { FlaskSubclass.new('<string("foo') }.
|
365
|
+
to raise_error(Mustermann::ParseError, 'unexpected end of string while parsing "<string(\\"foo"')
|
366
|
+
end
|
367
|
+
end
|
368
|
+
end
|