mustermann 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +429 -672
  3. data/lib/mustermann.rb +95 -20
  4. data/lib/mustermann/ast/boundaries.rb +44 -0
  5. data/lib/mustermann/ast/compiler.rb +13 -7
  6. data/lib/mustermann/ast/expander.rb +22 -12
  7. data/lib/mustermann/ast/node.rb +69 -5
  8. data/lib/mustermann/ast/param_scanner.rb +20 -0
  9. data/lib/mustermann/ast/parser.rb +138 -19
  10. data/lib/mustermann/ast/pattern.rb +59 -7
  11. data/lib/mustermann/ast/template_generator.rb +28 -0
  12. data/lib/mustermann/ast/transformer.rb +2 -2
  13. data/lib/mustermann/ast/translator.rb +20 -0
  14. data/lib/mustermann/ast/validation.rb +4 -3
  15. data/lib/mustermann/composite.rb +101 -0
  16. data/lib/mustermann/expander.rb +2 -2
  17. data/lib/mustermann/identity.rb +56 -0
  18. data/lib/mustermann/pattern.rb +185 -10
  19. data/lib/mustermann/pattern_cache.rb +49 -0
  20. data/lib/mustermann/regexp.rb +1 -0
  21. data/lib/mustermann/regexp_based.rb +18 -1
  22. data/lib/mustermann/regular.rb +4 -1
  23. data/lib/mustermann/simple_match.rb +5 -0
  24. data/lib/mustermann/sinatra.rb +22 -5
  25. data/lib/mustermann/to_pattern.rb +11 -6
  26. data/lib/mustermann/version.rb +1 -1
  27. data/mustermann.gemspec +1 -14
  28. data/spec/ast_spec.rb +14 -0
  29. data/spec/composite_spec.rb +147 -0
  30. data/spec/expander_spec.rb +15 -0
  31. data/spec/identity_spec.rb +44 -0
  32. data/spec/mustermann_spec.rb +17 -2
  33. data/spec/pattern_spec.rb +7 -3
  34. data/spec/regular_spec.rb +25 -0
  35. data/spec/sinatra_spec.rb +184 -9
  36. data/spec/to_pattern_spec.rb +49 -0
  37. metadata +15 -180
  38. data/.gitignore +0 -18
  39. data/.rspec +0 -2
  40. data/.travis.yml +0 -4
  41. data/.yardopts +0 -1
  42. data/Gemfile +0 -2
  43. data/LICENSE +0 -22
  44. data/Rakefile +0 -6
  45. data/internals.md +0 -64
  46. data/lib/mustermann/ast/tree_renderer.rb +0 -29
  47. data/lib/mustermann/rails.rb +0 -17
  48. data/lib/mustermann/shell.rb +0 -29
  49. data/lib/mustermann/simple.rb +0 -35
  50. data/lib/mustermann/template.rb +0 -47
  51. data/spec/rails_spec.rb +0 -521
  52. data/spec/shell_spec.rb +0 -108
  53. data/spec/simple_spec.rb +0 -236
  54. data/spec/support.rb +0 -5
  55. data/spec/support/coverage.rb +0 -16
  56. data/spec/support/env.rb +0 -16
  57. data/spec/support/expand_matcher.rb +0 -27
  58. data/spec/support/match_matcher.rb +0 -39
  59. data/spec/support/pattern.rb +0 -39
  60. data/spec/template_spec.rb +0 -814
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4c6a1504081dfaf03cfb2842c71d2295306771a8
4
- data.tar.gz: 18e9f9ea774fa6f668529502e36fe0f18a09374f
3
+ metadata.gz: d7452f3272ea2fe09869f438b2cef90dcba6bf4b
4
+ data.tar.gz: 5d79d32db97549662b82ef1b589d6d35f1f9df80
5
5
  SHA512:
