mustermann19 0.3.1.2 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -15,10 +15,58 @@ module Mustermann
15
15
  new.translate(tree)
16
16
  end
17
17
 
18
- translate(:node) { self }
19
- translate(:group, :root) do
20
- self.payload = t(payload)
21
- self
18
+ # recursive descent
19
+ translate(:node) do
20
+ node.payload = t(payload)
21
+ node
22
+ end
23
+
24
+ # ignore unknown objects on the tree
25
+ translate(Object) { node }
26
+
27
+ # turn a group containing or nodes into a union
28
+ # @!visibility private
29
+ class GroupTransformer < NodeTranslator
30
+ register :group
31
+
32
+ # @!visibility private
33
+ def translate
34
+ return union if payload.any? { |e| e.is_a? :or }
35
+ self.payload = t(payload)
36
+ self
37
+ end
38
+
39
+ # @!visibility private
40
+ def union
41
+ groups = split_payload.map { |g| group(g) }
42
+ Node[:union].new(groups, start: node.start, stop: node.stop)
43
+ end
44
+
45
+ # @!visibility private
46
+ def group(elements)
47
+ return t(elements.first) if elements.size == 1
48
+ start, stop = elements.first.start, elements.last.stop if elements.any?
49
+ Node[:group].new(t(elements), start: start, stop: stop)
50
+ end
51
+
52
+ # @!visibility private
53
+ def split_payload
54
+ groups = [[]]
55
+ payload.each { |e| e.is_a?(:or) ? groups << [] : groups.last << e }
56
+ groups.map!
57
+ end
58
+ end
59
+
60
+ # inject a union node right inside the root node if it contains or nodes
61
+ # @!visibility private
62
+ class RootTransformer < GroupTransformer
63
+ register :root
64
+
65
+ # @!visibility private
66
+ def union
67
+ self.payload = [super]
68
+ self
69
+ end
22
70
  end
23
71
 
24
72
  # URI expression transformations depending on operator
@@ -44,7 +92,7 @@ module Mustermann
44
92
  self.operator = OPERATORS.fetch(operator) { raise CompileError, "#{operator} operator not supported" }
45
93
  separator = Node[:separator].new(operator.separator)
46
94
  prefix = Node[:separator].new(operator.prefix)
47
- self.payload = Array(payload.inject { |list, element| Array(list) << t(separator) << t(element) })
95
+ self.payload = Array(payload.inject { |list, element| Array(list) << t(separator.dup) << t(element) })
48
96
  payload.unshift(prefix) if operator.prefix
49
97
  self
50
98
  end
@@ -91,7 +139,7 @@ module Mustermann
91
139
  # @!visibility private
92
140
  def create_lookahead(elements, *args)
93
141
  return elements unless elements.size > 1
94
- [Node[:with_look_ahead].new(elements, *args)]
142
+ [Node[:with_look_ahead].new(elements, *args, start: elements.first.start, stop: elements.last.stop)]
95
143
  end
96
144
 
97
145
  # can the given element be used in a look-ahead?
@@ -74,6 +74,26 @@ module Mustermann
74
74
  end
75
75
  end
76
76
 
77
+ # Enables quick creation of a translator object.
78
+ #
79
+ # @example
80
+ # require 'mustermann'
81
+ # require 'mustermann/ast/translator'
82
+ #
83
+ # translator = Mustermann::AST::Translator.create do
84
+ # translate(:node) { [type, *t(payload)].flatten.compact }
85
+ # translate(Array) { map { |e| t(e) } }
86
+ # translate(Object) { }
87
+ # end
88
+ #
89
+ # ast = Mustermann.new('/:name').to_ast
90
+ # translator.translate(ast) # => [:root, :separator, :capture]
91
+ #
92
+ # @!visibility private
93
+ def self.create(&block)
94
+ Class.new(self, &block).new
95
+ end
96
+
77
97
  raises Mustermann::Error
78
98
 
79
99
  # @param [Mustermann::AST::Node, Object] node to translate
@@ -15,15 +15,8 @@ module Mustermann
15
15
 
16
16
  translate(Object) { inspect }
17
17
  translate(Array) { map { |e| "\n" << t(e) }.join.gsub("\n", "\n ") }
