mustermann-express 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|