6
- metadata.gz: 8f7813109fbc6b146f078dc1562d4a3a40e9ba1385500bbabee888e125910fde8f2788523355802a3d5c8525df4b6aa0b5cb81a8c6116e3e36a850de95863d84
7
- data.tar.gz: c1ccd011b168a821f974eb6dd9f42f9f8f6cbe9abf0b8adbf81a7ae3913e428083acba1b8902c07859246621af20a939d2734a5f0562c3d884eb8cc6ef55a6bb
6
+ metadata.gz: 22ad9c7d0bfb6ae62a339cbfbe2581a91a9f860e4ba2f035ea26157ec0e31e16125d9f159561f43f821e2a07f239eda9e3df318ef3c29d63296a9107bcf65265
7
+ data.tar.gz: 5394bb963e310669de6e569b06bc690f90060dded1ecbb991d9307c904f5dcb4d6a1c39da7683be8e79eec4782ec28502c716ae9bc528c4221ea7ae0e708434a
data/README.md CHANGED
@@ -1,15 +1,8 @@
1
1
  # The Amazing Mustermann
2
2
 
3
- [![Build Status](https://travis-ci.org/rkh/mustermann.svg?branch=master)](https://travis-ci.org/rkh/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
3
  *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
4
 
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.
5
+ 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
6
 
14
7
  Given a string pattern, Mustermann will turn it into an object that behaves like a regular expression and has comparable performance characteristics.
15
8
 
@@ -23,139 +16,358 @@ when Mustermann.new('foo/*') then puts "prefixed with foo"
23
16
  when Mustermann.new('*.pdf') then puts "it's a PDF"
24
17
  when Mustermann.new('*.png') then puts "it's an image"
25
18
  end
19
+
20
+ pattern = Mustermann.new('/:prefix/*.*')
21
+ pattern.params('/a/b.c') # => { "prefix" => "a", splat => ["b", "c"] }
22
+ ```
23
+
24
+ ## Overview
25
+
26
+ ### Features
27
+
28
+ * **[Pattern Types](#-pattern-types):** Mustermann supports a wide variety of different pattern types, making it compatible with a large variety of existing software.
29
+ * **[Fine Grained Control](#-available-options):** You can easily adjust matching behavior and add constraints to the placeholders and capture groups.
30
+ * **[Binary Operators](#-binary-operators):** Patterns can be combined into composite patterns using binary operators.
31
+ * **[Regexp Look Alike](#-regexp-look-alike):** Mustermann patterns can be used as a replacement for regular expressions.
32
+ * **[Parameter Parsing](#-parameter-parsing):** Mustermann can parse matched parameters into a Sinatra-style "params" hash, including type casting.
33
+ * **[Peeking](#-peeking):** Lets you check if the beginning of a string matches a pattern.
34
+ * **[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.
35
+ * **[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.
36
+ * **[Proc Look Alike](#-proc-look-alike):** Pass on a pattern instead of a block.
37
+ * **[Duck Typing](#-duck-typing):** You can create your own pattern-like objects by implementing `to_pattern`.
38
+ * **[Performance](#-performance):** Patterns are implemented with both performance and a low memory footprint in mind.
39
+
40
+ ### Additional Tooling
41
+
42
+ These features are included in the library, but not loaded by default
43
+
44
+ * **[Mapper](#-mapper):** A simple tool for mapping one string to another based on patterns.
45
+ * **[Routers](#-routers):** Model execution flow based on pattern matching. Comes with a simple Rack router.
46
+ * **[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.
47
+
48
+ <a name="-pattern-types"></a>
49
+ ## Pattern Types
50
+
51
+ 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.
52
+
53
+ You can create a pattern of a certain type by passing `type` option to `Mustermann.new`:
54
+
55
+ ``` ruby
56
+ require 'mustermann'
57
+ pattern = Mustermann.new('/*/**', type: :shell)
58
+ ```
59
+
60
+ 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).
61
+
62
+ Alternatively, you can also load and instantiate the pattern type directly:
63
+
64
+ ``` ruby
65
+ require 'mustermann/shell'
66
+ pattern = Mustermann::Shell.new('/*/**')
67
+ ```
68
+
69
+ Mustermann itself includes the [sinatra](#-sinatra-pattern), [identity](#-identity-pattern) and [regexp](#-regexp-pattern) pattern types. Other pattern types are available as separate gems.
70
+
71
+ <a name="-binary-operators"></a>
72
+ ## Binary Operators
73
+
74
+ Patterns can be combined via binary operators. These are:
75
+
76
+ * `|` (or): Resulting pattern matches if at least one of the input pattern matches.
77
+ * `&` (and): Resulting pattern matches if all input patterns match.
78
+ * `^` (xor): Resulting pattern matches if exactly one of the input pattern matches.
79
+
80
+ ``` ruby
81
+ require 'mustermann'
82
+
83
+ first = Mustermann.new('/foo/:input')
84
+ second = Mustermann.new('/:input/bar')
85
+
86
+ first | second === "/foo/foo" # => true
87
+ first | second === "/foo/bar" # => true
88
+
89
+ first & second === "/foo/foo" # => false
90
+ first & second === "/foo/bar" # => true
91
+
92
+ first ^ second === "/foo/foo" # => true
93
+ first ^ second === "/foo/bar" # => false
94
+ ```
95
+
96
+ 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.
97
+
98
+ <a name="-regexp-look-alike"></a>
99
+ ## Regexp Look Alike
100
+
101
+ Pattern objects mimic Ruby's `Regexp` class by implementing `match`, `=~`, `===`, `names` and `named_captures`.
102
+
103
+ ``` ruby
104
+ require 'mustermann'
105
+
106
+ pattern = Mustermann.new('/:page')
107
+ pattern.match('/') # => nil
108
+ pattern.match('/home') # => #<MatchData "/home" page:"home">
109
+ pattern =~ '/home' # => 0
110
+ pattern === '/home' # => true (this allows using it in case statements)
111
+ pattern.names # => ['page']
112
+ pattern.names # => {"page"=>[1]}
113
+
114
+ pattern = Mustermann.new('/home', type: :identity)
115
+ pattern.match('/') # => nil
116
+ pattern.match('/home') # => #<Mustermann::SimpleMatch "/home">
117
+ pattern =~ '/home' # => 0
118
+ pattern === '/home' # => true (this allows using it in case statements)
119
+ pattern.names # => []
120
+ pattern.names # => {}
26
121
  ```
27
122
 
123
+ Moreover, patterns based on regular expressions (all but `identity` and `shell`) automatically convert to regular expressions when needed:
124
+
125
+ ``` ruby
126
+ require 'mustermann'
127
+
128
+ pattern = Mustermann.new('/:page')
129
+ union = Regexp.union(pattern, /^$/)
130
+
131
+ union =~ "/foo" # => 0
132
+ union =~ "" # => 0
133
+
134
+ Regexp.try_convert(pattern) # => /.../
135
+ ```
136
+
137
+ 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.
138
+
139
+ <a name="-parameter-parsing"></a>
140
+ ## Parameter Parsing
141
+
28
142
  Besides being a `Regexp` look-alike, Mustermann also adds a `params` method, that will give you a Sinatra-style hash:
29
143
 
30
144
  ``` ruby
145
+ require 'mustermann'
146
+
31
147
  pattern = Mustermann.new('/:prefix/*.*')
32
148
  pattern.params('/a/b.c') # => { "prefix" => "a", splat => ["b", "c"] }
33
149
  ```
34
150
 
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.
151
+ For patterns with typed captures, it will also automatically convert them:
36
152
 
37
- ## Types and Options
153
+ ``` ruby
154
+ require 'mustermann'
38
155
 
39
- You can pass in additional options to take fine grained control over the pattern:
156
+ pattern = Mustermann.new('/<prefix>/<int:id>', type: :flask)
157
+ pattern.params('/page/10') # => { "prefix" => "page", "id" => 10 }
158
+ ```
159
+
160
+ <a name="-peeking"></a>
161
+ ## Peeking
162
+
163
+ 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:
164
+
165
+ * `peek` returns the matching substring.
166
+ * `peek_size` returns the number of characters matching.
167
+ * `peek_match` will return a `MatchData` or `Mustermann::SimpleMatch` (just like `match` does for the full string)
168
+ * `peek_params` will return the `params` hash parsed from the substring and the number of characters.
169
+
170
+ All of the above will turn `nil` if there was no match.
40
171
 
41
172
  ``` ruby
42
- Mustermann.new('/:foo.:bar', capture: :alpha) # :foo and :bar will only match alphabetic characters
173
+ require 'mustermann'
174
+
175
+ pattern = Mustermann.new('/:prefix')
176
+ pattern.peek('/foo/bar') # => '/foo'
177
+ pattern.peek_size('/foo/bar') # => 4
178
+
179
+ path_info = '/foo/bar'
180
+ params, size = patter.peek_params(path_info) # params == { "prefix" => "foo" }
181
+ rest = path_info[size..-1] # => "/bar"
43
182
  ```
44
183
 
45
- In fact, you can even completely change the pattern type:
184
+ <a name="-expanding"></a>
185
+ ## Expanding
186
+
187
+ Similarly to parsing, it is also possible to generate a string from a pattern by expanding it with a hash.
188
+ For simple expansions, you can use `Pattern#expand`.
46
189
 
47
190
  ``` ruby
48
- Mustermann.new('/**/*.png', type: :shell)
191
+ pattern = Mustermann.new('/:file(.:ext)?')
192
+ pattern.expand(file: 'pony') # => "/pony"
193
+ pattern.expand(file: 'pony', ext: 'jpg') # => "/pony.jpg"
194
+ pattern.expand(ext: 'jpg') # raises Mustermann::ExpandError
49
195
  ```
50
196
 
51
- The available types are:
197
+ Expanding can be useful for instance when implementing link helpers.
52
198
 
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>
199
+ ### Expander Objects
200
+
201
+ To get fine-grained control over expansion, you can use `Mustermann::Expander` directly.
202
+
203
+ You can create an expander object directly from a string:
204
+
205
+ ``` ruby
206
+ require 'mustermann/expander'
207
+ expander = Mustermann::Expander("/:file.jpg")
208
+ expander.expand(file: 'pony') # => "/pony.jpg"
209
+
210
+ expander = Mustermann::Expander(":file(.:ext)", type: :rails)
211
+ expander.expand(file: 'pony', ext: 'jpg') # => "/pony.jpg"
212
+ ```
213
+
214
+ Or you can pass it a pattern instance:
215
+
216
+ ``` ruby
217
+ require 'mustermann'
218
+ pattern = Mustermann.new("/:file")
219
+
220
+ require 'mustermann/expander'
221
+ expander = Mustermann::Expander.new(pattern)
222
+ ```
223
+
224
+ ### Expanding Multiple Patterns
225
+
226
+ You can add patterns to an expander object via `<<`:
227
+
228
+ ``` ruby
229
+ require 'mustermann'
230
+
231
+ expander = Mustermann::Expander.new
232
+ expander << "/users/:user_id"
233
+ expander << "/pages/:page_id"
234
+
235
+ expander.expand(user_id: 15) # => "/users/15"
236
+ expander.expand(page_id: 58) # => "/pages/58"
237
+ ```
238
+
239
+ You can set pattern options when creating the expander:
240
+
241
+ ``` ruby
242
+ require 'mustermann'
243
+
244
+ expander = Mustermann::Expander.new(type: :template)
245
+ expander << "/users/{user_id}"
246
+ expander << "/pages/{page_id}"
247
+ ```
156
248
 
157
- See below for more details.
249
+ Additionally, it is possible to combine patterns of different types:
158
250
 
251
+ ``` ruby
252
+ require 'mustermann'
253
+
254
+ expander = Mustermann::Expander.new
255
+ expander << Mustermann.new("/users/{user_id}", type: :template)
256
+ expander << Mustermann.new("/pages/:page_id", type: :rails)
257
+ ```
258
+
259
+ ### Handling Additional Values
260
+
261
+ The handling of additional values passed in to `expand` can be changed by setting the `additional_values` option:
262
+
263
+ ``` ruby
264
+ require 'mustermann'
265
+
266
+ expander = Mustermann::Expander.new("/:slug", additional_values: :raise)
267
+ expander.expand(slug: "foo", value: "bar") # raises Mustermann::ExpandError
268
+
269
+ expander = Mustermann::Expander.new("/:slug", additional_values: :ignore)
270
+ expander.expand(slug: "foo", value: "bar") # => "/foo"
271
+
272
+ expander = Mustermann::Expander.new("/:slug", additional_values: :append)
273
+ expander.expand(slug: "foo", value: "bar") # => "/foo?value=bar"
274
+ ```
275
+
276
+ It is also possible to pass this directly to the `expand` call:
277
+
278
+ ``` ruby
279
+ require 'mustermann'
280
+
281
+ pattern = Mustermann.new('/:slug')
282
+ pattern.expand(:append, slug: "foo", value: "bar") # => "/foo?value=bar"
283
+ ```
284
+
285
+ <a name="-generating-templates"></a>
286
+ ## Generating Templates
287
+
288
+ ... TODO ...
289
+
290
+ <a name="-proc-look-alike"></a>
291
+ ## Proc Look Alike
292
+
293
+ Patterns implement `to_proc`:
294
+
295
+ ``` ruby
296
+ require 'mustermann'
297
+ pattern = Mustermann.new('/foo')
298
+ callback = pattern.to_proc # => #<Proc>
299
+
300
+ callback.call('/foo') # => true
301
+ callback.call('/bar') # => false
302
+ ```
303
+
304
+ They can therefore be easily passed to methods expecting a block:
305
+
306
+ ``` ruby
307
+ require 'mustermann'
308
+
309
+ list = ["foo", "example@email.com", "bar"]
310
+ pattern = Mustermann.new(":name@:domain.:tld")
311
+ email = list.detect(&pattern) # => "example@email.com"
312
+ ```
313
+
314
+ <a name="-mapper"></a>
315
+ ## Mapper
316
+
317
+
318
+ You can use a mapper to transform strings according to two or more mappings:
319
+
320
+ ``` ruby
321
+ require 'mustermann/mapper'
322
+
323
+ mapper = Mustermann::Mapper.new("/:page(.:format)?" => ["/:page/view.:format", "/:page/view.html"])
324
+ mapper['/foo'] # => "/foo/view.html"
325
+ mapper['/foo.xml'] # => "/foo/view.xml"
326
+ mapper['/foo/bar'] # => "/foo/bar"
327
+ ```
328
+
329
+ <a name="-routers"></a>
330
+ ## Routers
331
+
332
+ Mustermann comes with basic router implementations that will call certain callbacks depending on the input.
333
+
334
+ ### Simple Router
335
+
336
+ The simple router chooses callbacks based on an input string.
337
+
338
+ ``` ruby
339
+ require 'mustermann/router/simple'
340
+
341
+ router = Mustermann::Router::Simple.new(default: 42)
342
+ router.on(':name', capture: :digit) { |string| string.to_i }
343
+ router.call("23") # => 23
344
+ router.call("example") # => 42
345
+ ```
346
+
347
+ ### Rack Router
348
+
349
+ This is not a full replacement for Rails, Sinatra, Cuba, etc, as it only cares about path based routing.
350
+
351
+ ``` ruby
352
+ require 'mustermann/router/rack'
353
+
354
+ router = Mustermann::Router::Rack.new do
355
+ on '/' do |env|
356
+ [200, {'Content-Type' => 'text/plain'}, ['Hello World!']]
357
+ end
358
+
359
+ on '/:name' do |env|
360
+ name = env['mustermann.params']['name']
361
+ [200, {'Content-Type' => 'text/plain'}, ["Hello #{name}!"]]
362
+ end
363
+
364
+ on '/something/*', call: SomeApp
365
+ end
366
+
367
+ # in a config.ru
368
+ run router
369
+ ```
370
+ <a name="-sinatra-integration"></a>
159
371
  ## Sinatra Integration
160
372
 
161
373
  All patterns implement `match`, which means they can be dropped into Sinatra and other Rack routers:
@@ -272,97 +484,17 @@ end
272
484
  * Sinatra 2.0 will use Mustermann internally
273
485
  * Better exceptions for broken route syntax
274
486
 
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"
487
+ ### Why not include this in Sinatra 1.x?
359
488
 
360
- expander = Mustermann::Expander.new("/:slug", additional_values: :append)
361
- expander.expand(slug: "foo", value: "bar") # => "/foo?value=bar"
362
- ```
489
+ * It would introduce breaking changes, even though these would be minor
490
+ * Like Sinatra 2.0, Mustermann requires Ruby 2.0 or newer
363
491
 
492
+ <a name="-duck-typing"></a>
364
493
  ## Duck Typing
365
494
 
495
+ <a name="-duck-typing-to-pattern"></a>
496
+ ### `to_pattern`
497
+
366
498
  All methods converting string input to pattern objects will also accept any arbitrary object that implements `to_pattern`:
367
499
 
368
500
  ``` ruby
@@ -405,28 +537,43 @@ end
405
537
  MyObject.new.to_pattern # => #<Mustermann::Sinatra:"/foo">
406
538
  ```
407
539
 
408
- ## Partial Loading and Thread Safety
540
+ <a name="-duck-typing-respond-to"></a>
541
+ ### `respond_to?`
409
542
 
410
- Pattern objects are generally assumed to be thread-safe. You can easily match strings against the same pattern object concurrently.
543
+ You can and should use `respond_to?` to check if a pattern supports certain features.
411
544
 
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.
545
+ ``` ruby
546
+ require 'mustermann'
547
+ pattern = Mustermann.new("/")
413
548
 
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.
549
+ puts "supports expanding" if pattern.respond_to? :expand
550
+ puts "supports generating templates" if pattern.respond_to? :to_templates
551
+ ```
415
552
 
416
- To avoid this, you can load the pattern types you need manually:
553
+ Alternatively, you can handle a `NotImplementedError` raised from such a method.
417
554
 
418
555
  ``` ruby
419
- require 'mustermann/sinatra'
420
- Mustermann::Sinatra.new('/:foo')
556
+ require 'mustermann'
557
+ pattern = Mustermann.new("/")
558
+
559
+ begin
560
+ p pattern.to_templates
561
+ rescue NotImplementedError
562
+ puts "does not support generating templates"
563
+ end
421
564
  ```
422
565
 
423
- ## Options
566
+ This behavior corresponds to what Ruby does, for instance for [`fork`](http://ruby-doc.org/core-2.1.1/NotImplementedError.html).
567
+
568
+ <a name="-available-options"></a>
569
+ ## Available Options
424
570
 
571
+ <a name="-available-options--capture"></a>
425
572
  ### `capture`
426
573
 
427
- Supported by: `rails`, `sinatra`, `template`
574
+ Supported by: All types except `identity`, `shell` and `simple` patterns.
428
575
 
429
- **Sinatra**, **URI template** and **Rails** patterns support changing the way named captures work via the `capture` options.
576
+ Most pattern types support changing the strings named captures will match via the `capture` options.
430
577
 
431
578
  Possible values for a capture:
432
579
 
@@ -449,9 +596,10 @@ Mustermann.new('/:id.:ext', capture: { id: /\d+/, ext: ['png', 'jpg'] })
449
596
 
450
597
  Available POSIX character classes are: `:alnum`, `:alpha`, `:blank`, `:cntrl`, `:digit`, `:graph`, `:lower`, `:print`, `:punct`, `:space`, `:upper`, `:xdigit`, `:word` and `:ascii`.
451
598
 
599
+ <a name="-available-options--except"></a>
452
600
  ### `except`
453
601
 
454
- Supported by: `rails`, `sinatra`, `template`
602
+ Supported by: All types except `identity`, `shell` and `simple` patterns.
455
603
 
456
604
  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
605
 
@@ -469,13 +617,15 @@ pattern === '/foo.jpg' # => true
469
617
  pattern === '/foo.png' # => false
470
618
  ```
471
619
 
620
+ <a name="-available-options--greedy"></a>
472
621
  ### `greedy`
473
622
 
474
- Supported by: `rails`, `simple`, `sinatra`, `template`. Default value: `true`
623
+ Supported by: All types except `identity` and `shell` patterns.
624
+ Default value: `true`
475
625
 
476
626
  **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
627
 
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.
628
+ **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
629
 
480
630
  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
631
 
@@ -487,11 +637,13 @@ pattern = Mustermann.new(':a.:b', greedy: false)
487
637
  pattern.match('a.b.c.d') # => #<MatchData a:"a" b:"b.c.d">
488
638
  ```
489
639
 
640
+ <a name="-available-options--space_matches_plus"></a>
490
641
  ### `space_matches_plus`
491
642
 
492
- Supported by: `rails`, `simple`, `sinatra`, `template`. Default value: `true`
643
+ Supported by: All types except `identity`, `regexp` and `shell` patterns.
644
+ Default value: `true`
493
645
 
494
- **Sinatra**, **Simple**, **URI template** and **Rails** patterns will by default also match a plus sign for a space in the pattern:
646
+ Most pattern types will by default also match a plus sign for a space in the pattern:
495
647
 
496
648
  ``` ruby
497
649
  Mustermann.new('a b') === 'a+b' # => true
@@ -511,9 +663,11 @@ pattern.match('a b')[:x] # => 'a b'
511
663
  pattern.match('a+b')[:x] # => 'a+b'
512
664
  ````
513
665
 
666
+ <a name="-available-options--uri_decode"></a>
514
667
  ### `uri_decode`
515
668
 
516
- Supported by all patterns. Default value: `true`
669
+ Supported by all pattern types.
670
+ Default value: `true`
517
671
 
518
672
  Usually, characters in the pattern will also match the URI encoded version of these characters:
519
673
 
@@ -529,165 +683,49 @@ Mustermann.new('a b', uri_decode: false) === 'a b' # => true
529
683
  Mustermann.new('a b', uri_decode: false) === 'a%20b' # => false
530
684
  ```
531
685
 
686
+ <a name="-available-options--ignore_unknown_options"></a>
532
687
  ### `ignore_unknown_options`
533
688
 
534
- Supported by all patterns. Default value: `false`
689
+ Supported by all patterns.
690
+ Default value: `false`
535
691
 
536
692
  If you pass an option in that is not supported by the specific pattern type, Mustermann will raise an `ArgumentError`.
537
693
  By setting `ignore_unknown_options` to `true`, it will happily ignore the option.
538
694
 
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>
695
+ <a name="-performance"></a>
696
+ ## Performance
633
697
 
634
- ### `regexp`
698
+ 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.
635
699
 
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`).
700
+ 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.
638
701
 
639
- ``` ruby
640
- require 'mustermann'
702
+ ### String Matching
641
703
 
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
- ```
704
+ When using a pattern instead of a regular expression for string matching, performance will usually be comparable.
648
705
 
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>
706
+ 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).
663
707
 