18
- translate(:node) { "#{t.type(node)} #{t(payload)}" }
19
- translate(:with_look_ahead) { "#{t.type(node)} #{t(head)} #{t(payload)}" }
20
-
21
- # Turns a class name into a node identifier.
22
- #
23
- # @!visibility private
24
- def type(node)
25
- node.class.name[/[^:]+$/].split(/(?<=.)(?=[A-Z])/).map(&:downcase).join(?_)
26
- end
18
+ translate(:node) { "#{node.type} #{t(payload)}" }
19
+ translate(:with_look_ahead) { "#{node.type} #{t(head)} #{t(payload)}" }
27
20
  end
28
21
  end
29
22
  end
@@ -9,6 +9,8 @@ module Mustermann
9
9
  # @see Mustermann::Pattern
10
10
  # @see file:README.md#flask Syntax description in the README
11
11
  class Express < AST::Pattern
12
+ register :express
13
+
12
14
  on(nil, ??, ?+, ?*, ?)) { |c| unexpected(c) }
13
15
  on(?:) { |c| node(:capture) { scan(/\w+/) } }
14
16
  on(?() { |c| node(:splat, constraint: read_brackets(?(, ?))) }
@@ -9,6 +9,8 @@ module Mustermann
9
9
  # @see Mustermann::Pattern
10
10
  # @see file:README.md#flask Syntax description in the README
11
11
  class Flask < AST::Pattern
12
+ register :flask
13
+
12
14
  on(nil, ?>, ?:) { |c| unexpected(c) }
13
15
 
14
16
  on(?<) do |char|
@@ -10,6 +10,8 @@ module Mustermann
10
10
  # @see Mustermann::Pattern
11
11
  # @see file:README.md#identity Syntax description in the README
12
12
  class Identity < Pattern
13
+ register :identity
14
+
13
15
  # @param (see Mustermann::Pattern#===)
14
16
  # @return (see Mustermann::Pattern#===)
15
17
  # @see (see Mustermann::Pattern#===)
@@ -45,8 +47,8 @@ module Mustermann
45
47
  # Not used internally by {Mustermann::Identity}.
46
48
  # @!visibility private
47
49
  def to_ast
48
- payload = @string.each_char.map { |c| AST::Node[c == ?/ ? :separator : :char].new(c) }
49
- AST::Node[:root].new(payload, pattern: @string)
50
+ payload = @string.each_char.with_index.map { |c, i| AST::Node[c == ?/ ? :separator : :char].new(c, start: i, stop: i+1) }
51
+ AST::Node[:root].new(payload, pattern: @string, start: 0, stop: @string.length)
50
52
  end
51
53
 
52
54
  # Identity patterns support expanding.
@@ -65,7 +67,7 @@ module Mustermann
65
67
  return to_s if values.empty? or behavior == :ignore
66
68
  raise ExpandError, "cannot expand with keys %p" % values.keys.sort if behavior == :raise
67
69
  raise ArgumentError, "unknown behavior %p" % behavior if behavior != :append
68
- params = values.map { |key, value| @@uri.escape(key.to_s) + "=" + @@uri.escape(value.to_s, /[^\w\d]/) }
70
+ params = values.map { |key, value| @@uri.escape(key.to_s) + "=" + @@uri.escape(value.to_s, /[^\w]/) }
69
71
  separator = @string.include?(??) ? ?& : ??
70
72
  @string + separator + params.join(?&)
71
73
  end
@@ -28,6 +28,13 @@ module Mustermann
28
28
  options
29
29
  end
30
30
 
31
+ # Registers the pattern with Mustermann.
32
+ # @see Mustermann.register
33
+ # @!visibility private
34
+ def self.register(*names)
35
+ names.each { |name| Mustermann.register(name, self) }
36
+ end
37
+
31
38
  # @param [Symbol] option The option to check.
32
39
  # @return [Boolean] Whether or not option is supported.
33
40
  def self.supported?(option, options = {})
@@ -9,6 +9,8 @@ module Mustermann
9
9
  # @see Mustermann::Pattern
10
10
  # @see file:README.md#pryamid Syntax description in the README
11
11
  class Pyramid < AST::Pattern
12
+ register :pyramid
13
+
12
14
  on(nil, ?}) { |c| unexpected(c) }
13
15
 
