mustermann-express 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +96 -0
- data/lib/mustermann/express.rb +37 -0
- data/mustermann-express.gemspec +18 -0
- data/spec/express_spec.rb +209 -0
- metadata +63 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9c6795ad5822893caa59d2f3a3221c837d689fc3
|
4
|
+
data.tar.gz: 08d87ec97b358dc5ccb159f21e57b1559bd73dc6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: fd5a13bbba9b2701fbc0666d7329edd2f8aae9380ca9dac0e62936d14d439dc8b895b50c97a54da5d647683fbff2268a92f42e167d11e8044e04681d504c18ea
|
7
|
+
data.tar.gz: fce9833ea4809b93e30c75696763b8bf390e272b0e95115a3f6e671108d009726b57f955cd8229564a826b3d1e2a9a8cd465c3b7ba29e52583c1bc591271e40e
|
data/README.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# Express Syntax for Mustermann
|
2
|
+
|
3
|
+
This gem implements the `express` pattern type for Mustermann. It is compatible with [Express](http://expressjs.com/) and [pillar.js](https://pillarjs.github.io/).
|
4
|
+
|
5
|
+
## Overview
|
6
|
+
|
7
|
+
**Supported options:**
|
8
|
+
`capture`, `except`, `greedy`, `space_matches_plus`, `uri_decode`, and `ignore_unknown_options`.
|
9
|
+
|
10
|
+
**External documentation:**
|
11
|
+
[path-to-regexp](https://github.com/pillarjs/path-to-regexp#path-to-regexp),
|
12
|
+
[live demo](http://forbeslindesay.github.io/express-route-tester/)
|
13
|
+
|
14
|
+
Express patterns feature named captures (with repetition support via suffixes) that start with a colon and can have an optional regular expression constraint or unnamed captures that require a constraint.
|
15
|
+
|
16
|
+
``` ruby
|
17
|
+
require 'mustermann/express'
|
18
|
+
|
19
|
+
Mustermann.new('/:name/:rest+', type: :express).params('/a/b/c') # => { name: 'a', rest: 'b/c' }
|
20
|
+
|
21
|
+
pattern = Mustermann.new('/:name', type: :express)
|
22
|
+
|
23
|
+
pattern.respond_to? :expand # => true
|
24
|
+
pattern.expand(name: 'foo') # => '/foo'
|
25
|
+
|
26
|
+
pattern.respond_to? :to_templates # => true
|
27
|
+
pattern.to_templates # => ['/{name}']
|
28
|
+
```
|
29
|
+
|
30
|
+
## Syntax
|
31
|
+
|
32
|
+
<table>
|
33
|
+
<thead>
|
34
|
+
<tr>
|
35
|
+
<th>Syntax Element</th>
|
36
|
+
<th>Description</th>
|
37
|
+
</tr>
|
38
|
+
</thead>
|
39
|
+
<tbody>
|
40
|
+
<tr>
|
41
|
+
<td><b>:</b><i>name</i></td>
|
42
|
+
<td>
|
43
|
+
Captures anything but a forward slash in a semi-greedy fashion. Capture is named <i>name</i>.
|
44
|
+
Capture behavior can be modified with <tt>capture</tt> and <tt>greedy</tt> option.
|
45
|
+
</td>
|
46
|
+
</tr>
|
47
|
+
<tr>
|
48
|
+
<td><b>:</b><i>name</i><b>+</b></td>
|
49
|
+
<td>
|
50
|
+
Captures one or more segments (with segments being separated by forward slashes).
|
51
|
+
Capture is named <i>name</i>.
|
52
|
+
Capture behavior can be modified with <tt>capture</tt> option.
|
53
|
+
</td>
|
54
|
+
</tr>
|
55
|
+
<tr>
|
56
|
+
<td><b>:</b><i>name</i><b>*</b></td>
|
57
|
+
<td>
|
58
|
+
Captures zero or more segments (with segments being separated by forward slashes).
|
59
|
+
Capture is named <i>name</i>.
|
60
|
+
Capture behavior can be modified with <tt>capture</tt> option.
|
61
|
+
</td>
|
62
|
+
</tr>
|
63
|
+
<tr>
|
64
|
+
<td><b>:</b><i>name</i><b>?</b></td>
|
65
|
+
<td>
|
66
|
+
Captures anything but a forward slash in a semi-greedy fashion. Capture is named <i>name</i>.
|
67
|
+
Also matches an empty string.
|
68
|
+
Capture behavior can be modified with <tt>capture</tt> and <tt>greedy</tt> option.
|
69
|
+
</td>
|
70
|
+
</tr>
|
71
|
+
<tr>
|
72
|
+
<td><b>:</b><i>name</i><b>(</b><i>regexp</i><b>)</b></td>
|
73
|
+
<td>
|
74
|
+
Captures anything matching the <i>regexp</i> regular expression. Capture is named <i>name</i>.
|
75
|
+
Capture behavior can be modified with <tt>capture</tt>.
|
76
|
+
</td>
|
77
|
+
</tr>
|
78
|
+
<tr>
|
79
|
+
<td><b>(</b><i>regexp</i><b>)</b></td>
|
80
|
+
<td>
|
81
|
+
Captures anything matching the <i>regexp</i> regular expression. Capture is named splat.
|
82
|
+
Capture behavior can be modified with <tt>capture</tt>.
|
83
|
+
</td>
|
84
|
+
</tr>
|
85
|
+
<tr>
|
86
|
+
<td><b>/</b></td>
|
87
|
+
<td>
|
88
|
+
Matches forward slash. Does not match URI encoded version of forward slash.
|
89
|
+
</td>
|
90
|
+
</tr>
|
91
|
+
<tr>
|
92
|
+
<td><i>any other character</i></td>
|
93
|
+
<td>Matches exactly that character or a URI encoded version of it.</td>
|
94
|
+
</tr>
|
95
|
+
</tbody>
|
96
|
+
</table>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'mustermann'
|
2
|
+
require 'mustermann/ast/pattern'
|
3
|
+
|
4
|
+
module Mustermann
|
5
|
+
# Express style pattern implementation.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# Mustermann.new('/:foo', type: :express) === '/bar' # => true
|
9
|
+
#
|
10
|
+
# @see Mustermann::Pattern
|
11
|
+
# @see file:README.md#flask Syntax description in the README
|
12
|
+
class Express < AST::Pattern
|
13
|
+
register :express
|
14
|
+
|
15
|
+
on(nil, ??, ?+, ?*, ?)) { |c| unexpected(c) }
|
16
|
+
on(?:) { |c| node(:capture) { scan(/\w+/) } }
|
17
|
+
on(?() { |c| node(:splat, constraint: read_brackets(?(, ?))) }
|
18
|
+
|
19
|
+
suffix ??, after: :capture do |char, element|
|
20
|
+
unexpected(char) unless element.is_a? :capture
|
21
|
+
node(:optional, element)
|
22
|
+
end
|
23
|
+
|
24
|
+
suffix ?*, after: :capture do |match, element|
|
25
|
+
node(:named_splat, element.name)
|
26
|
+
end
|
27
|
+
|
28
|
+
suffix ?+, after: :capture do |match, element|
|
29
|
+
node(:named_splat, element.name, constraint: ".+")
|
30
|
+
end
|
31
|
+
|
32
|
+
suffix ?(, after: :capture do |match, element|
|
33
|
+
element.constraint = read_brackets(?(, ?))
|
34
|
+
element
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
$:.unshift File.expand_path("../../mustermann/lib", __FILE__)
|
2
|
+
require "mustermann/version"
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = "mustermann-express"
|
6
|
+
s.version = Mustermann::VERSION
|
7
|
+
s.author = "Konstantin Haase"
|
8
|
+
s.email = "konstantin.mailinglists@googlemail.com"
|
9
|
+
s.homepage = "https://github.com/rkh/mustermann"
|
10
|
+
s.summary = %q{Express.js syntax for Mustermann}
|
11
|
+
s.description = %q{Adds express.js style patterns to Mustermman}
|
12
|
+
s.license = 'MIT'
|
13
|
+
s.required_ruby_version = '>= 2.1.0'
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
+
s.add_dependency 'mustermann', Mustermann::VERSION
|
18
|
+
end
|
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'support'
|
2
|
+
require 'mustermann/express'
|
3
|
+
|
4
|
+
describe Mustermann::Express do
|
5
|
+
extend Support::Pattern
|
6
|
+
|
7
|
+
pattern '' do
|
8
|
+
it { should match('') }
|
9
|
+
it { should_not match('/') }
|
10
|
+
|
11
|
+
it { should expand.to('') }
|
12
|
+
it { should_not expand(a: 1) }
|
13
|
+
|
14
|
+
it { should generate_template('') }
|
15
|
+
|
16
|
+
it { should respond_to(:expand) }
|
17
|
+
it { should respond_to(:to_templates) }
|
18
|
+
end
|
19
|
+
|
20
|
+
pattern '/' do
|
21
|
+
it { should match('/') }
|
22
|
+
it { should_not match('/foo') }
|
23
|
+
|
24
|
+
it { should expand.to('/') }
|
25
|
+
it { should_not expand(a: 1) }
|
26
|
+
end
|
27
|
+
|
28
|
+
pattern '/foo' do
|
29
|
+
it { should match('/foo') }
|
30
|
+
it { should_not match('/bar') }
|
31
|
+
it { should_not match('/foo.bar') }
|
32
|
+
|
33
|
+
it { should expand.to('/foo') }
|
34
|
+
it { should_not expand(a: 1) }
|
35
|
+
end
|
36
|
+
|
37
|
+
pattern '/foo/bar' do
|
38
|
+
it { should match('/foo/bar') }
|
39
|
+
it { should_not match('/foo%2Fbar') }
|
40
|
+
it { should_not match('/foo%2fbar') }
|
41
|
+
|
42
|
+
it { should expand.to('/foo/bar') }
|
43
|
+
it { should_not expand(a: 1) }
|
44
|
+
end
|
45
|
+
|
46
|
+
pattern '/:foo' do
|
47
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
48
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
49
|
+
it { should match('/foo.bar') .capturing foo: 'foo.bar' }
|
50
|
+
it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
|
51
|
+
it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
|
52
|
+
|
53
|
+
it { should_not match('/foo?') }
|
54
|
+
it { should_not match('/foo/bar') }
|
55
|
+
it { should_not match('/') }
|
56
|
+
it { should_not match('/foo/') }
|
57
|
+
|
58
|
+
example { pattern.params('/foo') .should be == {"foo" => "foo"} }
|
59
|
+
example { pattern.params('/f%20o') .should be == {"foo" => "f o"} }
|
60
|
+
example { pattern.params('').should be_nil }
|
61
|
+
|
62
|
+
it { should expand(foo: 'bar') .to('/bar') }
|
63
|
+
it { should expand(foo: 'b r') .to('/b%20r') }
|
64
|
+
it { should expand(foo: 'foo/bar') .to('/foo%2Fbar') }
|
65
|
+
|
66
|
+
it { should_not expand(foo: 'foo', bar: 'bar') }
|
67
|
+
it { should_not expand(bar: 'bar') }
|
68
|
+
it { should_not expand }
|
69
|
+
|
70
|
+
it { should generate_template('/{foo}') }
|
71
|
+
end
|
72
|
+
|
73
|
+
pattern '/:foo+' do
|
74
|
+
it { should_not match('/') }
|
75
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
76
|
+
it { should match('/foo/bar') .capturing foo: 'foo/bar' }
|
77
|
+
|
78
|
+
it { should expand .to('/') }
|
79
|
+
it { should expand(foo: nil) .to('/') }
|
80
|
+
it { should expand(foo: '') .to('/') }
|
81
|
+
it { should expand(foo: 'foo') .to('/foo') }
|
82
|
+
it { should expand(foo: 'foo/bar') .to('/foo/bar') }
|
83
|
+
it { should expand(foo: 'foo.bar') .to('/foo.bar') }
|
84
|
+
|
85
|
+
it { should generate_template('/{+foo}') }
|
86
|
+
end
|
87
|
+
|
88
|
+
pattern '/:foo?' do
|
89
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
90
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
91
|
+
it { should match('/foo.bar') .capturing foo: 'foo.bar' }
|
92
|
+
it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
|
93
|
+
it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
|
94
|
+
it { should match('/') }
|
95
|
+
|
96
|
+
it { should_not match('/foo?') }
|
97
|
+
it { should_not match('/foo/bar') }
|
98
|
+
it { should_not match('/foo/') }
|
99
|
+
|
100
|
+
example { pattern.params('/foo') .should be == {"foo" => "foo"} }
|
101
|
+
example { pattern.params('/f%20o') .should be == {"foo" => "f o"} }
|
102
|
+
example { pattern.params('/') .should be == {"foo" => nil } }
|
103
|
+
|
104
|
+
it { should expand(foo: 'bar') .to('/bar') }
|
105
|
+
it { should expand(foo: 'b r') .to('/b%20r') }
|
106
|
+
it { should expand(foo: 'foo/bar') .to('/foo%2Fbar') }
|
107
|
+
it { should expand .to('/') }
|
108
|
+
|
109
|
+
it { should_not expand(foo: 'foo', bar: 'bar') }
|
110
|
+
it { should_not expand(bar: 'bar') }
|
111
|
+
|
112
|
+
it { should generate_template('/{foo}') }
|
113
|
+
it { should generate_template('/') }
|
114
|
+
end
|
115
|
+
|
116
|
+
pattern '/:foo*' do
|
117
|
+
it { should match('/') .capturing foo: '' }
|
118
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
119
|
+
it { should match('/foo/bar') .capturing foo: 'foo/bar' }
|
120
|
+
|
121
|
+
it { should expand .to('/') }
|
122
|
+
it { should expand(foo: nil) .to('/') }
|
123
|
+
it { should expand(foo: '') .to('/') }
|
124
|
+
it { should expand(foo: 'foo') .to('/foo') }
|
125
|
+
it { should expand(foo: 'foo/bar') .to('/foo/bar') }
|
126
|
+
it { should expand(foo: 'foo.bar') .to('/foo.bar') }
|
127
|
+
|
128
|
+
it { should generate_template('/{+foo}') }
|
129
|
+
end
|
130
|
+
|
131
|
+
pattern '/:foo(.*)' do
|
132
|
+
it { should match('/') .capturing foo: '' }
|
133
|
+
it { should match('/foo') .capturing foo: 'foo' }
|
134
|
+
it { should match('/foo/bar') .capturing foo: 'foo/bar' }
|
135
|
+
|
136
|
+
it { should expand(foo: '') .to('/') }
|
137
|
+
it { should expand(foo: 'foo') .to('/foo') }
|
138
|
+
it { should expand(foo: 'foo/bar') .to('/foo/bar') }
|
139
|
+
it { should expand(foo: 'foo.bar') .to('/foo.bar') }
|
140
|
+
|
141
|
+
it { should generate_template('/{foo}') }
|
142
|
+
end
|
143
|
+
|
144
|
+
pattern '/:foo(\d+)' do
|
145
|
+
it { should_not match('/') }
|
146
|
+
it { should_not match('/foo') }
|
147
|
+
it { should match('/15') .capturing foo: '15' }
|
148
|
+
it { should generate_template('/{foo}') }
|
149
|
+
end
|
150
|
+
|
151
|
+
pattern '/:foo(\d+|bar)' do
|
152
|
+
it { should_not match('/') }
|
153
|
+
it { should_not match('/foo') }
|
154
|
+
it { should match('/15') .capturing foo: '15' }
|
155
|
+
it { should match('/bar') .capturing foo: 'bar' }
|
156
|
+
it { should generate_template('/{foo}') }
|
157
|
+
end
|
158
|
+
|
159
|
+
pattern '/:foo(\))' do
|
160
|
+
it { should_not match('/') }
|
161
|
+
it { should_not match('/foo') }
|
162
|
+
it { should match('/)').capturing foo: ')' }
|
163
|
+
it { should generate_template('/{foo}') }
|
164
|
+
end
|
165
|
+
|
166
|
+
pattern '/:foo(prefix(\d+|bar))' do
|
167
|
+
it { should_not match('/prefix') }
|
168
|
+
it { should_not match('/prefixfoo') }
|
169
|
+
it { should match('/prefix15') .capturing foo: 'prefix15' }
|
170
|
+
it { should match('/prefixbar') .capturing foo: 'prefixbar' }
|
171
|
+
it { should generate_template('/{foo}') }
|
172
|
+
end
|
173
|
+
|
174
|
+
pattern '/(.+)' do
|
175
|
+
it { should_not match('/') }
|
176
|
+
it { should match('/foo') .capturing splat: 'foo' }
|
177
|
+
it { should match('/foo/bar') .capturing splat: 'foo/bar' }
|
178
|
+
it { should generate_template('/{+splat}') }
|
179
|
+
end
|
180
|
+
|
181
|
+
pattern '/(foo(a|b))' do
|
182
|
+
it { should_not match('/') }
|
183
|
+
it { should match('/fooa') .capturing splat: 'fooa' }
|
184
|
+
it { should match('/foob') .capturing splat: 'foob' }
|
185
|
+
it { should generate_template('/{+splat}') }
|
186
|
+
end
|
187
|
+
|
188
|
+
context 'invalid syntax' do
|
189
|
+
example 'unexpected closing parenthesis' do
|
190
|
+
expect { Mustermann::Express.new('foo)bar') }.
|
191
|
+
to raise_error(Mustermann::ParseError, 'unexpected ) while parsing "foo)bar"')
|
192
|
+
end
|
193
|
+
|
194
|
+
example 'missing closing parenthesis' do
|
195
|
+
expect { Mustermann::Express.new('foo(bar') }.
|
196
|
+
to raise_error(Mustermann::ParseError, 'unexpected end of string while parsing "foo(bar"')
|
197
|
+
end
|
198
|
+
|
199
|
+
example 'unexpected ?' do
|
200
|
+
expect { Mustermann::Express.new('foo?bar') }.
|
201
|
+
to raise_error(Mustermann::ParseError, 'unexpected ? while parsing "foo?bar"')
|
202
|
+
end
|
203
|
+
|
204
|
+
example 'unexpected *' do
|
205
|
+
expect { Mustermann::Express.new('foo*bar') }.
|
206
|
+
to raise_error(Mustermann::ParseError, 'unexpected * while parsing "foo*bar"')
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mustermann-express
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.4.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Konstantin Haase
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-11-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: mustermann
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.4.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.4.0
|
27
|
+
description: Adds express.js style patterns to Mustermman
|
28
|
+
email: konstantin.mailinglists@googlemail.com
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- README.md
|
34
|
+
- lib/mustermann/express.rb
|
35
|
+
- mustermann-express.gemspec
|
36
|
+
- spec/express_spec.rb
|
37
|
+
homepage: https://github.com/rkh/mustermann
|
38
|
+
licenses:
|
39
|
+
- MIT
|
40
|
+
metadata: {}
|
41
|
+
post_install_message:
|
42
|
+
rdoc_options: []
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: 2.1.0
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
requirements: []
|
56
|
+
rubyforge_project:
|
57
|
+
rubygems_version: 2.4.3
|
58
|
+
signing_key:
|
59
|
+
specification_version: 4
|
60
|
+
summary: Express.js syntax for Mustermann
|
61
|
+
test_files:
|
62
|
+
- spec/express_spec.rb
|
63
|
+
has_rdoc:
|