664
- It is also possible to turn a proper Regexp instance into a pattern object by passing it to `Mustermann.new`:
708
+ 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).
665
709
 
666
- ``` ruby
667
- require 'mustermann'
668
- Mustermann.new(/(?<example>.*)/).params("input") # => { "example" => "input" }
669
- ```
710
+ ### Expanding
670
711
 
671
- ### `shell`
712
+ Pattern expansion significantly outperforms other, widely used Ruby tools for generating URLs from URL patterns in most use cases.
672
713
 
673
- Shell patterns, as used in Bash or with `Dir.glob`.
714
+ This comes with a few trade-offs:
674
715
 
675
- ``` ruby
676
- require 'mustermann'
716
+ * 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.
717
+ * 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)
718
+ * 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.
719
+ * Partial expansion is (currently) not supported.
677
720
 
678
- pattern = Mustermann.new('/*', type: :shell)
679
- pattern === "/foo.bar" # => true
680
- pattern === "/foo/bar" # => false
721
+ ## Details on Pattern Types
681
722
 
682
- pattern = Mustermann.new('/**/*', type: :shell)
683
- pattern === "/foo.bar" # => true
684
- pattern === "/foo/bar" # => true
723
+ <a name="-identity-pattern"></a>
724
+ ### `identity`
685
725
 
686
- pattern = Mustermann.new('/{foo,bar}', type: :shell)
687
- pattern === "/foo" # => true
688
- pattern === "/bar" # => true
689
- pattern === "/baz" # => false
690
- ```
726
+ **Supported options:**
727
+ [`uri_decode`](#-available-options--uri_decode),
728
+ [`ignore_unknown_options`](#-available-options--ignore_unknown_options).
691
729
 
692
730
  <table>
693
731
  <thead>
@@ -698,57 +736,18 @@ pattern === "/baz" # => false
698
736
  </thead>
699
737
  <tbody>
700
738
  <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>
739
+ <td><i>any character</i></td>
740
+ <td>Matches exactly that character or a URI escaped version of it.</td>
723
741
  </tr>
724
742
  </tbody>
725
743
  </table>
726
744
 
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'
745
+ <a name="-regexp-pattern"></a>
746
+ ### `regexp`
733
747
 
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
- ```
748
+ **Supported options:**
749
+ [`uri_decode`](#-available-options--uri_decode),
750
+ [`ignore_unknown_options`](#-available-options--ignore_unknown_options).
752
751
 
753
752
  <table>
754
753
  <thead>
@@ -759,76 +758,22 @@ pattern.params("/foo/bar") # => { "splat" => ["foo/bar"] }
759
758
  </thead>
760
759
  <tbody>
761
760
  <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>
761
+ <td><i>any string</i></td>
762
+ <td>Interpreted as regular expression.</td>
791
763
  </tr>
792
764
  </tbody>
793
765
  </table>
794
766
 
767
+ <a name="-sinatra-pattern"></a>
795
768
  ### `sinatra`
796
769
 
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
- ```
770
+ **Supported options:**
771
+ [`capture`](#-available-options--capture),
772
+ [`except`](#-available-options--except),
773
+ [`greedy`](#-available-options--greedy),
774
+ [`space_matches_plus`](#-available-options--space_matches_plus),
775
+ [`uri_decode`](#-available-options--uri_decode),
776
+ [`ignore_unknown_options`](#-available-options--ignore_unknown_options).
832
777
 
833
778
  <table>
834
779
  <thead>
@@ -839,23 +784,23 @@ pattern.params("/foo/bar") # => { "example" => "foo/bar" }
839
784
  </thead>
840
785
  <tbody>
841
786
  <tr>
842
- <td><b>:</b><i>name</i></td>
787
+ <td><b>:</b><i>name</i> <i><b>or</b></i> <b>&#123;</b><i>name</i><b>&#125;</b></td>
843
788
  <td>
844
789
  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.
790
+ Capture behavior can be modified with <tt>capture</tt> and <tt>greedy</tt> option.
846
791
  </td>
847
792
  </tr>
848
793
  <tr>
849
- <td><b>*</b><i>name</i></td>
794
+ <td><b>*</b><i>name</i> <i><b>or</b></i> <b>&#123;+</b><i>name</i><b>&#125;</b></td>
850
795
  <td>
851
796
  Captures anything in a non-greedy fashion. Capture is named <i>name</i>.
852
797
  </td>
853
798
  </tr>
854
799
  <tr>
855
- <td><b>*</b></td>
800
+ <td><b>*</b> <i><b>or</b></i> <b>&#123;+splat&#125;</b></td>
856
801
  <td>
857
802
  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.
803
+ It is always an array of captures, as you can use it more than once in a pattern.
859
804
  </td>
860
805
  </tr>
861
806
  <tr>
@@ -865,9 +810,15 @@ pattern.params("/foo/bar") # => { "example" => "foo/bar" }
865
810
  or to separate two elements that would otherwise be parsed as one.
866
811
  </td>
867
812
  </tr>
813
+ <tr>
814
+ <td><b>(</b><i>expression</i><b>|</b><i>expression</i><b>|</b><i>...</i><b>)</b></td>
815
+ <td>
816
+ Will match anything matching the nested expressions. May contain any other syntax element, including captures.
817
+ </td>
818
+ </tr>
868
819
  <tr>
869
820
  <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>
821
+ <td>Makes <i>x</i> optional. For instance, <tt>(foo)?</tt> matches <tt>foo</tt> or an empty string.</td>
871
822
  </tr>
872
823
  <tr>
873
824
  <td><b>/</b></td>
@@ -885,197 +836,3 @@ pattern.params("/foo/bar") # => { "example" => "foo/bar" }
885
836
  </tr>
886
837
  </tbody>
887
838
  </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.