14
16
  on(?{) do |char|
@@ -1,4 +1,5 @@
1
1
  require 'mustermann/ast/pattern'
2
+ require 'mustermann/rails/versions'
2
3
 
3
4
  module Mustermann
4
5
  # Rails style pattern implementation.
@@ -9,9 +10,36 @@ module Mustermann
9
10
  # @see Mustermann::Pattern
10
11
  # @see file:README.md#rails Syntax description in the README
11
12
  class Rails < AST::Pattern
12
- on(nil, ?)) { |c| unexpected(c) }
13
- on(?*) { |c| node(:named_splat) { scan(/\w+/) } }
14
- on(?() { |c| node(:optional, node(:group) { read unless scan(?)) }) }
15
- on(?:) { |c| node(:capture) { scan(/\w+/) } }
13
+ extend Versions
14
+ register :rails
15
+
16
+ # first parser, no optional parts
17
+ version('2.3') do
18
+ on(nil) { |c| unexpected(c) }
19
+ on(?*) { |c| node(:named_splat) { scan(/\w+/) } }
20
+ on(?:) { |c| node(:capture) { scan(/\w+/) } }
21
+ end
22
+
23
+ # rack-mount
24
+ version('3.0', '3.1') do
25
+ on(?)) { |c| unexpected(c) }
26
+ on(?() { |c| node(:optional, node(:group) { read unless scan(?)) }) }
27
+ on(?\\) { |c| node(:char, expect(/./)) }
28
+ end
29
+
30
+ # stand-alone journey
31
+ version('3.2') do
32
+ on(?|) { |c| raise ParseError, "the implementation of | is broken in ActionDispatch, cannot compile compatible pattern" }
33
+ on(?\\) { |c| node(:char, c) }
34
+ end
35
+
36
+ # embedded journey, broken (ignored) escapes
37
+ version('4.0', '4.1') { on(?\\) { |c| read } }
38
+
39
+ # escapes got fixed in 4.2
40
+ version('4.2') { on(?\\) { |c| node(:char, expect(/./)) } }
41
+
42
+ # uncomment if Rails 5.0 fixes |
43
+ # version('5.0') { on(?|) { |c| node(:or) }}
16
44
  end
17
45
  end
@@ -0,0 +1,51 @@
1
+ module Mustermann
2
+ # Mixin that adds support for multiple versions of the same type.
3
+ # @see Mustermann::Rails
4
+ # @!visibility private
5
+ module Versions
6
+ # Checks if class has mulitple versions available and picks one that matches the version option.
7
+ # @!visibility private
8
+ def new(*args)
9
+ options = args.last.kind_of?(Hash) ? args.pop : {}
10
+ version = options.fetch(:version, nil)
11
+ options.delete(:version)
12
+ return super(*args, options) unless versions.any?
13
+ self[version].new(*args, options)
14
+ end
15
+
16
+ # @return [Hash] version to subclass mapping.
17
+ # @!visibility private
18
+ def versions
19
+ @versions ||= {}
20
+ end
21
+
22
+ # Defines a new version.
23
+ # @!visibility private
24
+ def version(*list, &block)
25
+ options = list.last.kind_of?(Hash) ? args.pop : {}
26
+ inherit_from = options.fetch(:inherit_from, nil)
27
+ superclass = self[inherit_from] || self
28
+ subclass = Class.new(superclass, &block)
29
+ list.each { |v| versions[v] = subclass }
30
+ end
31
+
32
+ # Resolve a subclass for a given version string.
33
+ # @!visibility private
34
+ def [](version)
35
+ return versions.values.last unless version
36
+ detected = versions.detect { |v,_| version.start_with?(v) }
37
+ raise ArgumentError, 'unsupported version %p' % version unless detected
38
+ detected.last
39
+ end
40
+
41
+ # @!visibility private
42
+ def name
43
+ super || superclass.name
44
+ end
45
+
46
+ # @!visibility private
47
+ def inspect
48
+ name
49
+ end
50
+ end
51
+ end
@@ -0,0 +1 @@
1
+ require 'mustermann/regular'
@@ -9,6 +9,8 @@ module Mustermann
9
9
  # @see Mustermann::Pattern
10
10
  # @see file:README.md#simple Syntax description in the README
11
11
  class Regular < RegexpBased
12
+ register :regexp, :regular
13
+
12
14
  # @param (see Mustermann::Pattern#initialize)
13
15
  # @return (see Mustermann::Pattern#initialize)
14
16
  # @see (see Mustermann::Pattern#initialize)
