mustermann19 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/.gitignore +18 -0
  2. data/.rspec +2 -0
  3. data/.travis.yml +10 -0
  4. data/.yardopts +1 -0
  5. data/Gemfile +2 -0
  6. data/LICENSE +22 -0
  7. data/README.md +1081 -0
  8. data/Rakefile +6 -0
  9. data/bench/capturing.rb +57 -0
  10. data/bench/regexp.rb +21 -0
  11. data/bench/simple_vs_sinatra.rb +23 -0
  12. data/bench/template_vs_addressable.rb +26 -0
  13. data/internals.md +64 -0
  14. data/lib/mustermann.rb +61 -0
  15. data/lib/mustermann/ast/compiler.rb +168 -0
  16. data/lib/mustermann/ast/expander.rb +134 -0
  17. data/lib/mustermann/ast/node.rb +160 -0
  18. data/lib/mustermann/ast/parser.rb +137 -0
  19. data/lib/mustermann/ast/pattern.rb +84 -0
  20. data/lib/mustermann/ast/transformer.rb +129 -0
  21. data/lib/mustermann/ast/translator.rb +108 -0
  22. data/lib/mustermann/ast/tree_renderer.rb +29 -0
  23. data/lib/mustermann/ast/validation.rb +43 -0
  24. data/lib/mustermann/caster.rb +117 -0
  25. data/lib/mustermann/equality_map.rb +48 -0
  26. data/lib/mustermann/error.rb +6 -0
  27. data/lib/mustermann/expander.rb +206 -0
  28. data/lib/mustermann/extension.rb +52 -0
  29. data/lib/mustermann/identity.rb +19 -0
  30. data/lib/mustermann/mapper.rb +98 -0
  31. data/lib/mustermann/pattern.rb +182 -0
  32. data/lib/mustermann/rails.rb +17 -0
  33. data/lib/mustermann/regexp_based.rb +30 -0
  34. data/lib/mustermann/regular.rb +26 -0
  35. data/lib/mustermann/router.rb +9 -0
  36. data/lib/mustermann/router/rack.rb +50 -0
  37. data/lib/mustermann/router/simple.rb +144 -0
  38. data/lib/mustermann/shell.rb +29 -0
  39. data/lib/mustermann/simple.rb +38 -0
  40. data/lib/mustermann/simple_match.rb +30 -0
  41. data/lib/mustermann/sinatra.rb +22 -0
  42. data/lib/mustermann/template.rb +48 -0
  43. data/lib/mustermann/to_pattern.rb +45 -0
  44. data/lib/mustermann/version.rb +3 -0
  45. data/mustermann.gemspec +31 -0
  46. data/spec/expander_spec.rb +105 -0
  47. data/spec/extension_spec.rb +296 -0
  48. data/spec/identity_spec.rb +83 -0
  49. data/spec/mapper_spec.rb +83 -0
  50. data/spec/mustermann_spec.rb +65 -0
  51. data/spec/pattern_spec.rb +49 -0
  52. data/spec/rails_spec.rb +522 -0
  53. data/spec/regexp_based_spec.rb +8 -0
  54. data/spec/regular_spec.rb +36 -0
  55. data/spec/router/rack_spec.rb +39 -0
  56. data/spec/router/simple_spec.rb +32 -0
  57. data/spec/shell_spec.rb +109 -0
  58. data/spec/simple_match_spec.rb +10 -0
  59. data/spec/simple_spec.rb +237 -0
  60. data/spec/sinatra_spec.rb +574 -0
  61. data/spec/support.rb +5 -0
  62. data/spec/support/coverage.rb +16 -0
  63. data/spec/support/env.rb +15 -0
  64. data/spec/support/expand_matcher.rb +27 -0
  65. data/spec/support/match_matcher.rb +39 -0
  66. data/spec/support/pattern.rb +39 -0
  67. data/spec/template_spec.rb +815 -0
  68. data/spec/to_pattern_spec.rb +20 -0
  69. metadata +301 -0
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ .test_queue_stats
7
+ .coverage
8
+ Gemfile.lock
9
+ InstalledFiles
10
+ _yardoc
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --tty
@@ -0,0 +1,10 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - 1.9.2
4
+ - ruby-head
5
+ - jruby-head
6
+ - rbx-2.0.0
7
+ matrix:
8
+ allow_failures:
9
+ - rvm: jruby-head
10
+ - rvm: rbx-2.0.0
@@ -0,0 +1 @@
1
+ - README.md internals.md
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Konstantin Haase
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,1081 @@
1
+ # The Amazing Mustermann
2
+
3
+ [![Build Status](https://travis-ci.org/namusyaka/mustermann.svg?branch=19support)](https://travis-ci.org/namusyaka/mustermann) [![Coverage Status](http://img.shields.io/coveralls/rkh/mustermann.svg?branch=master)](https://coveralls.io/r/rkh/mustermann) [![Code Climate](http://img.shields.io/codeclimate/github/rkh/mustermann.svg)](https://codeclimate.com/github/rkh/mustermann) [![Dependency Status](https://gemnasium.com/rkh/mustermann.svg)](https://gemnasium.com/rkh/mustermann) [![Gem Version](http://img.shields.io/gem/v/mustermann.svg)](https://rubygems.org/gems/mustermann)
4
+ [![Inline docs](http://inch-ci.org/github/rkh/mustermann.svg)](http://inch-ci.org/github/rkh/mustermann)
5
+ [![Documentation](http://img.shields.io/:yard-docs-38c800.svg)](http://rubydoc.info/gems/mustermann/frames)
6
+ [![License](http://img.shields.io/:license-MIT-38c800.svg)](http://rkh.mit-license.org)
7
+ [![Badges](http://img.shields.io/:badges-9/9-38c800.svg)](http://img.shields.io)
8
+
9
+
10
+ *Make sure you view the correct docs: [latest release](http://rubydoc.info/gems/mustermann/frames), [master](http://rubydoc.info/github/rkh/mustermann/master/frames).*
11
+
12
+ Welcome to [Mustermann](http://en.wikipedia.org/wiki/List_of_placeholder_names_by_language#German). Mustermann is your personal string matching expert. As an expert in the field of strings and patterns, Mustermann also has no runtime dependencies and is fully covered with specs and documentation.
13
+
14
+ Given a string pattern, Mustermann will turn it into an object that behaves like a regular expression and has comparable performance characteristics.
15
+
16
+ ``` ruby
17
+ if '/foo/bar' =~ Mustermann.new('/foo/*')
18
+ puts 'it works!'
19
+ end
20
+
21
+ case 'something.png'
22
+ when Mustermann.new('foo/*') then puts "prefixed with foo"
23
+ when Mustermann.new('*.pdf') then puts "it's a PDF"
24
+ when Mustermann.new('*.png') then puts "it's an image"
25
+ end
26
+ ```
27
+
28
+ Besides being a `Regexp` look-alike, Mustermann also adds a `params` method, that will give you a Sinatra-style hash:
29
+
30
+ ``` ruby
31
+ pattern = Mustermann.new('/:prefix/*.*')
32
+ pattern.params('/a/b.c') # => { "prefix" => "a", splat => ["b", "c"] }
33
+ ```
34
+
35
+ It's generally a good idea to reuse pattern objects, since as much computation as possible is happening during object creation, so that the actual matching or expanding is quite fast.
36
+
37
+ ## Types and Options
38
+
39
+ You can pass in additional options to take fine grained control over the pattern:
40
+
41
+ ``` ruby
42
+ Mustermann.new('/:foo.:bar', capture: :alpha) # :foo and :bar will only match alphabetic characters
43
+ ```
44
+
45
+ In fact, you can even completely change the pattern type:
46
+
47
+ ``` ruby
48
+ Mustermann.new('/**/*.png', type: :shell)
49
+ ```
50
+
51
+ The available types are:
52
+
53
+ <table>
54
+ <thead>
55
+ <tr>
56
+ <th>Type</th>
57
+ <th>Description</th>
58
+ <th>Example</th>
59
+ <th>Available Options</th>
60
+ <th>Additional Features</th>
61
+ </tr>
62
+ </thead>
63
+ <tbody>
64
+ <tr>
65
+ <th><a href="#identity"><tt>identity</tt></a></th>
66
+ <td>URI unescaped input string has to match exactly</td>
67
+ <td><tt>/image.png</tt></td>
68
+ <td>
69
+ <a href="#ignore_unknown_options"><tt>ignore_unknown_options</tt></a>,
70
+ <a href="#uri_decode"><tt>uri_decode</tt></a>
71
+ </td>
72
+ <td></td>
73
+ </tr>
74
+ <tr>
75
+ <th><a href="#rails"><tt>rails</tt></a></th>
76
+ <td>Rails style patterns</td>
77
+ <td><tt>/:slug(.:ext)</tt></td>
78
+ <td>
79
+ <a href="#capture"><tt>capture</tt></a>,
80
+ <a href="#except"><tt>except</tt></a>,
81
+ <a href="#greedy"><tt>greedy</tt></a>,
82
+ <a href="#ignore_unknown_options"><tt>ignore_unknown_options</tt></a>,
83
+ <a href="#space_matches_plus"><tt>space_matches_plus</tt></a>,
84
+ <a href="#uri_decode"><tt>uri_decode</tt></a>
85
+ </td>
86
+ <td>
87
+ <a href="#pattern_expanding">Expanding</a>
88
+ </td>
89
+ </tr>
90
+ <tr>
91
+ <th><a href="#regexp"><tt>regexp</tt></a></th>
92
+ <td>Regular expressions as implemented by Ruby</td>
93
+ <td><tt>/(?&lt;slug&gt;.*)</tt></td>
94
+ <td>
95
+ <a href="#ignore_unknown_options"><tt>ignore_unknown_options</tt></a>,
96
+ <a href="#uri_decode"><tt>uri_decode</tt></a>
97
+ </td>
98
+ <td></td>
99
+ </tr>
100
+ <tr>
101
+ <th><a href="#shell"><tt>shell</tt></th>
102
+ <td>Unix style patterns</td>
103
+ <td><tt>/*.{png,jpg}</tt></td>
104
+ <td>
105
+ <a href="#ignore_unknown_options"><tt>ignore_unknown_options</tt></a>,
106
+ <a href="#uri_decode"><tt>uri_decode</tt></a>
107
+ </td>
108
+ <td></td>
109
+ </tr>
110
+ <tr>
111
+ <th><a href="#simple"><tt>simple</tt></a></th>
112
+ <td>Sinatra 1.3 style patterns</td>
113
+ <td><tt>/:slug.:ext</tt></td>
114
+ <td>
115
+ <a href="#greedy"><tt>greedy</tt></a>,
116
+ <a href="#ignore_unknown_options"><tt>ignore_unknown_options</tt></a>,
117
+ <a href="#space_matches_plus"><tt>space_matches_plus</tt></a>,
118
+ <a href="#uri_decode"><tt>uri_decode</tt></a>
119
+ </td>
120
+ <td></td>
121
+ </tr>
122
+ <tr>
123
+ <th><a href="#sinatra"><tt>sinatra</tt></a></th>
124
+ <td>Sinatra 2.0 style patterns (default)</td>
125
+ <td><tt>/:slug(.:ext)?</tt></td>
126
+ <td>
127
+ <a href="#capture"><tt>capture</tt></a>,
128
+ <a href="#except"><tt>except</tt></a>,
129
+ <a href="#greedy"><tt>greedy</tt></a>,
130
+ <a href="#ignore_unknown_options"><tt>ignore_unknown_options</tt></a>,
131
+ <a href="#space_matches_plus"><tt>space_matches_plus</tt></a>,
132
+ <a href="#uri_decode"><tt>uri_decode</tt></a>
133
+ </td>
134
+ <td>
135
+ <a href="#pattern_expanding">Expanding</a>
136
+ </td>
137
+ </tr>
138
+ <tr>
139
+ <th><a href="#template"><tt>template</tt></a></th>
140
+ <td><a href="http://tools.ietf.org/html/rfc6570">URI templates</a></td>
141
+ <td><tt>/dictionary/{term}</tt></td>
142
+ <td>
143
+ <a href="#capture"><tt>capture</tt></a>,
144
+ <a href="#except"><tt>except</tt></a>,
145
+ <a href="#greedy"><tt>greedy</tt></a>,
146
+ <a href="#ignore_unknown_options"><tt>ignore_unknown_options</tt></a>,
147
+ <a href="#space_matches_plus"><tt>space_matches_plus</tt></a>,
148
+ <a href="#uri_decode"><tt>uri_decode</tt></a>
149
+ </td>
150
+ <td>
151
+ <a href="#pattern_expanding">Expanding</a>
152
+ </td>
153
+ </tr>
154
+ </tbody>
155
+ </table>
156
+
157
+ See below for more details.
158
+
159
+ ## Sinatra Integration
160
+
161
+ All patterns implement `match`, which means they can be dropped into Sinatra and other Rack routers:
162
+
163
+ ``` ruby
164
+ require 'sinatra'
165
+ require 'mustermann'
166
+
167
+ get Mustermann.new('/:foo') do
168
+ params[:foo]
169
+ end
170
+ ```
171
+
172
+ In fact, since using this with Sinatra is the main use case, it comes with a build-in extension for **Sinatra 1.x**.
173
+
174
+ ``` ruby
175
+ require 'sinatra'
176
+ require 'mustermann'
177
+
178
+ register Mustermann
179
+
180
+ # this will use Mustermann rather than the built-in pattern matching
181
+ get '/:slug(.ext)?' do
182
+ params[:slug]
183
+ end
184
+ ```
185
+
186
+ ### Configuration
187
+
188
+ You can change what pattern type you want to use for your app via the `pattern` option:
189
+
190
+ ``` ruby
191
+ require 'sinatra/base'
192
+ require 'mustermann'
193
+
194
+ class MyApp < Sinatra::Base
195
+ register Mustermann
196
+ set :pattern, type: :shell
197
+
198
+ get '/images/*.png' do
199
+ send_file request.path_info
200
+ end
201
+
202
+ get '/index{.htm,.html,}' do
203
+ erb :index
204
+ end
205
+ end
206
+ ```
207
+
208
+ You can use the same setting for options:
209
+
210
+ ``` ruby
211
+ require 'sinatra'
212
+ require 'mustermann'
213
+
214
+ register Mustermann
215
+ set :pattern, capture: { ext: %w[png jpg html txt] }
216
+
217
+ get '/:slug(.:ext)?' do
218
+ # slug will be 'foo' for '/foo.png'
219
+ # slug will be 'foo.bar' for '/foo.bar'
220
+ # slug will be 'foo.bar' for '/foo.bar.html'
221
+ params[:slug]
222
+ end
223
+ ```
224
+
225
+ It is also possible to pass in options to a specific route:
226
+
227
+ ``` ruby
228
+ require 'sinatra'
229
+ require 'mustermann'
230
+
231
+ register Mustermann
232
+
233
+ get '/:slug(.:ext)?', pattern: { greedy: false } do
234
+ # slug will be 'foo' for '/foo.png'
235
+ # slug will be 'foo' for '/foo.bar'
236
+ # slug will be 'foo' for '/foo.bar.html'
237
+ params[:slug]
238
+ end
239
+ ```
240
+
241
+ Of course, all of the above can be combined.
242
+ Moreover, the `capture` and the `except` option can be passed to route directly.
243
+ And yes, this also works with `before` and `after` filters.
244
+
245
+ ``` ruby
246
+ require 'sinatra/base'
247
+ require 'sinatra/respond_with'
248
+ require 'mustermann'
249
+
250
+ class MyApp < Sinatra::Base
251
+ register Mustermann, Sinatra::RespondWith
252
+ set :pattern, capture: { id: /\d+/ } # id will only match digits
253
+
254
+ # only capture extensions known to Rack
255
+ before '*:ext', capture: Rack::Mime::MIME_TYPES.keys do
256
+ content_type params[:ext] # set Content-Type
257
+ request.path_info = params[:splat].first # drop the extension
258
+ end
259
+
260
+ get '/:id' do
261
+ not_found unless page = Page.find params[:id]
262
+ respond_with(page)
263
+ end
264
+ end
265
+ ```
266
+
267
+ ### Why would I want this?
268
+
269
+ * It gives you fine grained control over the pattern matching
270
+ * Allows you to use different pattern styles in your app
271
+ * The default is more robust and powerful than the built-in patterns
272
+ * Sinatra 2.0 will use Mustermann internally
273
+ * Better exceptions for broken route syntax
274
+
275
+ ### Why not include this in Sinatra 1.x?
276
+
277
+ * It would introduce breaking changes, even though these would be minor
278
+ * Like Sinatra 2.0, Mustermann requires Ruby 2.0 or newer
279
+
280
+ <a name="pattern_expanding"></a>
281
+ ## Expanding
282
+
283
+ Similarly to parsing, it is also possible to generate a string from a pattern by expanding it with a hash.
284
+ For simple expansions, you can use `Pattern#expand`.
285
+
286
+ ``` ruby
287
+ pattern = Mustermann.new('/:file(.:ext)?')
288
+ pattern.expand(file: 'pony') # => "/pony"
289
+ pattern.expand(file: 'pony', ext: 'jpg') # => "/pony.jpg"
290
+ pattern.expand(ext: 'jpg') # raises Mustermann::ExpandError
291
+ ```
292
+
293
+ Expanding can be useful for instance when implementing link helpers.
294
+
295
+ ### Expander Objects
296
+
297
+ To get fine-grained control over expansion, you can use `Mustermann::Expander` directly.
298
+
299
+ You can create an expander object directly from a string:
300
+
301
+ ``` ruby
302
+ require 'mustermann/expander'
303
+ expander = Mustermann::Expander("/:file.jpg")
304
+ expander.expand(file: 'pony') # => "/pony.jpg"
305
+
306
+ expander = Mustermann::Expander(":file(.:ext)", type: :rails)
307
+ expander.expand(file: 'pony', ext: 'jpg') # => "/pony.jpg"
308
+ ```
309
+
310
+ Or you can pass it a pattern instance:
311
+
312
+ ``` ruby
313
+ require 'mustermann'
314
+ pattern = Mustermann.new("/:file")
315
+
316
+ require 'mustermann/expander'
317
+ expander = Mustermann::Expander.new(pattern)
318
+ ```
319
+
320
+ ### Expanding Multiple Patterns
321
+
322
+ You can add patterns to an expander object via `<<`:
323
+
324
+ ``` ruby
325
+ expander = Mustermann::Expander.new
326
+ expander << "/users/:user_id"
327
+ expander << "/pages/:page_id"
328
+
329
+ expander.expand(user_id: 15) # => "/users/15"
330
+ expander.expand(page_id: 58) # => "/pages/58"
331
+ ```
332
+
333
+ You can set pattern options when creating the expander:
334
+
335
+ ``` ruby
336
+ expander = Mustermann::Expander.new(type: :template)
337
+ expander << "/users/{user_id}"
338
+ expander << "/pages/{page_id}"
339
+ ```
340
+
341
+ Additionally, it is possible to combine patterns of different types:
342
+
343
+ ``` ruby
344
+ expander = Mustermann::Expander.new
345
+ expander << Mustermann.new("/users/{user_id}", type: :template)
346
+ expander << Mustermann.new("/pages/:page_id", type: :rails)
347
+ ```
348
+
349
+ ### Handling Additional Values
350
+
351
+ The handling of additional values passed in to `expand` can be changed by setting the `additional_values` option:
352
+
353
+ ``` ruby
354
+ expander = Mustermann::Expander.new("/:slug", additional_values: :raise)
355
+ expander.expand(slug: "foo", value: "bar") # raises Mustermann::ExpandError
356
+
357
+ expander = Mustermann::Expander.new("/:slug", additional_values: :ignore)
358
+ expander.expand(slug: "foo", value: "bar") # => "/foo"
359
+
360
+ expander = Mustermann::Expander.new("/:slug", additional_values: :append)
361
+ expander.expand(slug: "foo", value: "bar") # => "/foo?value=bar"
362
+ ```
363
+
364
+ ## Duck Typing
365
+
366
+ All methods converting string input to pattern objects will also accept any arbitrary object that implements `to_pattern`:
367
+
368
+ ``` ruby
369
+ require 'mustermann'
370
+
371
+ class MyObject
372
+ def to_pattern(**options)
373
+ Mustermann.new("/foo", **options)
374
+ end
375
+ end
376
+
377
+ object = MyObject.new
378
+ Mustermann.new(object, type: :rails) # => #<Mustermann::Rails:"/foo">
379
+ ```
380
+
381
+ It might also be that you want to call `to_pattern` yourself instead of `Mustermann.new`. You can load `mustermann/to_pattern` to implement this method for strings, regular expressions and pattern objects:
382
+
383
+ ``` ruby
384
+ require 'mustermann/to_pattern'
385
+
386
+ "/foo".to_pattern # => #<Mustermann::Sinatra:"/foo">
387
+ "/foo".to_pattern(type: :rails) # => #<Mustermann::Rails:"/foo">
388
+ %r{/foo}.to_pattern # => #<Mustermann::Regular:"\\/foo">
389
+ "/foo".to_pattern.to_pattern # => #<Mustermann::Sinatra:"/foo">
390
+ ```
391
+
392
+ You can also use the `Mustermann::ToPattern` mixin to easily add `to_pattern` to your own objects:
393
+
394
+ ``` ruby
395
+ require 'mustermann/to_pattern'
396
+
397
+ class MyObject
398
+ include Mustermann::ToPattern
399
+
400
+ def to_s
401
+ "/foo"
402
+ end
403
+ end
404
+
405
+ MyObject.new.to_pattern # => #<Mustermann::Sinatra:"/foo">
406
+ ```
407
+
408
+ ## Partial Loading and Thread Safety
409
+
410
+ Pattern objects are generally assumed to be thread-safe. You can easily match strings against the same pattern object concurrently.
411
+
412
+ Mustermann will only load the pattern implementation you need. For example, `mustermann/rails` is loaded the first time you invoke `Mustermann.new(..., type: :rails)`. This part might not be thread-safe, depending on your Ruby implementation.
413
+
414
+ In the common use cases, that is Sinatra and similar, patterns are compiled on the main thread during the application load phase, so this is a non-issue there.
415
+
416
+ To avoid this, you can load the pattern types you need manually:
417
+
418
+ ``` ruby
419
+ require 'mustermann/sinatra'
420
+ Mustermann::Sinatra.new('/:foo')
421
+ ```
422
+
423
+ ## Options
424
+
425
+ ### `capture`
426
+
427
+ Supported by: `rails`, `sinatra`, `template`
428
+
429
+ **Sinatra**, **URI template** and **Rails** patterns support changing the way named captures work via the `capture` options.
430
+
431
+ Possible values for a capture:
432
+
433
+ ``` ruby
434
+ # String: Matches the given string (or any URI encoded version of it)
435
+ Mustermann.new('/index.:ext', capture: 'png')
436
+
437
+ # Regexp: Matches the Regular expression
438
+ Mustermann.new('/:id', capture: /\d+/)
439
+
440
+ # Symbol: Matches POSIX character class
441
+ Mustermann.new('/:id', capture: :digit)
442
+
443
+ # Array of the above: Matches anything in the array
444
+ Mustermann.new('/:id_or_slug', capture: [/\d+/, :word])
445
+
446
+ # Hash of the above: Looks up the hash entry by capture name and uses value for matching
447
+ Mustermann.new('/:id.:ext', capture: { id: /\d+/, ext: ['png', 'jpg'] })
448
+ ```
449
+
450
+ Available POSIX character classes are: `:alnum`, `:alpha`, `:blank`, `:cntrl`, `:digit`, `:graph`, `:lower`, `:print`, `:punct`, `:space`, `:upper`, `:xdigit`, `:word` and `:ascii`.
451
+
452
+ ### `except`
453
+
454
+ Supported by: `rails`, `sinatra`, `template`
455
+
456
+ Given you supply a second pattern via the except option. Any string that would match the primary pattern but also matches the except pattern will not result in a successful match. Feel free to read that again. Or just take a look at this example:
457
+
458
+ ``` ruby
459
+ pattern = Mustermann.new('/auth/*', except: '/auth/login')
460
+ pattern === '/auth/dunno' # => true
461
+ pattern === '/auth/login' # => false
462
+ ```
463
+
464
+ Now, as said above, `except` treats the value as a pattern:
465
+
466
+ ``` ruby
467
+ pattern = Mustermann.new('/*anything', type: :rails, except: '/*anything.png')
468
+ pattern === '/foo.jpg' # => true
469
+ pattern === '/foo.png' # => false
470
+ ```
471
+
472
+ ### `greedy`
473
+
474
+ Supported by: `rails`, `simple`, `sinatra`, `template`. Default value: `true`
475
+
476
+ **Simple** patterns are greedy, meaning that for the pattern `:foo:bar?`, everything will be captured as `foo`, `bar` will always be `nil`. By setting `greedy` to `false`, `foo` will capture as little as possible (which in this case would only be the first letter), leaving the rest to `bar`.
477
+
478
+ **Sinatra**, **URI template** and **Rails** patterns are semi-greedy. This means `:foo(.:bar)?` (`:foo(.:bar)` for Rails patterns) will capture everything before the *last* dot as `foo`. For these two pattern types, you can switch into non-greedy mode by setting the `greedy` option to false. In that case `foo` will only capture the part before the *first* dot.
479
+
480
+ Semi-greedy behavior is not specific to dots, it works with all characters or strings. For instance, `:a(foo:b)` will capture everything before the *last* `foo` as `a`, and `:foo(bar)?` will not capture a `bar` at the end.
481
+
482
+ ``` ruby
483
+ pattern = Mustermann.new(':a.:b', greedy: true)
484
+ pattern.match('a.b.c.d') # => #<MatchData a:"a.b.c" b:"d">
485
+
486
+ pattern = Mustermann.new(':a.:b', greedy: false)
487
+ pattern.match('a.b.c.d') # => #<MatchData a:"a" b:"b.c.d">
488
+ ```
489
+
490
+ ### `space_matches_plus`
491
+
492
+ Supported by: `rails`, `simple`, `sinatra`, `template`. Default value: `true`
493
+
494
+ **Sinatra**, **Simple**, **URI template** and **Rails** patterns will by default also match a plus sign for a space in the pattern:
495
+
496
+ ``` ruby
497
+ Mustermann.new('a b') === 'a+b' # => true
498
+ ```
499
+
500
+ You can disable this behavior via `space_matches_plus`:
501
+
502
+ ``` ruby
503
+ Mustermann.new('a b', space_matches_plus: false) === 'a+b' # => false
504
+ ```
505
+
506
+ **Important:** This setting has no effect on captures, captures will always keep plus signs as plus sings and spaces as spaces:
507
+
508
+ ``` ruby
509
+ pattern = Mustermann.new(':x')
510
+ pattern.match('a b')[:x] # => 'a b'
511
+ pattern.match('a+b')[:x] # => 'a+b'
512
+ ````
513
+
514
+ ### `uri_decode`
515
+
516
+ Supported by all patterns. Default value: `true`
517
+
518
+ Usually, characters in the pattern will also match the URI encoded version of these characters:
519
+
520
+ ``` ruby
521
+ Mustermann.new('a b') === 'a b' # => true
522
+ Mustermann.new('a b') === 'a%20b' # => true
523
+ ```
524
+
525
+ You can avoid this by setting `uri_decode` to `false`:
526
+
527
+ ``` ruby
528
+ Mustermann.new('a b', uri_decode: false) === 'a b' # => true
529
+ Mustermann.new('a b', uri_decode: false) === 'a%20b' # => false
530
+ ```
531
+
532
+ ### `ignore_unknown_options`
533
+
534
+ Supported by all patterns. Default value: `false`
535
+
536
+ If you pass an option in that is not supported by the specific pattern type, Mustermann will raise an `ArgumentError`.
537
+ By setting `ignore_unknown_options` to `true`, it will happily ignore the option.
538
+
539
+ ## Pattern Types
540
+
541
+ ### `identity`
542
+
543
+ Identity patterns are strings that have to match the input exactly.
544
+
545
+ ``` ruby
546
+ require 'mustermann'
547
+
548
+ pattern = Mustermann.new('/:example', type: :identity)
549
+ pattern === "/foo.bar" # => false
550
+ pattern === "/:example" # => true
551
+ pattern.params("/foo.bar") # => nil
552
+ pattern.params("/:example") # => {}
553
+ ```
554
+
555
+ <table>
556
+ <thead>
557
+ <tr>
558
+ <th>Syntax Element</th>
559
+ <th>Description</th>
560
+ </tr>
561
+ </thead>
562
+ <tbody>
563
+ <tr>
564
+ <td><i>any character</i></td>
565
+ <td>Matches exactly that character or a URI escaped version of it.</td>
566
+ </tr>
567
+ </tbody>
568
+ </table>
569
+
570
+ ### `rails`
571
+
572
+ Patterns with the syntax used in Rails route definitions.
573
+
574
+ ``` ruby
575
+ require 'mustermann'
576
+
577
+ pattern = Mustermann.new('/:example', type: :rails)
578
+ pattern === "/foo.bar" # => true
579
+ pattern === "/foo/bar" # => false
580
+ pattern.params("/foo.bar") # => { "example" => "foo.bar" }
581
+ pattern.params("/foo/bar") # => nil
582
+
583
+ pattern = Mustermann.new('/:example(/:optional)', type: :rails)
584
+ pattern === "/foo.bar" # => true
585
+ pattern === "/foo/bar" # => true
586
+ pattern.params("/foo.bar") # => { "example" => "foo.bar", "optional" => nil }
587
+ pattern.params("/foo/bar") # => { "example" => "foo", "optional" => "bar" }
588
+
589
+ pattern = Mustermann.new('/*example', type: :rails)
590
+ pattern === "/foo.bar" # => true
591
+ pattern === "/foo/bar" # => true
592
+ pattern.params("/foo.bar") # => { "example" => "foo.bar" }
593
+ pattern.params("/foo/bar") # => { "example" => "foo/bar" }
594
+ ```
595
+
596
+ <table>
597
+ <thead>
598
+ <tr>
599
+ <th>Syntax Element</th>
600
+ <th>Description</th>
601
+ </tr>
602
+ </thead>
603
+ <tbody>
604
+ <tr>
605
+ <td><b>:</b><i>name</i></td>
606
+ <td>
607
+ Captures anything but a forward slash in a semi-greedy fashion. Capture is named <i>name</i>.
608
+ Capture behavior can be modified with <a href="#capture"><tt>capture</tt></a> and <a href="#greedy"><tt>greedy</tt></a> option.
609
+ </td>
610
+ </tr>
611
+ <tr>
612
+ <td><b>*</b><i>name</i></td>
613
+ <td>
614
+ Captures anything in a non-greedy fashion. Capture is named <i>name</i>.
615
+ </td>
616
+ </tr>
617
+ <tr>
618
+ <td><b>(</b><i>expression</i><b>)</b></td>
619
+ <td>Enclosed <i>expression</i> is optional.</td>
620
+ </tr>
621
+ <tr>
622
+ <td><b>/</b></td>
623
+ <td>
624
+ Matches forward slash. Does not match URI encoded version of forward slash.
625
+ </td>
626
+ </tr>
627
+ <tr>
628
+ <td><i>any other character</i></td>
629
+ <td>Matches exactly that character or a URI encoded version of it.</td>
630
+ </tr>
631
+ </tbody>
632
+ </table>
633
+
634
+ ### `regexp`
635
+
636
+ Regular expression patterns, as used implemented by Ruby. Do not include characters for matching beginning or end of string/line.
637
+ This pattern type is also known as `regular` and the pattern class is `Mustermann::Regular` (located in `mustermann/regular`).
638
+
639
+ ``` ruby
640
+ require 'mustermann'
641
+
642
+ pattern = Mustermann.new('/(?<example>.*)', type: :regexp)
643
+ pattern === "/foo.bar" # => true
644
+ pattern === "/foo/bar" # => true
645
+ pattern.params("/foo.bar") # => { "example" => "foo.bar" }
646
+ pattern.params("/foo/bar") # => { "example" => "foo/bar" }
647
+ ```
648
+
649
+ <table>
650
+ <thead>
651
+ <tr>
652
+ <th>Syntax Element</th>
653
+ <th>Description</th>
654
+ </tr>
655
+ </thead>
656
+ <tbody>
657
+ <tr>
658
+ <td><i>any string</i></td>
659
+ <td>Interpreted as regular expression.</td>
660
+ </tr>
661
+ </tbody>
662
+ </table>
663
+
664
+ It is also possible to turn a proper Regexp instance into a pattern object by passing it to `Mustermann.new`:
665
+
666
+ ``` ruby
667
+ require 'mustermann'
668
+ Mustermann.new(/(?<example>.*)/).params("input") # => { "example" => "input" }
669
+ ```
670
+
671
+ ### `shell`
672
+
673
+ Shell patterns, as used in Bash or with `Dir.glob`.
674
+
675
+ ``` ruby
676
+ require 'mustermann'
677
+
678
+ pattern = Mustermann.new('/*', type: :shell)
679
+ pattern === "/foo.bar" # => true
680
+ pattern === "/foo/bar" # => false
681
+
682
+ pattern = Mustermann.new('/**/*', type: :shell)
683
+ pattern === "/foo.bar" # => true
684
+ pattern === "/foo/bar" # => true
685
+
686
+ pattern = Mustermann.new('/{foo,bar}', type: :shell)
687
+ pattern === "/foo" # => true
688
+ pattern === "/bar" # => true
689
+ pattern === "/baz" # => false
690
+ ```
691
+
692
+ <table>
693
+ <thead>
694
+ <tr>
695
+ <th>Syntax Element</th>
696
+ <th>Description</th>
697
+ </tr>
698
+ </thead>
699
+ <tbody>
700
+ <tr>
701
+ <td><b>*</b></td>
702
+ <td>Matches anything but a slash.</td>
703
+ </tr>
704
+ <tr>
705
+ <td><b>**</b></td>
706
+ <td>Matches anything.</td>
707
+ </tr>
708
+ <tr>
709
+ <td><b>[</b><i>set</i><b>]</b></td>
710
+ <td>Matches one character in <i>set</i>.</td>
711
+ </tr>
712
+ <tr>
713
+ <td><b>&#123;</b><i>a</i>,<i>b</i><b>&#125;</b></td>
714
+ <td>Matches <i>a</i> or <i>b</i>.</td>
715
+ </tr>
716
+ <tr>
717
+ <td><b>\</b><i>x</i></td>
718
+ <td>Matches <i>x</i> or URI encoded version of <i>x</i>. For instance <tt>\*</tt> matches <tt>*</tt>.</td>
719
+ </tr>
720
+ <tr>
721
+ <td><i>any other character</i></td>
722
+ <td>Matches exactly that character or a URI encoded version of it.</td>
723
+ </tr>
724
+ </tbody>
725
+ </table>
726
+
727
+ ### `simple`
728
+
729
+ Patterns as used by Sinatra 1.3. Useful for porting an application that relies on this behavior to a later Sinatra version and to make sure [Sinatra 2.0](#sinatra) patterns do not decrease performance. Simple patterns internally use the same code older Sinatra versions used for compiling the pattern. Error messages for broken patterns will therefore not be as informative as for other pattern implementations.
730
+
731
+ ``` ruby
732
+ require 'mustermann'
733
+
734
+ pattern = Mustermann.new('/:example', type: :simple)
735
+ pattern === "/foo.bar" # => true
736
+ pattern === "/foo/bar" # => false
737
+ pattern.params("/foo.bar") # => { "example" => "foo.bar" }
738
+ pattern.params("/foo/bar") # => nil
739
+
740
+ pattern = Mustermann.new('/:example/?:optional?', type: :simple)
741
+ pattern === "/foo.bar" # => true
742
+ pattern === "/foo/bar" # => true
743
+ pattern.params("/foo.bar") # => { "example" => "foo.bar", "optional" => nil }
744
+ pattern.params("/foo/bar") # => { "example" => "foo", "optional" => "bar" }
745
+
746
+ pattern = Mustermann.new('/*', type: :simple)
747
+ pattern === "/foo.bar" # => true
748
+ pattern === "/foo/bar" # => true
749
+ pattern.params("/foo.bar") # => { "splat" => ["foo.bar"] }
750
+ pattern.params("/foo/bar") # => { "splat" => ["foo/bar"] }
751
+ ```
752
+
753
+ <table>
754
+ <thead>
755
+ <tr>
756
+ <th>Syntax Element</th>
757
+ <th>Description</th>
758
+ </tr>
759
+ </thead>
760
+ <tbody>
761
+ <tr>
762
+ <td><b>:</b><i>name</i></td>
763
+ <td>
764
+ Captures anything but a forward slash in a greedy fashion. Capture is named <i>name</i>.
765
+ </td>
766
+ </tr>
767
+ <tr>
768
+ <td><b>*</b></td>
769
+ <td>
770
+ Captures anything in a non-greedy fashion. Capture is named splat.
771
+ It is always an array of captures, as you can use <tt>*</tt> more than once in a pattern.
772
+ </td>
773
+ </tr>
774
+ <tr>
775
+ <td><i>x</i><b>?</b></td>
776
+ <td>Makes <i>x</i> optional. For instance <tt>foo?</tt> matches <tt>foo</tt> or <tt>fo</tt>.</td>
777
+ </tr>
778
+ <tr>
779
+ <td><b>/</b></td>
780
+ <td>
781
+ Matches forward slash. Does not match URI encoded version of forward slash.
782
+ </td>
783
+ </tr>
784
+ <tr>
785
+ <td><i>any special character</i></td>
786
+ <td>Matches exactly that character or a URI encoded version of it.</td>
787
+ </tr>
788
+ <tr>
789
+ <td><i>any other character</i></td>
790
+ <td>Matches exactly that character.</td>
791
+ </tr>
792
+ </tbody>
793
+ </table>
794
+
795
+ ### `sinatra`
796
+
797
+ Sinatra 2.0 style patterns. The default used by Mustermann.
798
+
799
+ ``` ruby
800
+ require 'mustermann'
801
+
802
+ pattern = Mustermann.new('/:example')
803
+ pattern === "/foo.bar" # => true
804
+ pattern === "/foo/bar" # => false
805
+ pattern.params("/foo.bar") # => { "example" => "foo.bar" }
806
+ pattern.params("/foo/bar") # => nil
807
+
808
+ pattern = Mustermann.new('/\:example')
809
+ pattern === "/foo.bar" # => false
810
+ pattern === "/:example" # => true
811
+ pattern.params("/foo.bar") # => nil
812
+ pattern.params("/:example") # => {}
813
+
814
+ pattern = Mustermann.new('/:example(/:optional)?')
815
+ pattern === "/foo.bar" # => true
816
+ pattern === "/foo/bar" # => true
817
+ pattern.params("/foo.bar") # => { "example" => "foo.bar", "optional" => nil }
818
+ pattern.params("/foo/bar") # => { "example" => "foo", "optional" => "bar" }
819
+
820
+ pattern = Mustermann.new('/*')
821
+ pattern === "/foo.bar" # => true
822
+ pattern === "/foo/bar" # => true
823
+ pattern.params("/foo.bar") # => { "splat" => ["foo.bar"] }
824
+ pattern.params("/foo/bar") # => { "splat" => ["foo/bar"] }
825
+
826
+ pattern = Mustermann.new('/*example')
827
+ pattern === "/foo.bar" # => true
828
+ pattern === "/foo/bar" # => true
829
+ pattern.params("/foo.bar") # => { "example" => "foo.bar" }
830
+ pattern.params("/foo/bar") # => { "example" => "foo/bar" }
831
+ ```
832
+
833
+ <table>
834
+ <thead>
835
+ <tr>
836
+ <th>Syntax Element</th>
837
+ <th>Description</th>
838
+ </tr>
839
+ </thead>
840
+ <tbody>
841
+ <tr>
842
+ <td><b>:</b><i>name</i></td>
843
+ <td>
844
+ Captures anything but a forward slash in a semi-greedy fashion. Capture is named <i>name</i>.
845
+ Capture behavior can be modified with <a href="#capture"><tt>capture</tt></a> and <a href="#greedy"><tt>greedy</tt></a> option.
846
+ </td>
847
+ </tr>
848
+ <tr>
849
+ <td><b>*</b><i>name</i></td>
850
+ <td>
851
+ Captures anything in a non-greedy fashion. Capture is named <i>name</i>.
852
+ </td>
853
+ </tr>
854
+ <tr>
855
+ <td><b>*</b></td>
856
+ <td>
857
+ Captures anything in a non-greedy fashion. Capture is named splat.
858
+ It is always an array of captures, as you can use <tt>*</tt> more than once in a pattern.
859
+ </td>
860
+ </tr>
861
+ <tr>
862
+ <td><b>(</b><i>expression</i><b>)</b></td>
863
+ <td>
864
+ Enclosed <i>expression</i> is a group. Useful when combined with <tt>?</tt> to make it optional,
865
+ or to separate two elements that would otherwise be parsed as one.
866
+ </td>
867
+ </tr>
868
+ <tr>
869
+ <td><i>x</i><b>?</b></td>
870
+ <td>Makes <i>x</i> optional. For instance <tt>(foo)?</tt> matches <tt>foo</tt> or an empty string.</td>
871
+ </tr>
872
+ <tr>
873
+ <td><b>/</b></td>
874
+ <td>
875
+ Matches forward slash. Does not match URI encoded version of forward slash.
876
+ </td>
877
+ </tr>
878
+ <tr>
879
+ <td><b>\</b><i>x</i></td>
880
+ <td>Matches <i>x</i> or URI encoded version of <i>x</i>. For instance <tt>\*</tt> matches <tt>*</tt>.</td>
881
+ </tr>
882
+ <tr>
883
+ <td><i>any other character</i></td>
884
+ <td>Matches exactly that character or a URI encoded version of it.</td>
885
+ </tr>
886
+ </tbody>
887
+ </table>
888
+
889
+ ### `template`
890
+
891
+ Parses fully expanded URI templates as specified by [RFC 6570](http://tools.ietf.org/html/rfc6570).
892
+
893
+ Note that it differs from URI templates in that it takes the unescaped version of special character instead of the escaped version.
894
+
895
+ ``` ruby
896
+ require 'mustermann'
897
+
898
+ pattern = Mustermann.new('/{example}', type: :template)
899
+ pattern === "/foo.bar" # => true
900
+ pattern === "/foo/bar" # => false
901
+ pattern.params("/foo.bar") # => { "example" => "foo.bar" }
902
+ pattern.params("/foo/bar") # => nil
903
+
904
+ pattern = Mustermann.new("{/segments*}/{page}{.ext,cmpr:2}", type: :template)
905
+ pattern.params("/a/b/c.tar.gz") # => {"segments"=>["a","b"], "page"=>"c", "ext"=>"tar", "cmpr"=>"gz"}
906
+ ```
907
+
908
+ <table>
909
+ <thead>
910
+ <tr>
911
+ <th>Syntax Element</th>
912
+ <th>Description</th>
913
+ </tr>
914
+ </thead>
915
+ <tbody>
916
+ <tr>
917
+ <td><b>&#123;</b><i>o</i> <i>var</i> <i>m</i><b>,</b> <i>var</i> <i>m</i><b>,</b> ...<b>&#125;</b></td>
918
+ <td>
919
+ Captures expansion.
920
+ Operator <i>o</i>: <code>+ # . / ; ? &amp;</tt> or none.
921
+ Modifier <i>m</i>: <code>:num *</tt> or none.
922
+ </td>
923
+ </tr>
924
+ <tr>
925
+ <td><b>/</b></td>
926
+ <td>
927
+ Matches forward slash. Does not match URI encoded version of forward slash.
928
+ </td>
929
+ </tr>
930
+ <tr>
931
+ <td><i>any other character</i></td>
932
+ <td>Matches exactly that character or a URI encoded version of it.</td>
933
+ </tr>
934
+ </tbody>
935
+ </table>
936
+
937
+ The operators `+` and `#` will always match non-greedy, whereas all other operators match semi-greedy by default.
938
+ All modifiers and operators are supported. However, it does not parse lists as single values without the *explode* modifier (aka *star*).
939
+ Parametric operators (`;`, `?` and `&`) currently only match parameters in given order.
940
+
941
+ Please keep the following in mind:
942
+
943
+ > "Some URI Templates can be used in reverse for the purpose of variable matching: comparing the template to a fully formed URI in order to extract the variable parts from that URI and assign them to the named variables. Variable matching only works well if the template expressions are delimited by the beginning or end of the URI or by characters that cannot be part of the expansion, such as reserved characters surrounding a simple string expression. In general, regular expression languages are better suited for variable matching."
944
+ > &mdash; *RFC 6570, Sec 1.5: "Limitations"*
945
+
946
+ If you reuse the exact same templates and expose them via an external API meant for expansion,
947
+ you should set `uri_decode` to `false` in order to conform with the specification.
948
+
949
+ If you are looking for an alternative implementation that also supports expanding, check out [addressable](http://addressable.rubyforge.org/).
950
+
951
+ ## Mapper
952
+
953
+ You can use a mapper to transform strings according to two or more mappings:
954
+
955
+ ``` ruby
956
+ require 'mustermann/mapper'
957
+
958
+ mapper = Mustermann::Mapper.new("/:page(.:format)?" => ["/:page/view.:format", "/:page/view.html"])
959
+ mapper['/foo'] # => "/foo/view.html"
960
+ mapper['/foo.xml'] # => "/foo/view.xml"
961
+ mapper['/foo/bar'] # => "/foo/bar"
962
+ ```
963
+
964
+ ## Routers
965
+
966
+ Mustermann comes with basic router implementations that will call certain callbacks depending on the input.
967
+
968
+ ### Simple Router
969
+
970
+ The simple router chooses callbacks based on an input string.
971
+
972
+ ``` ruby
973
+ require 'mustermann/router/simple'
974
+
975
+ router = Mustermann::Router::Simple.new(default: 42)
976
+ router.on(':name', capture: :digit) { |string| string.to_i }
977
+ router.call("23") # => 23
978
+ router.call("example") # => 42
979
+ ```
980
+
981
+ ### Rack Router
982
+
983
+ This is not a full replacement for Rails, Sinatra, Cuba, etc, as it only cares about path based routing.
984
+
985
+ ``` ruby
986
+ require 'mustermann/router/rack'
987
+
988
+ router = Mustermann::Router::Rack.new do
989
+ on '/' do |env|
990
+ [200, {'Content-Type' => 'text/plain'}, ['Hello World!']]
991
+ end
992
+
993
+ on '/:name' do |env|
994
+ name = env['mustermann.params']['name']
995
+ [200, {'Content-Type' => 'text/plain'}, ["Hello #{name}!"]]
996
+ end
997
+
998
+ on '/something/*', call: SomeApp
999
+ end
1000
+
1001
+ # in a config.ru
1002
+ run router
1003
+ ```
1004
+
1005
+ ## Requirements
1006
+
1007
+ Mustermann has no dependencies besides a Ruby 2.0 compatible Ruby implementation.
1008
+
1009
+ It is known to work on **MRI 2.0** and **MRI trunk**.
1010
+
1011
+ **JRuby** is not yet fully supported. It is possible to run large parts of Mustermann by passing in `--2.0 -X-C` starting from JRuby 1.7.4. See [issue #2](https://github.com/rkh/mustermann/issues/2) for up to date information.
1012
+
1013
+ **Rubinius** is not yet able to parse the Mustermann source code. See [issue #14](https://github.com/rkh/mustermann/issues/14) for up to date information.
1014
+
1015
+ ## Release History
1016
+
1017
+ Mustermann follows [Semantic Versioning 2.0](http://semver.org/). Anything documented in the README or via YARD and not declared private is part of the public API.
1018
+
1019
+ ### Stable Releases
1020
+
1021
+ There have been no stable releases yet. The code base is considered solid but I don't know of anyone using it in production yet.
1022
+ As there has been no stable release yet, the API might still change, though I consider this unlikely.
1023
+
1024
+ ### Development Releases
1025
+
1026
+ * **Mustermann 0.3.1** (2014-09-12)
1027
+ * More Infos:
1028
+ [RubyGems.org](http://rubygems.org/gems/mustermann/versions/0.3.1),
1029
+ [RubyDoc.info](http://rubydoc.info/gems/mustermann/0.3.1/frames),
1030
+ [GitHub.com](https://github.com/rkh/mustermann/tree/v0.3.1)
1031
+ * Speed up pattern generation and matchin (thanks [Daniel Mendler](https://github.com/minad))
1032
+ * Small change so `Mustermann === Mustermann.new('...')` returns `true`.
1033
+ * **Mustermann 0.3.0** (2014-08-18)
1034
+ * More Infos:
1035
+ [RubyGems.org](http://rubygems.org/gems/mustermann/versions/0.3.0),
1036
+ [RubyDoc.info](http://rubydoc.info/gems/mustermann/0.3.0/frames),
1037
+ [GitHub.com](https://github.com/rkh/mustermann/tree/v0.3.0)
1038
+ * Add `regexp` pattern.
1039
+ * Add named splats to Sinatra patterns.
1040
+ * Add `Mustermann::Mapper`.
1041
+ * Improve duck typing support.
1042
+ * Improve documentation.
1043
+ * **Mustermann 0.2.0** (2013-08-24)
1044
+ * More Infos:
1045
+ [RubyGems.org](http://rubygems.org/gems/mustermann/versions/0.2.0),
1046
+ [RubyDoc.info](http://rubydoc.info/gems/mustermann/0.2.0/frames),
1047
+ [GitHub.com](https://github.com/rkh/mustermann/tree/v0.2.0)
1048
+ * Add first class expander objects.
1049
+ * Add params casting for expander.
1050
+ * Add simple router and rack router.
1051
+ * Add weak equality map to significantly improve performance.
1052
+ * Fix Ruby warnings.
1053
+ * Improve documentation.
1054
+ * Refactor pattern validation, AST transformations.
1055
+ * Increase test coverage (from 100%+ to 100%++).
1056
+ * Improve JRuby compatibility.
1057
+ * Work around bug in 2.0.0-p0.
1058
+ * **Mustermann 0.1.0** (2013-05-12)
1059
+ * More Infos:
1060
+ [RubyGems.org](http://rubygems.org/gems/mustermann/versions/0.1.0),
1061
+ [RubyDoc.info](http://rubydoc.info/gems/mustermann/0.1.0/frames),
1062
+ [GitHub.com](https://github.com/rkh/mustermann/tree/v0.1.0)
1063
+ * Add `Pattern#expand` for generating strings from patterns.
1064
+ * Add better internal API for working with the AST.
1065
+ * Improved documentation.
1066
+ * Avoids parsing the path twice when used as Sinatra extension.
1067
+ * Better exceptions for unknown pattern types.
1068
+ * Better handling of edge cases around extend.
1069
+ * More specs to ensure API stability.
1070
+ * Largely rework internals of Sinatra, Rails and Template patterns.
1071
+ * **Mustermann 0.0.1** (2013-04-27)
1072
+ * More Infos:
1073
+ [RubyGems.org](http://rubygems.org/gems/mustermann/versions/0.0.1),
1074
+ [RubyDoc.info](http://rubydoc.info/gems/mustermann/0.0.1/frames),
1075
+ [GitHub.com](https://github.com/rkh/mustermann/tree/v0.0.1)
1076
+ * Initial Release.
1077
+
1078
+ ### Upcoming Releases
1079
+
1080
+ * **Mustermann 1.0.0** (before Sinatra 2.0)
1081
+ * First stable release.