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/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
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
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.
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 keeps its runtime dependencies to a minimum and is fully covered with specs and documentation.
13
13
 
14
14
  Given a string pattern, Mustermann will turn it into an object that behaves like a regular expression and has comparable performance characteristics.
15
15
 
@@ -23,261 +23,323 @@ when Mustermann.new('foo/*') then puts "prefixed with foo"
23
23
  when Mustermann.new('*.pdf') then puts "it's a PDF"
24
24
  when Mustermann.new('*.png') then puts "it's an image"
25
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
26
 
30
- ``` ruby
31
27
  pattern = Mustermann.new('/:prefix/*.*')
32
28
  pattern.params('/a/b.c') # => { "prefix" => "a", splat => ["b", "c"] }
33
29
  ```
34
30
 
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.
31
+ ## Overview
32
+
33
+ ### Features
34
+
35
+ * **[Pattern Types](#-pattern-types):** Mustermann supports a wide variety of different pattern types, making it compatible with a large variety of existing software.
36
+ * **[Fine Grained Control](#-available-options):** You can easily adjust matching behavior and add constraints to the placeholders and capture groups.
37
+ * **[Binary Operators](#-binary-operators):** Patterns can be combined into composite patterns using binary operators.
38
+ * **[Regexp Look Alike](#-regexp-look-alike):** Mustermann patterns can be used as a replacement for regular expressions.
39
+ * **[Parameter Parsing](#-parameter-parsing):** Mustermann can parse matched parameters into a Sinatra-style "params" hash, including type casting.
40
+ * **[Peeking](#-peeking):** Lets you check if the beginning of a string matches a pattern.
41
+ * **[Expanding](#-expanding):** Besides parsing a parameters from an input string, a pattern object can also be used to generate a string from a set of parameters.
42
+ * **[Generating Templates](#-generating-templates):** This comes in handy when wanting to hand on patterns rather than fully expanded strings as part of an external API.
43
+ * **[Proc Look Alike](#-proc-look-alike):** Pass on a pattern instead of a block.
44
+ * **[Duck Typing](#-duck-typing):** You can create your own pattern-like objects by implementing `to_pattern`.
45
+ * **[Performance](#-performance):** Patterns are implemented with both performance and a low memory footprint in mind.
46
+
47
+ ### Additional Tooling
36
48
 
37
- ## Types and Options
49
+ These features are included in the library, but not loaded by default
38
50
 
39
- You can pass in additional options to take fine grained control over the pattern:
51
+ * **[String Scanner](#-string-scanner):** It comes with a version of Ruby's [StringScanner](http://ruby-doc.org/stdlib-2.0/libdoc/strscan/rdoc/StringScanner.html) made for pattern objects.
52
+ * **[Mapper](#-mapper):** A simple tool for mapping one string to another based on patterns.
53
+ * **[Routers](#-routers):** Model execution flow based on pattern matching. Comes with a simple Rack router.
54
+ * **[Sinatra Integration](#-sinatra-integration):** Mustermann can be used as a [Sinatra](http://www.sinatrarb.com/) extension. Sinatra 2.0 and beyond will use Mustermann by default.
55
+
56
+ ### More Infos
57
+
58
+ * **[Requirements](#-requirements):** Mustermann currently requires Ruby 2.0 or later.
59
+ * **[Release History](#-release-history):** See what's new.
60
+
61
+ <a name="-pattern-types"></a>
62
+ ## Pattern Types
63
+
64
+ Mustermann support multiple pattern types. A pattern type defines the syntax, matching semantics and whether certain features, like [expanding](#-expanding) and [generating templates](#-generating-templates), are available.
65
+
66
+ You can create a pattern of a certain type by passing `type` option to `Mustermann.new`:
40
67
 
41
68
  ``` ruby
42
- Mustermann.new('/:foo.:bar', capture: :alpha) # :foo and :bar will only match alphabetic characters
69
+ require 'mustermann'
70
+ pattern = Mustermann.new('/*/**', type: :shell)
43
71
  ```
44
72
 
45
- In fact, you can even completely change the pattern type:
73
+ Note that this will use the type as suggestion: When passing in a string argument, it will create a pattern of the given type, but it might choose a different type for other objects (a regular expression argument will always result in a [regexp](#-pattern-details-regexp) pattern, a symbol always in a [sinatra](#-pattern-details-sinatra) pattern, etc).
74
+
75
+ Alternatively, you can also load and instantiate the pattern type directly:
46
76
 
47
77
  ``` ruby
48
- Mustermann.new('/**/*.png', type: :shell)
78
+ require 'mustermann/shell'
79
+ pattern = Mustermann::Shell.new('/*/**')
49
80
  ```
50
81
 
51
- The available types are:
82
+ ### Available Types
52
83
 
53
84
  <table>
54
85
  <thead>
55
86
  <tr>
56
87
  <th>Type</th>
57
- <th>Description</th>
58
88
  <th>Example</th>
59
- <th>Available Options</th>
60
- <th>Additional Features</th>
89
+ <th>Compatible with</th>
90
+ <th>Notes</th>
61
91
  </tr>
62
92
  </thead>
63
93
  <tbody>
94
+
64
95
  <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>
96
+ <th><a href="#-pattern-details-cake"><tt>cake</tt></a></th>
97
+ <td><tt>/:prefix/**</tt></td>
98
+ <td><a href="http://cakephp.org/">CakePHP</a></td>
99
+ <td></td>
100
+ </tr>
101
+
102
+ <tr>
103
+ <th><a href="#-pattern-details-express"><tt>express</tt></a></th>
104
+ <td><tt>/:prefix+/:id(\d+)</tt></td>
68
105
  <td>
69
- <a href="#ignore_unknown_options"><tt>ignore_unknown_options</tt></a>,
70
- <a href="#uri_decode"><tt>uri_decode</tt></a>
106
+ <a href="http://expressjs.com/">Express</a>,
107
+ <a href="https://pillarjs.github.io/">pillar.js</a>
71
108
  </td>
72
109
  <td></td>
73
110
  </tr>
111
+
74
112
  <tr>
75
- <th><a href="#rails"><tt>rails</tt></a></th>
76
- <td>Rails style patterns</td>
77
- <td><tt>/:slug(.:ext)</tt></td>
113
+ <th><a href="#-pattern-details-flask"><tt>flask</tt></a></th>
114
+ <td><tt>/&lt;prefix&gt;/&lt;int:id&gt;</tt></td>
78
115
  <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>
116
+ <a href="http://flask.pocoo.org/">Flask</a>,
117
+ <a href="http://werkzeug.pocoo.org/">Werkzeug</a>,
118
+ <a href="http://bottlepy.org/docs/dev/index.html">Bottle</a>
85
119
  </td>
120
+ <td></td>
121
+ </tr>
122
+
123
+ <tr>
124
+ <th><a href="#-pattern-details-identity"><tt>identity</tt></a></th>
125
+ <td><tt>/image.png</tt></td>
126
+ <td>any software using strings</td>
86
127
  <td>
87
- <a href="#pattern_expanding">Expanding</a>
128
+ Exact string matching (no parameter parsing).
88
129
  </td>
89
130
  </tr>
131
+
90
132
  <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>
133
+ <th><a href="#-pattern-details-pyramid"><tt>pyramid</tt></a></th>
134
+ <td><tt>/{prefix:.*}/{id}</tt></td>
94
135
  <td>
95
- <a href="#ignore_unknown_options"><tt>ignore_unknown_options</tt></a>,
96
- <a href="#uri_decode"><tt>uri_decode</tt></a>
136
+ <a href="http://www.pylonsproject.org/projects/pyramid/about">Pyramid</a>,
137
+ <a href="http://www.pylonsproject.org/projects/pylons-framework/about">Pylons</a>
97
138
  </td>
98
139
  <td></td>
99
140
  </tr>
141
+
142
+ <tr>
143
+ <th><a href="#-pattern-details-rails"><tt>rails</tt></a></th>
144
+ <td><tt>/:slug(.:ext)</tt></td>
145
+ <td>
146
+ <a href="http://rubyonrails.org/">Ruby on Rails</a>,
147
+ <a href="https://github.com/joshbuddy/http_router">HTTP Router</a>,
148
+ <a href="http://lotusrb.org/">Lotus</a>,
149
+ <a href="http://www.scalatra.org/">Scalatra</a> (if <a href="http://www.scalatra.org/2.3/guides/http/routes.html#toc_248">configured</a>)</td>
150
+ <td></td>
151
+ </tr>
152
+
100
153
  <tr>
101
- <th><a href="#shell"><tt>shell</tt></th>
102
- <td>Unix style patterns</td>
103
- <td><tt>/*.{png,jpg}</tt></td>
154
+ <th><a href="#-pattern-details-regexp"><tt>regexp</tt></a></th>
155
+ <td><tt>/(?&lt;slug&gt;[^\/]+)</tt></td>
104
156
  <td>
105
- <a href="#ignore_unknown_options"><tt>ignore_unknown_options</tt></a>,
106
- <a href="#uri_decode"><tt>uri_decode</tt></a>
157
+ <a href="http://www.geocities.jp/kosako3/oniguruma/">Oniguruma</a>,
158
+ <a href="https://github.com/k-takata/Onigmo">Onigmo<a>,
159
+ regular expressions
107
160
  </td>
108
- <td></td>
161
+ <td>
162
+ Created when you pass a regexp to <tt>Mustermann.new</tt>.<br>
163
+ Does not support expanding or generating templates.
164
+ </td>
165
+ </tr>
166
+
167
+ <tr>
168
+ <th><a href="#-pattern-details-shell"><tt>shell</tt></a></th>
169
+ <td><tt>/*.{png,jpg}</tt></td>
170
+ <td>Unix Shell (bash, zsh)</td>
171
+ <td>Does not support expanding or generating templates.</td>
109
172
  </tr>
173
+
110
174
  <tr>
111
- <th><a href="#simple"><tt>simple</tt></a></th>
112
- <td>Sinatra 1.3 style patterns</td>
175
+ <th><a href="#-pattern-details-simple"><tt>simple</tt></a></th>
113
176
  <td><tt>/:slug.:ext</tt></td>
114
177
  <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>
178
+ <a href="http://www.sinatrarb.com/">Sinatra</a> (1.x),
179
+ <a href="http://www.scalatra.org/">Scalatra</a>,
180
+ <a href="http://perldancer.org/">Dancer</a>
181
+ </td>
182
+ <td>
183
+ Implementation is a direct copy from Sinatra 1.3.<br>
184
+ Does not support expanding or generating templates.
119
185
  </td>
120
- <td></td>
121
186
  </tr>
187
+
122
188
  <tr>
123
- <th><a href="#sinatra"><tt>sinatra</tt></a></th>
124
- <td>Sinatra 2.0 style patterns (default)</td>
189
+ <th><a href="#-pattern-details-sinatra"><tt>sinatra</tt></a></th>
125
190
  <td><tt>/:slug(.:ext)?</tt></td>
126
191
  <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>
192
+ <a href="http://www.sinatrarb.com/">Sinatra</a> (2.x),
193
+ <a href="http://www.padrinorb.com/">Padrino</a> (>= 0.13.0),
194
+ <a href="https://github.com/namusyaka/pendragon">Pendragon</a>,
195
+ <a href="https://github.com/kenichi/angelo">Angelo</a>
133
196
  </td>
134
197
  <td>
135
- <a href="#pattern_expanding">Expanding</a>
198
+ <u>This is the default</u> and the only type "invented here".<br>
199
+ It is a superset of <tt>simple</tt> and has a common subset with
200
+ <tt>template</tt> (and others).
136
201
  </td>
137
202
  </tr>
203
+
138
204
  <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>
205
+ <th><a href="#-pattern-details-sinatra"><tt>template</tt></a></th>
206
+ <td><tt>/{+pre}/{page}{?q}</tt></td>
142
207
  <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>
208
+ <a href="https://tools.ietf.org/html/rfc6570">RFC 6570</a>,
209
+ <a href="http://jsonapi.org/">JSON API</a>,
210
+ <a href="http://tools.ietf.org/html/draft-nottingham-json-home-02">JSON Home Documents</a>
211
+ and <a href="https://code.google.com/p/uri-templates/wiki/Implementations">many more</a>
152
212
  </td>
213
+ <td>Standardized URI templates, can be <a href="#-generating-templates">generated</a> from most other types.</td>
153
214
  </tr>
154
215
  </tbody>
155
216
  </table>
156
217
 
157
- See below for more details.
218
+ Any software using Mustermann is obviously compatible with at least one of the above.
158
219
 
159
- ## Sinatra Integration
220
+ ### Why so many?
160
221
 
161
- All patterns implement `match`, which means they can be dropped into Sinatra and other Rack routers:
222
+ Short answer: **Why not?**
162
223
 
163
- ``` ruby
164
- require 'sinatra'
165
- require 'mustermann'
224
+ You are probably concerned about one of these issues:
166
225
 
167
- get Mustermann.new('/:foo') do
168
- params[:foo]
169
- end
170
- ```
226
+ * **Code base complexity:** Most of these pattern implementations are very short and simple. The `cake` definition is two lines long, the `rails` implementation four lines.
227
+ * **Memory cost:** If you don't explicitly load one of these implementations and don't create any patterns of that type, the implementation itself will not be loaded. The only memory it uses it one more entry in the list of available pattern types.
171
228
 
172
- In fact, since using this with Sinatra is the main use case, it comes with a build-in extension for **Sinatra 1.x**.
229
+ <a name="-binary-operators"></a>
230
+ ## Binary Operators
231
+
232
+ Patterns can be combined via binary operators. These are:
233
+
234
+ * `|` (or): Resulting pattern matches if at least one of the input pattern matches.
235
+ * `&` (and): Resulting pattern matches if all input patterns match.
236
+ * `^` (xor): Resulting pattern matches if exactly one of the input pattern matches.
173
237
 
174
238
  ``` ruby
175
- require 'sinatra'
176
239
  require 'mustermann'
177
240
 
178
- register Mustermann
241
+ first = Mustermann.new('/foo/:input')
242
+ second = Mustermann.new('/:input/bar')
179
243
 
180
- # this will use Mustermann rather than the built-in pattern matching
181
- get '/:slug(.ext)?' do
182
- params[:slug]
183
- end
244
+ first | second === "/foo/foo" # => true
245
+ first | second === "/foo/bar" # => true
246
+
247
+ first & second === "/foo/foo" # => false
248
+ first & second === "/foo/bar" # => true
249
+
250
+ first ^ second === "/foo/foo" # => true
251
+ first ^ second === "/foo/bar" # => false
184
252
  ```
185
253
 
186
- ### Configuration
254
+ These resulting objects are fully functional pattern objects, allowing you to call methods like `params` or `to_proc` on them. Moreover, *or* patterns created solely from expandable patterns will also be expandable. The same logic also applies to generating templates from *or* patterns.
187
255
 
188
- You can change what pattern type you want to use for your app via the `pattern` option:
256
+ <a name="-regexp-look-alike"></a>
257
+ ## Regexp Look Alike
258
+
259
+ Pattern objects mimic Ruby's `Regexp` class by implementing `match`, `=~`, `===`, `names` and `named_captures`.
189
260
 
190
261
  ``` ruby
191
- require 'sinatra/base'
192
262
  require 'mustermann'
193
263
 
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
264
+ pattern = Mustermann.new('/:page')
265
+ pattern.match('/') # => nil
266
+ pattern.match('/home') # => #<MatchData "/home" page:"home">
267
+ pattern =~ '/home' # => 0
268
+ pattern === '/home' # => true (this allows using it in case statements)
269
+ pattern.names # => ['page']
270
+ pattern.names # => {"page"=>[1]}
271
+
272
+ pattern = Mustermann.new('/home', type: :identity)
273
+ pattern.match('/') # => nil
274
+ pattern.match('/home') # => #<Mustermann::SimpleMatch "/home">
275
+ pattern =~ '/home' # => 0
276
+ pattern === '/home' # => true (this allows using it in case statements)
277
+ pattern.names # => []
278
+ pattern.names # => {}
206
279
  ```
207
280
 
208
- You can use the same setting for options:
281
+ Moreover, patterns based on regular expressions (all but `identity` and `shell`) automatically convert to regular expressions when needed:
209
282
 
210
283
  ``` ruby
211
- require 'sinatra'
212
284
  require 'mustermann'
213
285
 
214
- register Mustermann
215
- set :pattern, capture: { ext: %w[png jpg html txt] }
286
+ pattern = Mustermann.new('/:page')
287
+ union = Regexp.union(pattern, /^$/)
216
288
 
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
289
+ union =~ "/foo" # => 0
290
+ union =~ "" # => 0
291
+
292
+ Regexp.try_convert(pattern) # => /.../
223
293
  ```
224
294
 
225
- It is also possible to pass in options to a specific route:
295
+ This way, unless some code explicitly checks the class for a regular expression, you should be able to pass in a pattern object instead even if the code in question was not written with Mustermann in mind.
296
+
297
+ <a name="-parameter-parsing"></a>
298
+ ## Parameter Parsing
299
+
300
+ Besides being a `Regexp` look-alike, Mustermann also adds a `params` method, that will give you a Sinatra-style hash:
226
301
 
227
302
  ``` ruby
228
- require 'sinatra'
229
303
  require 'mustermann'
230
304
 
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
305
+ pattern = Mustermann.new('/:prefix/*.*')
306
+ pattern.params('/a/b.c') # => { "prefix" => "a", splat => ["b", "c"] }
239
307
  ```
240
308
 
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.
309
+ For patterns with typed captures, it will also automatically convert them:
244
310
 
245
311
  ``` ruby
246
- require 'sinatra/base'
247
- require 'sinatra/respond_with'
248
312
  require 'mustermann'
249
313
 
250
- class MyApp < Sinatra::Base
251
- register Mustermann, Sinatra::RespondWith
252
- set :pattern, capture: { id: /\d+/ } # id will only match digits
314
+ pattern = Mustermann.new('/<prefix>/<int:id>', type: :flask)
315
+ pattern.params('/page/10') # => { "prefix" => "page", "id" => 10 }
316
+ ```
253
317
 
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
318
+ <a name="-peeking"></a>
319
+ ## Peeking
259
320
 
260
- get '/:id' do
261
- not_found unless page = Page.find params[:id]
262
- respond_with(page)
263
- end
264
- end
265
- ```
321
+ Peeking gives the option to match a pattern against the beginning of a string rather the full string. Patterns come with four methods for peeking:
266
322
 
267
- ### Why would I want this?
323
+ * `peek` returns the matching substring.
324
+ * `peek_size` returns the number of characters matching.
325
+ * `peek_match` will return a `MatchData` or `Mustermann::SimpleMatch` (just like `match` does for the full string)
326
+ * `peek_params` will return the `params` hash parsed from the substring and the number of characters.
268
327
 
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
328
+ All of the above will turn `nil` if there was no match.
274
329
 
275
- ### Why not include this in Sinatra 1.x?
330
+ ``` ruby
331
+ require 'mustermann'
276
332
 
277
- * It would introduce breaking changes, even though these would be minor
278
- * Like Sinatra 2.0, Mustermann requires Ruby 2.0 or newer
333
+ pattern = Mustermann.new('/:prefix')
334
+ pattern.peek('/foo/bar') # => '/foo'
335
+ pattern.peek_size('/foo/bar') # => 4
336
+
337
+ path_info = '/foo/bar'
338
+ params, size = patter.peek_params(path_info) # params == { "prefix" => "foo" }
339
+ rest = path_info[size..-1] # => "/bar"
340
+ ```
279
341
 
280
- <a name="pattern_expanding"></a>
342
+ <a name="-expanding"></a>
281
343
  ## Expanding
282
344
 
283
345
  Similarly to parsing, it is also possible to generate a string from a pattern by expanding it with a hash.
@@ -322,6 +384,8 @@ expander = Mustermann::Expander.new(pattern)
322
384
  You can add patterns to an expander object via `<<`:
323
385
 
324
386
  ``` ruby
387
+ require 'mustermann'
388
+
325
389
  expander = Mustermann::Expander.new
326
390
  expander << "/users/:user_id"
327
391
  expander << "/pages/:page_id"
@@ -333,6 +397,8 @@ expander.expand(page_id: 58) # => "/pages/58"
333
397
  You can set pattern options when creating the expander:
334
398
 
335
399
  ``` ruby
400
+ require 'mustermann'
401
+
336
402
  expander = Mustermann::Expander.new(type: :template)
337
403
  expander << "/users/{user_id}"
338
404
  expander << "/pages/{page_id}"
@@ -341,6 +407,8 @@ expander << "/pages/{page_id}"
341
407
  Additionally, it is possible to combine patterns of different types:
342
408
 
343
409
  ``` ruby
410
+ require 'mustermann'
411
+
344
412
  expander = Mustermann::Expander.new
345
413
  expander << Mustermann.new("/users/{user_id}", type: :template)
346
414
  expander << Mustermann.new("/pages/:page_id", type: :rails)
@@ -351,6 +419,8 @@ expander << Mustermann.new("/pages/:page_id", type: :rails)
351
419
  The handling of additional values passed in to `expand` can be changed by setting the `additional_values` option:
352
420
 
353
421
  ``` ruby
422
+ require 'mustermann'
423
+
354
424
  expander = Mustermann::Expander.new("/:slug", additional_values: :raise)
355
425
  expander.expand(slug: "foo", value: "bar") # raises Mustermann::ExpandError
356
426
 
@@ -361,8 +431,233 @@ expander = Mustermann::Expander.new("/:slug", additional_values: :append)
361
431
  expander.expand(slug: "foo", value: "bar") # => "/foo?value=bar"
362
432
  ```
363
433
 
434
+ It is also possible to pass this directly to the `expand` call:
435
+
436
+ ``` ruby
437
+ require 'mustermann'
438
+
439
+ pattern = Mustermann.new('/:slug')
440
+ pattern.expan(:append, slug: "foo", value: "bar") # => "/foo?value=bar"
441
+ ```
442
+
443
+ <a name="-generating-templates"></a>
444
+ ## Generating Templates
445
+
446
+ ... TODO ...
447
+
448
+ <a name="-proc-look-alike"></a>
449
+ ## Proc Look Alike
450
+
451
+ Patterns implement `to_proc`:
452
+
453
+ ``` ruby
454
+ require 'mustermann'
455
+ pattern = Mustermann.new('/foo')
456
+ callback = pattern.to_proc # => #<Proc>
457
+
458
+ callback.call('/foo') # => true
459
+ callback.call('/bar') # => false
460
+ ```
461
+
462
+ They can therefore be easily passed to methods expecting a block:
463
+
464
+ ``` ruby
465
+ require 'mustermann'
466
+
467
+ list = ["foo", "example@email.com", "bar"]
468
+ pattern = Mustermann.new(":name@:domain.:tld")
469
+ email = list.detect(&pattern) # => "example@email.com"
470
+ ```
471
+
472
+ <a name="-string-scanner"></a>
473
+ ## String Scanner
474
+
475
+ ... TODO ...
476
+
477
+ <a name="-mapper"></a>
478
+ ## Mapper
479
+
480
+
481
+ You can use a mapper to transform strings according to two or more mappings:
482
+
483
+ ``` ruby
484
+ require 'mustermann/mapper'
485
+
486
+ mapper = Mustermann::Mapper.new("/:page(.:format)?" => ["/:page/view.:format", "/:page/view.html"])
487
+ mapper['/foo'] # => "/foo/view.html"
488
+ mapper['/foo.xml'] # => "/foo/view.xml"
489
+ mapper['/foo/bar'] # => "/foo/bar"
490
+ ```
491
+
492
+ <a name="-routers"></a>
493
+ ## Routers
494
+
495
+ Mustermann comes with basic router implementations that will call certain callbacks depending on the input.
496
+
497
+ ### Simple Router
498
+
499
+ The simple router chooses callbacks based on an input string.
500
+
501
+ ``` ruby
502
+ require 'mustermann/router/simple'
503
+
504
+ router = Mustermann::Router::Simple.new(default: 42)
505
+ router.on(':name', capture: :digit) { |string| string.to_i }
506
+ router.call("23") # => 23
507
+ router.call("example") # => 42
508
+ ```
509
+
510
+ ### Rack Router
511
+
512
+ This is not a full replacement for Rails, Sinatra, Cuba, etc, as it only cares about path based routing.
513
+
514
+ ``` ruby
515
+ require 'mustermann/router/rack'
516
+
517
+ router = Mustermann::Router::Rack.new do
518
+ on '/' do |env|
519
+ [200, {'Content-Type' => 'text/plain'}, ['Hello World!']]
520
+ end
521
+
522
+ on '/:name' do |env|
523
+ name = env['mustermann.params']['name']
524
+ [200, {'Content-Type' => 'text/plain'}, ["Hello #{name}!"]]
525
+ end
526
+
527
+ on '/something/*', call: SomeApp
528
+ end
529
+
530
+ # in a config.ru
531
+ run router
532
+ ```
533
+ <a name="-sinatra-integration"></a>
534
+ ## Sinatra Integration
535
+
536
+ All patterns implement `match`, which means they can be dropped into Sinatra and other Rack routers:
537
+
538
+ ``` ruby
539
+ require 'sinatra'
540
+ require 'mustermann'
541
+
542
+ get Mustermann.new('/:foo') do
543
+ params[:foo]
544
+ end
545
+ ```
546
+
547
+ In fact, since using this with Sinatra is the main use case, it comes with a build-in extension for **Sinatra 1.x**.
548
+
549
+ ``` ruby
550
+ require 'sinatra'
551
+ require 'mustermann'
552
+
553
+ register Mustermann
554
+
555
+ # this will use Mustermann rather than the built-in pattern matching
556
+ get '/:slug(.ext)?' do
557
+ params[:slug]
558
+ end
559
+ ```
560
+
561
+ ### Configuration
562
+
563
+ You can change what pattern type you want to use for your app via the `pattern` option:
564
+
565
+ ``` ruby
566
+ require 'sinatra/base'
567
+ require 'mustermann'
568
+
569
+ class MyApp < Sinatra::Base
570
+ register Mustermann
571
+ set :pattern, type: :shell
572
+
573
+ get '/images/*.png' do
574
+ send_file request.path_info
575
+ end
576
+
577
+ get '/index{.htm,.html,}' do
578
+ erb :index
579
+ end
580
+ end
581
+ ```
582
+
583
+ You can use the same setting for options:
584
+
585
+ ``` ruby
586
+ require 'sinatra'
587
+ require 'mustermann'
588
+
589
+ register Mustermann
590
+ set :pattern, capture: { ext: %w[png jpg html txt] }
591
+
592
+ get '/:slug(.:ext)?' do
593
+ # slug will be 'foo' for '/foo.png'
594
+ # slug will be 'foo.bar' for '/foo.bar'
595
+ # slug will be 'foo.bar' for '/foo.bar.html'
596
+ params[:slug]
597
+ end
598
+ ```
599
+
600
+ It is also possible to pass in options to a specific route:
601
+
602
+ ``` ruby
603
+ require 'sinatra'
604
+ require 'mustermann'
605
+
606
+ register Mustermann
607
+
608
+ get '/:slug(.:ext)?', pattern: { greedy: false } do
609
+ # slug will be 'foo' for '/foo.png'
610
+ # slug will be 'foo' for '/foo.bar'
611
+ # slug will be 'foo' for '/foo.bar.html'
612
+ params[:slug]
613
+ end
614
+ ```
615
+
616
+ Of course, all of the above can be combined.
617
+ Moreover, the `capture` and the `except` option can be passed to route directly.
618
+ And yes, this also works with `before` and `after` filters.
619
+
620
+ ``` ruby
621
+ require 'sinatra/base'
622
+ require 'sinatra/respond_with'
623
+ require 'mustermann'
624
+
625
+ class MyApp < Sinatra::Base
626
+ register Mustermann, Sinatra::RespondWith
627
+ set :pattern, capture: { id: /\d+/ } # id will only match digits
628
+
629
+ # only capture extensions known to Rack
630
+ before '*:ext', capture: Rack::Mime::MIME_TYPES.keys do
631
+ content_type params[:ext] # set Content-Type
632
+ request.path_info = params[:splat].first # drop the extension
633
+ end
634
+
635
+ get '/:id' do
636
+ not_found unless page = Page.find params[:id]
637
+ respond_with(page)
638
+ end
639
+ end
640
+ ```
641
+
642
+ ### Why would I want this?
643
+
644
+ * It gives you fine grained control over the pattern matching
645
+ * Allows you to use different pattern styles in your app
646
+ * The default is more robust and powerful than the built-in patterns
647
+ * Sinatra 2.0 will use Mustermann internally
648
+ * Better exceptions for broken route syntax
649
+
650
+ ### Why not include this in Sinatra 1.x?
651
+
652
+ * It would introduce breaking changes, even though these would be minor
653
+ * Like Sinatra 2.0, Mustermann requires Ruby 2.0 or newer
654
+
655
+ <a name="-duck-typing"></a>
364
656
  ## Duck Typing
365
657
 
658
+ <a name="-duck-typing-to-pattern"></a>
659
+ ### `to_pattern`
660
+
366
661
  All methods converting string input to pattern objects will also accept any arbitrary object that implements `to_pattern`:
367
662
 
368
663
  ``` ruby
@@ -405,28 +700,43 @@ end
405
700
  MyObject.new.to_pattern # => #<Mustermann::Sinatra:"/foo">
406
701
  ```
407
702
 
408
- ## Partial Loading and Thread Safety
703
+ <a name="-duck-typing-respond-to"></a>
704
+ ### `respond_to?`
409
705
 
410
- Pattern objects are generally assumed to be thread-safe. You can easily match strings against the same pattern object concurrently.
706
+ You can and should use `respond_to?` to check if a pattern supports certain features.
411
707
 
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.
708
+ ``` ruby
709
+ require 'mustermann'
710
+ pattern = Mustermann.new("/")
413
711
 
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.
712
+ puts "supports expanding" if pattern.respond_to? :expand
713
+ puts "supports generating templates" if pattern.respond_to? :to_templates
714
+ ```
415
715
 
416
- To avoid this, you can load the pattern types you need manually:
716
+ Alternatively, you can handle a `NotImplementedError` raised from such a method.
417
717
 
418
718
  ``` ruby
419
- require 'mustermann/sinatra'
420
- Mustermann::Sinatra.new('/:foo')
719
+ require 'mustermann'
720
+ pattern = Mustermann.new("/")
721
+
722
+ begin
723
+ p pattern.to_templates
724
+ rescue NotImplementedError
725
+ puts "does not support generating templates"
726
+ end
421
727
  ```
422
728
 
423
- ## Options
729
+ This behavior corresponds to what Ruby does, for instance for [`fork`](http://ruby-doc.org/core-2.1.1/NotImplementedError.html).
424
730
 
731
+ <a name="-available-options"></a>
732
+ ## Available Options
733
+
734
+ <a name="-available-options--capture"></a>
425
735
  ### `capture`
426
736
 
427
- Supported by: `rails`, `sinatra`, `template`
737
+ Supported by: All types except `identity`, `shell` and `simple` patterns.
428
738
 
429
- **Sinatra**, **URI template** and **Rails** patterns support changing the way named captures work via the `capture` options.
739
+ Most pattern types support changing the strings named captures will match via the `capture` options.
430
740
 
431
741
  Possible values for a capture:
432
742
 
@@ -449,9 +759,10 @@ Mustermann.new('/:id.:ext', capture: { id: /\d+/, ext: ['png', 'jpg'] })
449
759
 
450
760
  Available POSIX character classes are: `:alnum`, `:alpha`, `:blank`, `:cntrl`, `:digit`, `:graph`, `:lower`, `:print`, `:punct`, `:space`, `:upper`, `:xdigit`, `:word` and `:ascii`.
451
761
 
762
+ <a name="-available-options--except"></a>
452
763
  ### `except`
453
764
 
454
- Supported by: `rails`, `sinatra`, `template`
765
+ Supported by: All types except `identity`, `shell` and `simple` patterns.
455
766
 
456
767
  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
768
 
@@ -469,13 +780,15 @@ pattern === '/foo.jpg' # => true
469
780
  pattern === '/foo.png' # => false
470
781
  ```
471
782
 
783
+ <a name="-available-options--greedy"></a>
472
784
  ### `greedy`
473
785
 
474
- Supported by: `rails`, `simple`, `sinatra`, `template`. Default value: `true`
786
+ Supported by: All types except `identity` and `shell` patterns.
787
+ Default value: `true`
475
788
 
476
789
  **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
790
 
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.
791
+ **All other** supported 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
792
 
480
793
  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
794
 
@@ -487,11 +800,13 @@ pattern = Mustermann.new(':a.:b', greedy: false)
487
800
  pattern.match('a.b.c.d') # => #<MatchData a:"a" b:"b.c.d">
488
801
  ```
489
802
 
803
+ <a name="-available-options--space_matches_plus"></a>
490
804
  ### `space_matches_plus`
491
805
 
492
- Supported by: `rails`, `simple`, `sinatra`, `template`. Default value: `true`
806
+ Supported by: All types except `identity`, `regexp` and `shell` patterns.
807
+ Default value: `true`
493
808
 
494
- **Sinatra**, **Simple**, **URI template** and **Rails** patterns will by default also match a plus sign for a space in the pattern:
809
+ Most pattern types will by default also match a plus sign for a space in the pattern:
495
810
 
496
811
  ``` ruby
497
812
  Mustermann.new('a b') === 'a+b' # => true
@@ -511,9 +826,11 @@ pattern.match('a b')[:x] # => 'a b'
511
826
  pattern.match('a+b')[:x] # => 'a+b'
512
827
  ````
513
828
 
829
+ <a name="-available-options--uri_decode"></a>
514
830
  ### `uri_decode`
515
831
 
516
- Supported by all patterns. Default value: `true`
832
+ Supported by all pattern types.
833
+ Default value: `true`
517
834
 
518
835
  Usually, characters in the pattern will also match the URI encoded version of these characters:
519
836
 
@@ -529,28 +846,134 @@ Mustermann.new('a b', uri_decode: false) === 'a b' # => true
529
846
  Mustermann.new('a b', uri_decode: false) === 'a%20b' # => false
530
847
  ```
531
848
 
849
+ <a name="-available-options--converters"></a>
850
+ ### `converters`
851
+
852
+ Supported by `flask` patterns.
853
+ Default value: `{}`
854
+
855
+ [Flask patterns](#-pattern-details-flask) support registering custom converters.
856
+
857
+ A converter object may implement any of the following methods:
858
+
859
+ * `convert`: Should return a block converting a string value to whatever value should end up in the `params` hash.
860
+ * `constraint`: Should return a regular expression limiting which input string will match the capture.
861
+ * `new`: Returns an object that may respond to `convert` and/or `constraint` as described above. Any arguments used for the converter inside the pattern will be passed to `new`.
862
+
863
+ ``` ruby
864
+ require 'mustermann'
865
+
866
+ SimpleConverter = Struct.new(:constraint, :convert)
867
+ id_converter = SimpleConverter.new(/\d/, -> s { s.to_i })
868
+
869
+ class NumConverter
870
+ def initialize(base: 10)
871
+ @base = Integer(base)
872
+ end
873
+
874
+ def convert
875
+ -> s { s.to_i(@base) }
876
+ end
877
+
878
+ def constraint
879
+ @base > 10 ? /[\da-#{(@base-1).to_s(@base)}]/ : /[0-#{@base-1}]/
880
+ end
881
+ end
882
+
883
+ pattern = Mustermann.new('/<id:id>/<num(base=8):oct>/<num(base=16):hex>',
884
+ type: :flask, converters: { id: id_converter, num: NumConverter})
885
+
886
+ pattern.params('/10/12/f1') # => {"id"=>10, "oct"=>10, "hex"=>241}
887
+ ```
888
+
889
+ <a name="-available-options--ignore_unknown_options"></a>
532
890
  ### `ignore_unknown_options`
533
891
 
534
- Supported by all patterns. Default value: `false`
892
+ Supported by all patterns.
893
+ Default value: `false`
535
894
 
536
895
  If you pass an option in that is not supported by the specific pattern type, Mustermann will raise an `ArgumentError`.
537
896
  By setting `ignore_unknown_options` to `true`, it will happily ignore the option.
538
897
 
539
- ## Pattern Types
898
+ <a name="-performance"></a>
899
+ ## Performance
540
900
 
541
- ### `identity`
901
+ 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.
542
902
 
543
- Identity patterns are strings that have to match the input exactly.
903
+ Pattern objects should be treated as immutable. Their internals have been designed for both performance and low memory usage. To reduce pattern compilation, `Mustermann.new` and `Mustermann::Pattern.new` might return the same instance when given the same arguments, if that instance has not yet been garbage collected. However, this is not guaranteed, so do not rely on object identity.
544
904
 
545
- ``` ruby
546
- require 'mustermann'
905
+ ### String Matching
547
906
 
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
- ```
907
+ When using a pattern instead of a regular expression for string matching, performance will usually be comparable.
908
+
909
+ In certain cases, Mustermann might outperform naive, equivalent regular expressions. It achieves this by using look-ahead and atomic groups in ways that work well with a backtracking, NFA-based regular expression engine (such as the Oniguruma/Onigmo engine used by Ruby). It can be difficult and error prone to construct complex regular expressions using these techniques by hand. This only applies to patterns generating an AST internally (all but [identity](#-pattern-details-identity), [shell](#-pattern-details-shell), [simple](#-pattern-details-simple) and [regexp](#-pattern-details-regexp) patterns).
910
+
911
+ When using a Mustermann pattern as a direct Regexp replacement (ie, via methods like `=~`, `match` or `===`), the overhead will be a single method dispatch, which some Ruby implementations might even eliminate with method inlining. This only applies to patterns using a regular expression internally (all but [identity](#-pattern-details-identity) and [shell](#-pattern-details-shell) patterns).
912
+
913
+ ### Expanding
914
+
915
+ Pattern expansion significantly outperforms other, widely used Ruby tools for generating URLs from URL patterns in most use cases.
916
+
917
+ This comes with a few trade-offs:
918
+
919
+ * As with pattern compilation, as much computation as possible has been shifted to compiling expansion rules. This will add compilation overhead, which is why patterns only generate these rules on the first invocation to `Mustermann::Pattern#expand`. Create a `Mustermann::Expander` instance yourself to get better control over the point in time this computation should happen.
920
+ * Memory is sacrificed in favor of performance: The size of the expander object will grow linear with the number of possible combination for expansion keys ("/:foo/:bar" has one such combination, but "/(:foo/)?:bar?" has four)
921
+ * Parsing a params hash from a string generated from another params hash might not result in two identical hashes, and vice versa. Specifically, expanding ignores capture constraints, type casting and greediness.
922
+ * Partial expansion is (currently) not supported.
923
+
924
+ ## Details on Pattern Types
925
+
926
+ <a name="-pattern-details-cake"></a>
927
+ ### `cake`
928
+
929
+ **Supported options:**
930
+ [`capture`](#-available-options--capture),
931
+ [`except`](#-available-options--except),
932
+ [`greedy`](#-available-options--greedy),
933
+ [`space_matches_plus`](#-available-options--space_matches_plus),
934
+ [`uri_decode`](#-available-options--uri_decode),
935
+ [`ignore_unknown_options`](#-available-options--ignore_unknown_options).
936
+
937
+ **External documentation:**
938
+ [CakePHP 2.0 Routing](http://book.cakephp.org/2.0/en/development/routing.html),
939
+ [CakePHP 3.0 Routing](http://book.cakephp.org/3.0/en/development/routing.html)
940
+
941
+ <a name="-pattern-details-express"></a>
942
+ ### `express`
943
+
944
+ **Supported options:**
945
+ [`capture`](#-available-options--capture),
946
+ [`except`](#-available-options--except),
947
+ [`greedy`](#-available-options--greedy),
948
+ [`space_matches_plus`](#-available-options--space_matches_plus),
949
+ [`uri_decode`](#-available-options--uri_decode),
950
+ [`ignore_unknown_options`](#-available-options--ignore_unknown_options).
951
+
952
+ **External documentation:**
953
+ [path-to-regexp](https://github.com/pillarjs/path-to-regexp#path-to-regexp),
954
+ [live demo](http://forbeslindesay.github.io/express-route-tester/)
955
+
956
+ <a name="-pattern-details-flask"></a>
957
+ ### `flask`
958
+
959
+ **Supported options:**
960
+ [`capture`](#-available-options--capture),
961
+ [`except`](#-available-options--except),
962
+ [`greedy`](#-available-options--greedy),
963
+ [`space_matches_plus`](#-available-options--space_matches_plus),
964
+ [`uri_decode`](#-available-options--uri_decode),
965
+ [`converters`](#-available-options--converters),
966
+ [`ignore_unknown_options`](#-available-options--ignore_unknown_options).
967
+
968
+ **External documentation:**
969
+ [Werkzeug: URL Routing](http://werkzeug.pocoo.org/docs/0.9/routing/)
970
+
971
+ <a name="-pattern-details-identity"></a>
972
+ ### `identity`
973
+
974
+ **Supported options:**
975
+ [`uri_decode`](#-available-options--uri_decode),
976
+ [`ignore_unknown_options`](#-available-options--ignore_unknown_options).
554
977
 
555
978
  <table>
556
979
  <thead>
@@ -567,31 +990,27 @@ pattern.params("/:example") # => {}
567
990
  </tbody>
568
991
  </table>
569
992
 
570
- ### `rails`
993
+ <a name="-pattern-details-pyramid"></a>
994
+ ### `pyramid`
571
995
 
572
- Patterns with the syntax used in Rails route definitions.
996
+ **Supported options:**
997
+ [`capture`](#-available-options--capture),
998
+ [`except`](#-available-options--except),
999
+ [`greedy`](#-available-options--greedy),
1000
+ [`space_matches_plus`](#-available-options--space_matches_plus),
1001
+ [`uri_decode`](#-available-options--uri_decode),
1002
+ [`ignore_unknown_options`](#-available-options--ignore_unknown_options).
573
1003
 
574
- ``` ruby
575
- require 'mustermann'
1004
+ <a name="-pattern-details-rails"></a>
1005
+ ### `rails`
576
1006
 
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
- ```
1007
+ **Supported options:**
1008
+ [`capture`](#-available-options--capture),
1009
+ [`except`](#-available-options--except),
1010
+ [`greedy`](#-available-options--greedy),
1011
+ [`space_matches_plus`](#-available-options--space_matches_plus),
1012
+ [`uri_decode`](#-available-options--uri_decode),
1013
+ [`ignore_unknown_options`](#-available-options--ignore_unknown_options).
595
1014
 
596
1015
  <table>
597
1016
  <thead>
@@ -631,20 +1050,12 @@ pattern.params("/foo/bar") # => { "example" => "foo/bar" }
631
1050
  </tbody>
632
1051
  </table>
633
1052
 
1053
+ <a name="-pattern-details-regexp"></a>
634
1054
  ### `regexp`
635
1055
 
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
- ```
1056
+ **Supported options:**
1057
+ [`uri_decode`](#-available-options--uri_decode),
1058
+ [`ignore_unknown_options`](#-available-options--ignore_unknown_options).
648
1059
 
649
1060
  <table>
650
1061
  <thead>
@@ -661,33 +1072,12 @@ pattern.params("/foo/bar") # => { "example" => "foo/bar" }
661
1072
  </tbody>
662
1073
  </table>
663
1074
 
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
-
1075
+ <a name="-pattern-details-shell"></a>
671
1076
  ### `shell`
672
1077
 
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
- ```
1078
+ **Supported options:**
1079
+ [`uri_decode`](#-available-options--uri_decode),
1080
+ [`ignore_unknown_options`](#-available-options--ignore_unknown_options).
691
1081
 
692
1082
  <table>
693
1083
  <thead>
@@ -724,31 +1114,14 @@ pattern === "/baz" # => false
724
1114
  </tbody>
725
1115
  </table>
726
1116
 
1117
+ <a name="-pattern-details-simple"></a>
727
1118
  ### `simple`
728
1119
 
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
- ```
1120
+ **Supported options:**
1121
+ [`greedy`](#-available-options--greedy),
1122
+ [`space_matches_plus`](#-available-options--space_matches_plus),
1123
+ [`uri_decode`](#-available-options--uri_decode),
1124
+ [`ignore_unknown_options`](#-available-options--ignore_unknown_options).
752
1125
 
753
1126
  <table>
754
1127
  <thead>
@@ -792,43 +1165,16 @@ pattern.params("/foo/bar") # => { "splat" => ["foo/bar"] }
792
1165
  </tbody>
793
1166
  </table>
794
1167
 
1168
+ <a name="-pattern-details-sinatra"></a>
795
1169
  ### `sinatra`
796
1170
 
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
- ```
1171
+ **Supported options:**
1172
+ [`capture`](#-available-options--capture),
1173
+ [`except`](#-available-options--except),
1174
+ [`greedy`](#-available-options--greedy),
1175
+ [`space_matches_plus`](#-available-options--space_matches_plus),
1176
+ [`uri_decode`](#-available-options--uri_decode),
1177
+ [`ignore_unknown_options`](#-available-options--ignore_unknown_options).
832
1178
 
833
1179
  <table>
834
1180
  <thead>
@@ -839,23 +1185,23 @@ pattern.params("/foo/bar") # => { "example" => "foo/bar" }
839
1185
  </thead>
840
1186
  <tbody>
841
1187
  <tr>
842
- <td><b>:</b><i>name</i></td>
1188
+ <td><b>:</b><i>name</i> <i><b>or</b></i> <b>&#123;</b><i>name</i><b>&#125;</b></td>
843
1189
  <td>
844
1190
  Captures anything but a forward slash in a semi-greedy fashion. Capture is named <i>name</i>.
845
1191
  Capture behavior can be modified with <a href="#capture"><tt>capture</tt></a> and <a href="#greedy"><tt>greedy</tt></a> option.
846
1192
  </td>
847
1193
  </tr>
848
1194
  <tr>
849
- <td><b>*</b><i>name</i></td>
1195
+ <td><b>*</b><i>name</i> <i><b>or</b></i> <b>&#123;+</b><i>name</i><b>&#125;</b></td>
850
1196
  <td>
851
1197
  Captures anything in a non-greedy fashion. Capture is named <i>name</i>.
852
1198
  </td>
853
1199
  </tr>
854
1200
  <tr>
855
- <td><b>*</b></td>
1201
+ <td><b>*</b> <i><b>or</b></i> <b>&#123;+splat&#125;</b></td>
856
1202
  <td>
857
1203
  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.
1204
+ It is always an array of captures, as you can use it more than once in a pattern.
859
1205
  </td>
860
1206
  </tr>
861
1207
  <tr>
@@ -865,9 +1211,15 @@ pattern.params("/foo/bar") # => { "example" => "foo/bar" }
865
1211
  or to separate two elements that would otherwise be parsed as one.
866
1212
  </td>
867
1213
  </tr>
1214
+ <tr>
1215
+ <td><b>(</b><i>expression</i><b>|</b><i>expression</i><b>|</b><i>...</i><b>)</b></td>
1216
+ <td>
1217
+ Will match anything matching the nested expressions. May contain any other syntax element, including captures.
1218
+ </td>
1219
+ </tr>
868
1220
  <tr>
869
1221
  <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>
1222
+ <td>Makes <i>x</i> optional. For instance, <tt>(foo)?</tt> matches <tt>foo</tt> or an empty string.</td>
871
1223
  </tr>
872
1224
  <tr>
873
1225
  <td><b>/</b></td>
@@ -886,24 +1238,16 @@ pattern.params("/foo/bar") # => { "example" => "foo/bar" }
886
1238
  </tbody>
887
1239
  </table>
888
1240
 
1241
+ <a name="-pattern-details-template"></a>
889
1242
  ### `template`
890
1243
 
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
- ```
1244
+ **Supported options:**
1245
+ [`capture`](#-available-options--capture),
1246
+ [`except`](#-available-options--except),
1247
+ [`greedy`](#-available-options--greedy),
1248
+ [`space_matches_plus`](#-available-options--space_matches_plus),
1249
+ [`uri_decode`](#-available-options--uri_decode),
1250
+ [`ignore_unknown_options`](#-available-options--ignore_unknown_options).
907
1251
 
908
1252
  <table>
909
1253
  <thead>
@@ -943,82 +1287,32 @@ Please keep the following in mind:
943
1287
  > "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
1288
  > &mdash; *RFC 6570, Sec 1.5: "Limitations"*
945
1289
 
1290
+ Note that it differs from URI templates in that it takes the unescaped version of special character instead of the escaped version.
1291
+
946
1292
  If you reuse the exact same templates and expose them via an external API meant for expansion,
947
1293
  you should set `uri_decode` to `false` in order to conform with the specification.
948
1294
 
949
1295
  If you are looking for an alternative implementation that also supports expanding, check out [addressable](http://addressable.rubyforge.org/).
950
1296
 
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
-
1297
+ <a name="-requirements"></a>
1005
1298
  ## Requirements
1006
1299
 
1007
- Mustermann has no dependencies besides a Ruby 2.0 compatible Ruby implementation.
1300
+ Mustermann depends on [tool](https://github.com/rkh/tool) (which has been extracted from Mustermann and Sinatra 2.0), and a Ruby 2.0 compatible Ruby implementation.
1008
1301
 
1009
- It is known to work on **MRI 2.0** and **MRI trunk**.
1302
+ It is known to work on MRI 2.0 and 2.1.
1010
1303
 
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.
1304
+ **JRuby** is not yet fully supported. It is possible to run parts of Mustermann by passing in `--2.0 -X-C`, but as of JRuby 1.7, we would recommend waiting for proper Ruby 2.0 support to land in JRuby. The same goes for **Rubinius**.
1012
1305
 
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.
1306
+ If you need Ruby 1.9 support, you might be able to use the **unofficial** [mustermann19](http://rubygems.org/gems/mustermann19) gem based on [namusyaka's fork](https://github.com/namusyaka/mustermann).
1014
1307
 
1308
+ <a name="-release-history"></a>
1015
1309
  ## Release History
1016
1310
 
1017
1311
  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
1312
 
1019
1313
  ### Stable Releases
1020
1314
 
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.
1315
+ There have been no stable releases yet. The code base is considered solid but I only know of a small number of actual production usage.
1022
1316
  As there has been no stable release yet, the API might still change, though I consider this unlikely.
1023
1317
 
1024
1318
  ### Development Releases
@@ -1028,7 +1322,7 @@ As there has been no stable release yet, the API might still change, though I co
1028
1322
  [RubyGems.org](http://rubygems.org/gems/mustermann/versions/0.3.1),
1029
1323
  [RubyDoc.info](http://rubydoc.info/gems/mustermann/0.3.1/frames),
1030
1324
  [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))
1325
+ * Speed up pattern generation and matching (thanks [Daniel Mendler](https://github.com/minad))
1032
1326
  * Small change so `Mustermann === Mustermann.new('...')` returns `true`.
1033
1327
  * **Mustermann 0.3.0** (2014-08-18)
1034
1328
  * More Infos:
@@ -1077,5 +1371,15 @@ As there has been no stable release yet, the API might still change, though I co
1077
1371
 
1078
1372
  ### Upcoming Releases
1079
1373
 
1374
+ * **Mustermann 0.4.0** (next release with new features)
1375
+ * Add `Pattern#to_proc`.
1376
+ * Add `Pattern#|`, `Pattern#&` and `Pattern#^`.
1377
+ * Add `Pattern#peek`, `Pattern#peek_size`, `Pattern#peek_match` and `Pattern#peek_params`.
1378
+ * Add `Mustermann::StringScanner`.
1379
+ * Add `Pattern#to_templates`.
1380
+ * Add `|` syntax to `sinatra` templates.
1381
+ * Add template style placeholders to `sinatra` templates.
1382
+ * Add `cake`, `express`, `flask` and `pyramid` patterns.
1383
+ * Allow passing in additional value behavior directly to `Pattern#expand`.
1080
1384
  * **Mustermann 1.0.0** (before Sinatra 2.0)
1081
1385
  * First stable release.