@@ -10,6 +10,8 @@ module Mustermann
10
10
  # @see Mustermann::Pattern
11
11
  # @see file:README.md#shell Syntax description in the README
12
12
  class Shell < Pattern
13
+ register :shell
14
+
13
15
  # @param (see Mustermann::Pattern#initialize)
14
16
  # @return (see Mustermann::Pattern#initialize)
15
17
  # @see (see Mustermann::Pattern#initialize)
@@ -9,6 +9,7 @@ module Mustermann
9
9
  # @see Mustermann::Pattern
10
10
  # @see file:README.md#simple Syntax description in the README
11
11
  class Simple < RegexpBased
12
+ register :simple
12
13
  supported_options :greedy, :space_matches_plus
13
14
 
14
15
  def compile(options = {})
@@ -9,17 +9,15 @@ module Mustermann
9
9
  # @see Mustermann::Pattern
10
10
  # @see file:README.md#sinatra Syntax description in the README
11
11
  class Sinatra < AST::Pattern
12
- on(nil, ??, ?), ?|) { |c| unexpected(c) }
12
+ register :sinatra
13
+
14
+ on(nil, ??, ?)) { |c| unexpected(c) }
13
15
 
14
16
  on(?*) { |c| scan(/\w+/) ? node(:named_splat, buffer.matched) : node(:splat) }
15
17
  on(?:) { |c| node(:capture) { scan(/\w+/) } }
16
18
  on(?\\) { |c| node(:char, expect(/./)) }
17
-
18
- on ?( do |char|
19
- groups = []
20
- groups << node(:group) { read unless check(?)) or scan(?|) } until scan(?))
21
- groups.size == 1 ? groups.first : node(:union, groups)
22
- end
19
+ on(?() { |c| node(:group) { read unless scan(?)) } }
20
+ on(?|) { |c| node(:or) }
23
21
 
24
22
  on ?{ do |char|
25
23
  type = scan(?+) ? :named_splat : :capture
@@ -25,7 +25,7 @@ module Mustermann
25
25
  # Even if it was thread-safe, scanning concurrently would probably lead to unwanted behaviour.
26
26
  class StringScanner
27
27
  # Exception raised if scan/unscan operation cannot be performed.
28
- ScanError = Class.new(::StringScanner::Error)
28
+ ScanError = Class.new(::ScanError)
29
29
  PATTERN_CACHE = PatternCache.new
30
30
  #private_constant :PATTERN_CACHE
31
31
 
@@ -10,10 +10,13 @@ module Mustermann
10
10
  # @see file:README.md#template Syntax description in the README
11
11
  # @see http://tools.ietf.org/html/rfc6570 RFC 6570
12
12
  class Template < AST::Pattern
13
+ register :template, :uri_template
14
+
13
15
  on ?{ do |char|
14
16
  variable = proc do
17
+ start = pos
15
18
  match = expect(/(?<name>\w+)(?:\:(?<prefix>\d{1,4})|(?<explode>\*))?/)
16
- node(:variable, match[:name], prefix: match[:prefix], explode: match[:explode])
19
+ node(:variable, match[:name], prefix: match[:prefix], explode: match[:explode], start: start)
17
20
  end
18
21
 
19
22
  operator = buffer.scan(/[\+\#\.\/;\?\&\=\,\!\@\|]/)
@@ -0,0 +1 @@
1
+ require 'mustermann/template'
@@ -1,3 +1,3 @@
1
1
  module Mustermann
2
- VERSION ||= '0.3.1.2'
2
+ VERSION ||= '0.4.0'
3
3
  end
@@ -0,0 +1,838 @@
1
+ # The Amazing Mustermann
2
+
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).*
4
+
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.
6
+
7
+ Given a string pattern, Mustermann will turn it into an object that behaves like a regular expression and has comparable performance characteristics.
8
+
9
+ ``` ruby
10
+ if '/foo/bar' =~ Mustermann.new('/foo/*')
11
+ puts 'it works!'
12
+ end
13
+
14
+ case 'something.png'
15
+ when Mustermann.new('foo/*') then puts "prefixed with foo"
16
+ when Mustermann.new('*.pdf') then puts "it's a PDF"
17
+ when Mustermann.new('*.png') then puts "it's an image"
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 # => {}
121
+ ```
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
+
142
+ Besides being a `Regexp` look-alike, Mustermann also adds a `params` method, that will give you a Sinatra-style hash:
143
+
144
+ ``` ruby
145
+ require 'mustermann'
146
+
147
+ pattern = Mustermann.new('/:prefix/*.*')
148
+ pattern.params('/a/b.c') # => { "prefix" => "a", splat => ["b", "c"] }
149
+ ```
150
+
151
+ For patterns with typed captures, it will also automatically convert them:
152
+
153
+ ``` ruby
154
+ require 'mustermann'
155
+
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.
171
+
172
+ ``` ruby
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"
182
+ ```
183
+
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`.
189
+
190
+ ``` ruby
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
195
+ ```
196
+
197
+ Expanding can be useful for instance when implementing link helpers.
198
+
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
+ ```
248
+
249
+ Additionally, it is possible to combine patterns of different types:
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>
371
+ ## Sinatra Integration
372
+
373
+ All patterns implement `match`, which means they can be dropped into Sinatra and other Rack routers:
374
+
375
+ ``` ruby
376
+ require 'sinatra'
377
+ require 'mustermann'
378
+
379
+ get Mustermann.new('/:foo') do
380
+ params[:foo]
381
+ end
382
+ ```
383
+
384
+ In fact, since using this with Sinatra is the main use case, it comes with a build-in extension for **Sinatra 1.x**.
385
+
386
+ ``` ruby
387
+ require 'sinatra'
388
+ require 'mustermann'
389
+
390
+ register Mustermann
391
+
392
+ # this will use Mustermann rather than the built-in pattern matching
393
+ get '/:slug(.ext)?' do
394
+ params[:slug]
395
+ end
396
+ ```
397
+
398
+ ### Configuration
399
+
400
+ You can change what pattern type you want to use for your app via the `pattern` option:
401
+
402
+ ``` ruby
403
+ require 'sinatra/base'
404
+ require 'mustermann'
405
+
406
+ class MyApp < Sinatra::Base
407
+ register Mustermann
408
+ set :pattern, type: :shell
409
+
410
+ get '/images/*.png' do
411
+ send_file request.path_info
412
+ end
413
+
414
+ get '/index{.htm,.html,}' do
415
+ erb :index
416
+ end
417
+ end
418
+ ```
419
+
420
+ You can use the same setting for options:
421
+
422
+ ``` ruby
423
+ require 'sinatra'
424
+ require 'mustermann'
425
+
426
+ register Mustermann
427
+ set :pattern, capture: { ext: %w[png jpg html txt] }
428
+
429
+ get '/:slug(.:ext)?' do
430
+ # slug will be 'foo' for '/foo.png'
431
+ # slug will be 'foo.bar' for '/foo.bar'
432
+ # slug will be 'foo.bar' for '/foo.bar.html'
433
+ params[:slug]
434
+ end
435
+ ```
436
+
437
+ It is also possible to pass in options to a specific route:
438
+
439
+ ``` ruby
440
+ require 'sinatra'
441
+ require 'mustermann'
442
+
443
+ register Mustermann
444
+
445
+ get '/:slug(.:ext)?', pattern: { greedy: false } do
446
+ # slug will be 'foo' for '/foo.png'
447
+ # slug will be 'foo' for '/foo.bar'
448
+ # slug will be 'foo' for '/foo.bar.html'
449
+ params[:slug]
450
+ end
451
+ ```
452
+
453
+ Of course, all of the above can be combined.
454
+ Moreover, the `capture` and the `except` option can be passed to route directly.
455
+ And yes, this also works with `before` and `after` filters.
456
+
457
+ ``` ruby
458
+ require 'sinatra/base'
459
+ require 'sinatra/respond_with'
460
+ require 'mustermann'
461
+
462
+ class MyApp < Sinatra::Base
463
+ register Mustermann, Sinatra::RespondWith
464
+ set :pattern, capture: { id: /\d+/ } # id will only match digits
465
+
466
+ # only capture extensions known to Rack
467
+ before '*:ext', capture: Rack::Mime::MIME_TYPES.keys do
468
+ content_type params[:ext] # set Content-Type
469
+ request.path_info = params[:splat].first # drop the extension
470
+ end
471
+
472
+ get '/:id' do
473
+ not_found unless page = Page.find params[:id]
474
+ respond_with(page)
475
+ end
476
+ end
477
+ ```
478
+
479
+ ### Why would I want this?
480
+
481
+ * It gives you fine grained control over the pattern matching
482
+ * Allows you to use different pattern styles in your app
483
+ * The default is more robust and powerful than the built-in patterns
484
+ * Sinatra 2.0 will use Mustermann internally
485
+ * Better exceptions for broken route syntax
486
+
487
+ ### Why not include this in Sinatra 1.x?
488
+
489
+ * It would introduce breaking changes, even though these would be minor
490
+ * Like Sinatra 2.0, Mustermann requires Ruby 2.0 or newer
491
+
492
+ <a name="-duck-typing"></a>
493
+ ## Duck Typing
494
+
495
+ <a name="-duck-typing-to-pattern"></a>
496
+ ### `to_pattern`
497
+
498
+ All methods converting string input to pattern objects will also accept any arbitrary object that implements `to_pattern`:
499
+
500
+ ``` ruby
501
+ require 'mustermann'
502
+
503
+ class MyObject
504
+ def to_pattern(**options)
505
+ Mustermann.new("/foo", **options)
506
+ end
507
+ end
508
+
509
+ object = MyObject.new
510
+ Mustermann.new(object, type: :rails) # => #<Mustermann::Rails:"/foo">
511
+ ```
512
+
513
+ It might also be that you want to call `to_pattern` yourself instead of `Mustermann.new`. You can load `mustermann/to_pattern` to implement this method for strings, regular expressions and pattern objects:
514
+
515
+ ``` ruby
516
+ require 'mustermann/to_pattern'
517
+
518
+ "/foo".to_pattern # => #<Mustermann::Sinatra:"/foo">
519
+ "/foo".to_pattern(type: :rails) # => #<Mustermann::Rails:"/foo">
520
+ %r{/foo}.to_pattern # => #<Mustermann::Regular:"\\/foo">
521
+ "/foo".to_pattern.to_pattern # => #<Mustermann::Sinatra:"/foo">
522
+ ```
523
+
524
+ You can also use the `Mustermann::ToPattern` mixin to easily add `to_pattern` to your own objects:
525
+
526
+ ``` ruby
527
+ require 'mustermann/to_pattern'
528
+
529
+ class MyObject
530
+ include Mustermann::ToPattern
531
+
532
+ def to_s
533
+ "/foo"
534
+ end
535
+ end
536
+
537
+ MyObject.new.to_pattern # => #<Mustermann::Sinatra:"/foo">
538
+ ```
539
+
540
+ <a name="-duck-typing-respond-to"></a>
541
+ ### `respond_to?`
542
+
543
+ You can and should use `respond_to?` to check if a pattern supports certain features.
544
+
545
+ ``` ruby
546
+ require 'mustermann'
547
+ pattern = Mustermann.new("/")
548
+
549
+ puts "supports expanding" if pattern.respond_to? :expand
550
+ puts "supports generating templates" if pattern.respond_to? :to_templates
551
+ ```
552
+
553
+ Alternatively, you can handle a `NotImplementedError` raised from such a method.
554
+
555
+ ``` ruby
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
564
+ ```
565
+
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
570
+
571
+ <a name="-available-options--capture"></a>
572
+ ### `capture`
573
+
574
+ Supported by: All types except `identity`, `shell` and `simple` patterns.
575
+
576
+ Most pattern types support changing the strings named captures will match via the `capture` options.
577
+
578
+ Possible values for a capture:
579
+
580
+ ``` ruby
581
+ # String: Matches the given string (or any URI encoded version of it)
582
+ Mustermann.new('/index.:ext', capture: 'png')
583
+
584
+ # Regexp: Matches the Regular expression
585
+ Mustermann.new('/:id', capture: /\d+/)
586
+
587
+ # Symbol: Matches POSIX character class
588
+ Mustermann.new('/:id', capture: :digit)
589
+
590
+ # Array of the above: Matches anything in the array
591
+ Mustermann.new('/:id_or_slug', capture: [/\d+/, :word])
592
+
593
+ # Hash of the above: Looks up the hash entry by capture name and uses value for matching
594
+ Mustermann.new('/:id.:ext', capture: { id: /\d+/, ext: ['png', 'jpg'] })
595
+ ```
596
+
597
+ Available POSIX character classes are: `:alnum`, `:alpha`, `:blank`, `:cntrl`, `:digit`, `:graph`, `:lower`, `:print`, `:punct`, `:space`, `:upper`, `:xdigit`, `:word` and `:ascii`.
598
+
599
+ <a name="-available-options--except"></a>
600
+ ### `except`
601
+
602
+ Supported by: All types except `identity`, `shell` and `simple` patterns.
603
+
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:
605
+
606
+ ``` ruby
607
+ pattern = Mustermann.new('/auth/*', except: '/auth/login')
608
+ pattern === '/auth/dunno' # => true
609
+ pattern === '/auth/login' # => false
610
+ ```
611
+
612
+ Now, as said above, `except` treats the value as a pattern:
613
+
614
+ ``` ruby
615
+ pattern = Mustermann.new('/*anything', type: :rails, except: '/*anything.png')
616
+ pattern === '/foo.jpg' # => true
617
+ pattern === '/foo.png' # => false
618
+ ```
619
+
620
+ <a name="-available-options--greedy"></a>
621
+ ### `greedy`
622
+
623
+ Supported by: All types except `identity` and `shell` patterns.
624
+ Default value: `true`
625
+
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`.
627
+
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.
629
+
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.
631
+
632
+ ``` ruby
633
+ pattern = Mustermann.new(':a.:b', greedy: true)
634
+ pattern.match('a.b.c.d') # => #<MatchData a:"a.b.c" b:"d">
635
+
636
+ pattern = Mustermann.new(':a.:b', greedy: false)
637
+ pattern.match('a.b.c.d') # => #<MatchData a:"a" b:"b.c.d">
638
+ ```
639
+
640
+ <a name="-available-options--space_matches_plus"></a>
641
+ ### `space_matches_plus`
642
+
643
+ Supported by: All types except `identity`, `regexp` and `shell` patterns.
644
+ Default value: `true`
645
+
646
+ Most pattern types will by default also match a plus sign for a space in the pattern:
647
+
648
+ ``` ruby
649
+ Mustermann.new('a b') === 'a+b' # => true
650
+ ```
651
+
652
+ You can disable this behavior via `space_matches_plus`:
653
+
654
+ ``` ruby
655
+ Mustermann.new('a b', space_matches_plus: false) === 'a+b' # => false
656
+ ```
657
+
658
+ **Important:** This setting has no effect on captures, captures will always keep plus signs as plus sings and spaces as spaces:
659
+
660
+ ``` ruby
661
+ pattern = Mustermann.new(':x')
662
+ pattern.match('a b')[:x] # => 'a b'
663
+ pattern.match('a+b')[:x] # => 'a+b'
664
+ ````
665
+
666
+ <a name="-available-options--uri_decode"></a>
667
+ ### `uri_decode`
668
+
669
+ Supported by all pattern types.
670
+ Default value: `true`
671
+
672
+ Usually, characters in the pattern will also match the URI encoded version of these characters:
673
+
674
+ ``` ruby
675
+ Mustermann.new('a b') === 'a b' # => true
676
+ Mustermann.new('a b') === 'a%20b' # => true
677
+ ```
678
+
679
+ You can avoid this by setting `uri_decode` to `false`:
680
+
681
+ ``` ruby
682
+ Mustermann.new('a b', uri_decode: false) === 'a b' # => true
683
+ Mustermann.new('a b', uri_decode: false) === 'a%20b' # => false
684
+ ```
685
+
686
+ <a name="-available-options--ignore_unknown_options"></a>
687
+ ### `ignore_unknown_options`
688
+
689
+ Supported by all patterns.
690
+ Default value: `false`
691
+
692
+ If you pass an option in that is not supported by the specific pattern type, Mustermann will raise an `ArgumentError`.
693
+ By setting `ignore_unknown_options` to `true`, it will happily ignore the option.
694
+
695
+ <a name="-performance"></a>
696
+ ## Performance
697
+
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.
699
+
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.
701
+
702
+ ### String Matching
703
+
704
+ When using a pattern instead of a regular expression for string matching, performance will usually be comparable.
705
+
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).
707
+
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).
709
+
710
+ ### Expanding
711
+
712
+ Pattern expansion significantly outperforms other, widely used Ruby tools for generating URLs from URL patterns in most use cases.
713
+
714
+ This comes with a few trade-offs:
715
+
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.
720
+
721
+ ## Details on Pattern Types
722
+
723
+ <a name="-identity-pattern"></a>
724
+ ### `identity`
725
+
726
+ **Supported options:**
727
+ [`uri_decode`](#-available-options--uri_decode),
728
+ [`ignore_unknown_options`](#-available-options--ignore_unknown_options).
729
+
730
+ <table>
731
+ <thead>
732
+ <tr>
733
+ <th>Syntax Element</th>
734
+ <th>Description</th>
735
+ </tr>
736
+ </thead>
737
+ <tbody>
738
+ <tr>
739
+ <td><i>any character</i></td>
740
+ <td>Matches exactly that character or a URI escaped version of it.</td>
741
+ </tr>
742
+ </tbody>
743
+ </table>
744
+
745
+ <a name="-regexp-pattern"></a>
746
+ ### `regexp`
747
+
748
+ **Supported options:**
749
+ [`uri_decode`](#-available-options--uri_decode),
750
+ [`ignore_unknown_options`](#-available-options--ignore_unknown_options).
751
+
752
+ <table>
753
+ <thead>
754
+ <tr>
755
+ <th>Syntax Element</th>
756
+ <th>Description</th>
757
+ </tr>
758
+ </thead>
759
+ <tbody>
760
+ <tr>
761
+ <td><i>any string</i></td>
762
+ <td>Interpreted as regular expression.</td>
763
+ </tr>
764
+ </tbody>
765
+ </table>
766
+
767
+ <a name="-sinatra-pattern"></a>
768
+ ### `sinatra`
769
+
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).
777
+
778
+ <table>
779
+ <thead>
780
+ <tr>
781
+ <th>Syntax Element</th>
782
+ <th>Description</th>
783
+ </tr>
784
+ </thead>
785
+ <tbody>
786
+ <tr>
787
+ <td><b>:</b><i>name</i> <i><b>or</b></i> <b>&#123;</b><i>name</i><b>&#125;</b></td>
788
+ <td>
789
+ Captures anything but a forward slash in a semi-greedy fashion. Capture is named <i>name</i>.
790
+ Capture behavior can be modified with <tt>capture</tt> and <tt>greedy</tt> option.
791
+ </td>
792
+ </tr>
793
+ <tr>
794
+ <td><b>*</b><i>name</i> <i><b>or</b></i> <b>&#123;+</b><i>name</i><b>&#125;</b></td>
795
+ <td>
796
+ Captures anything in a non-greedy fashion. Capture is named <i>name</i>.
797
+ </td>
798
+ </tr>
799
+ <tr>
800
+ <td><b>*</b> <i><b>or</b></i> <b>&#123;+splat&#125;</b></td>
801
+ <td>
802
+ Captures anything in a non-greedy fashion. Capture is named splat.
803
+ It is always an array of captures, as you can use it more than once in a pattern.
804
+ </td>
805
+ </tr>
806
+ <tr>
807
+ <td><b>(</b><i>expression</i><b>)</b></td>
808
+ <td>
809
+ Enclosed <i>expression</i> is a group. Useful when combined with <tt>?</tt> to make it optional,
810
+ or to separate two elements that would otherwise be parsed as one.
811
+ </td>
812
+ </tr>
813
+ <tr>
814
+ <td><i>expression</i><b>|</b><i>expression</i><b>|</b><i>...</i></td>
815
+ <td>
816
+ Will match anything matching the nested expressions. May contain any other syntax element, including captures.
817
+ </td>
818
+ </tr>
819
+ <tr>
820
+ <td><i>x</i><b>?</b></td>
821
+ <td>Makes <i>x</i> optional. For instance, <tt>(foo)?</tt> matches <tt>foo</tt> or an empty string.</td>
822
+ </tr>
823
+ <tr>
824
+ <td><b>/</b></td>
825
+ <td>
826
+ Matches forward slash. Does not match URI encoded version of forward slash.
827
+ </td>
828
+ </tr>
829
+ <tr>
830
+ <td><b>\</b><i>x</i></td>
831
+ <td>Matches <i>x</i> or URI encoded version of <i>x</i>. For instance <tt>\*</tt> matches <tt>*</tt>.</td>
832
+ </tr>
833
+ <tr>
834
+ <td><i>any other character</i></td>
835
+ <td>Matches exactly that character or a URI encoded version of it.</td>
836
+ </tr>
837
+ </tbody>
838
+ </table>