mustermann 0.4.0 → 1.0.0.beta2
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 +4 -4
- data/README.md +61 -46
- data/lib/mustermann.rb +3 -2
- data/lib/mustermann/ast/compiler.rb +2 -0
- data/lib/mustermann/ast/node.rb +4 -0
- data/lib/mustermann/ast/parser.rb +1 -22
- data/lib/mustermann/ast/pattern.rb +3 -3
- data/lib/mustermann/ast/transformer.rb +53 -4
- data/lib/mustermann/caster.rb +2 -10
- data/lib/mustermann/composite.rb +13 -3
- data/lib/mustermann/concat.rb +124 -0
- data/lib/mustermann/equality_map.rb +60 -0
- data/lib/mustermann/expander.rb +10 -6
- data/lib/mustermann/identity.rb +1 -0
- data/lib/mustermann/pattern.rb +53 -7
- data/lib/mustermann/regexp_based.rb +1 -1
- data/lib/mustermann/regular.rb +17 -2
- data/lib/mustermann/simple_match.rb +18 -5
- data/lib/mustermann/sinatra.rb +64 -16
- data/lib/mustermann/sinatra/parser.rb +45 -0
- data/lib/mustermann/sinatra/safe_renderer.rb +26 -0
- data/lib/mustermann/sinatra/try_convert.rb +48 -0
- data/lib/mustermann/version.rb +1 -1
- data/mustermann.gemspec +1 -2
- data/spec/composite_spec.rb +20 -5
- data/spec/concat_spec.rb +114 -0
- data/spec/equality_map_spec.rb +25 -0
- data/spec/mustermann_spec.rb +8 -5
- data/spec/regular_spec.rb +40 -0
- data/spec/sinatra_spec.rb +85 -5
- metadata +15 -42
- data/lib/mustermann/router.rb +0 -9
- data/lib/mustermann/router/rack.rb +0 -47
- data/lib/mustermann/router/simple.rb +0 -142
- data/spec/router/rack_spec.rb +0 -39
- data/spec/router/simple_spec.rb +0 -32
@@ -0,0 +1,26 @@
|
|
1
|
+
module Mustermann
|
2
|
+
class Sinatra < AST::Pattern
|
3
|
+
# Generates a string that can safely be concatenated with other strings
|
4
|
+
# without chaning its semantics
|
5
|
+
# @see #safe_string
|
6
|
+
# @!visibility private
|
7
|
+
SafeRenderer = AST::Translator.create do
|
8
|
+
translate(:splat, :named_splat) { "{+#{name}}" }
|
9
|
+
translate(:char, :separator) { Sinatra.escape(payload) }
|
10
|
+
translate(:root) { t(payload) }
|
11
|
+
translate(:group) { "(#{t(payload)})" }
|
12
|
+
translate(:union) { "(#{t(payload, join: ?|)})" }
|
13
|
+
translate(:optional) { "#{t(payload)}?" }
|
14
|
+
translate(Array) { |join: ""| map { |e| t(e) }.join(join) }
|
15
|
+
|
16
|
+
translate(:capture) do
|
17
|
+
raise Mustermann::Error, 'cannot render variables' if node.is_a? :variable
|
18
|
+
raise Mustermann::Error, 'cannot translate constraints' if constraint or qualifier or convert
|
19
|
+
prefix = node.is_a?(:splat) ? "+" : ""
|
20
|
+
"{#{prefix}#{name}}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private_constant :SafeRenderer
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Mustermann
|
2
|
+
class Sinatra < AST::Pattern
|
3
|
+
# Tries to translate objects to Sinatra patterns.
|
4
|
+
# @!visibility private
|
5
|
+
class TryConvert < AST::Translator
|
6
|
+
# @return [Mustermann::Sinatra, nil]
|
7
|
+
# @!visibility private
|
8
|
+
def self.convert(input, **options)
|
9
|
+
new(options).translate(input)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Expected options for the resulting pattern.
|
13
|
+
# @!visibility private
|
14
|
+
attr_reader :options
|
15
|
+
|
16
|
+
# @!visibility private
|
17
|
+
def initialize(options)
|
18
|
+
@options = options
|
19
|
+
end
|
20
|
+
|
21
|
+
# @return [Mustermann::Sinatra]
|
22
|
+
# @!visibility private
|
23
|
+
def new(input, escape = false)
|
24
|
+
input = Mustermann::Sinatra.escape(input) if escape
|
25
|
+
Mustermann::Sinatra.new(input, **options)
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [true, false] whether or not expected pattern should have uri_decode option set
|
29
|
+
# @!visibility private
|
30
|
+
def uri_decode
|
31
|
+
options.fetch(:uri_decode, true)
|
32
|
+
end
|
33
|
+
|
34
|
+
translate(Object) { nil }
|
35
|
+
translate(String) { t.new(self, true) }
|
36
|
+
|
37
|
+
translate(Identity) { t.new(self, true) if uri_decode == t.uri_decode }
|
38
|
+
translate(Sinatra) { node if options == t.options }
|
39
|
+
|
40
|
+
translate AST::Pattern do
|
41
|
+
next unless options == t.options
|
42
|
+
t.new(SafeRenderer.translate(to_ast)) rescue nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private_constant :TryConvert
|
47
|
+
end
|
48
|
+
end
|
data/lib/mustermann/version.rb
CHANGED
data/mustermann.gemspec
CHANGED
@@ -10,9 +10,8 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.summary = %q{use patterns like regular expressions}
|
11
11
|
s.description = %q{library implementing patterns that behave like regular expressions}
|
12
12
|
s.license = 'MIT'
|
13
|
-
s.required_ruby_version = '>= 2.
|
13
|
+
s.required_ruby_version = '>= 2.2.0'
|
14
14
|
s.files = `git ls-files`.split("\n")
|
15
15
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
16
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
17
|
-
s.add_dependency 'tool', '~> 0.2'
|
18
17
|
end
|
data/spec/composite_spec.rb
CHANGED
@@ -12,6 +12,14 @@ describe Mustermann::Composite do
|
|
12
12
|
pattern = Mustermann.new('/foo')
|
13
13
|
Mustermann::Composite.new(pattern).should be == pattern
|
14
14
|
end
|
15
|
+
|
16
|
+
example 'with supported type specific arguments' do
|
17
|
+
Mustermann::Composite.new("/a", "/b", greedy: true)
|
18
|
+
end
|
19
|
+
|
20
|
+
example 'with unsupported type specific arguments' do
|
21
|
+
expect { Mustermann::Composite.new("/a", "/b", greedy: true, type: :identity) }.to raise_error(ArgumentError)
|
22
|
+
end
|
15
23
|
end
|
16
24
|
|
17
25
|
context :| do
|
@@ -64,6 +72,13 @@ describe Mustermann::Composite do
|
|
64
72
|
example { expect { subject.to_templates }.to raise_error(NotImplementedError) }
|
65
73
|
end
|
66
74
|
end
|
75
|
+
|
76
|
+
describe :eql? do
|
77
|
+
example { should be_eql(pattern) }
|
78
|
+
example { should be_eql(Mustermann.new('/foo/:name', '/:first/:second', operator: :|)) }
|
79
|
+
example { should_not be_eql(Mustermann.new('/bar/:name', '/:first/:second', operator: :|)) }
|
80
|
+
example { should_not be_eql(Mustermann.new('/foo/:name', '/:first/:second', operator: :&)) }
|
81
|
+
end
|
67
82
|
end
|
68
83
|
|
69
84
|
context :& do
|
@@ -136,12 +151,12 @@ describe Mustermann::Composite do
|
|
136
151
|
|
137
152
|
describe :inspect do
|
138
153
|
let(:sinatra) { Mustermann.new('x') }
|
139
|
-
let(:
|
154
|
+
let(:shell) { Mustermann.new('x', type: :shell) }
|
140
155
|
let(:identity) { Mustermann.new('x', type: :identity) }
|
141
156
|
|
142
|
-
example { (sinatra |
|
143
|
-
example { (sinatra ^
|
144
|
-
example { (sinatra |
|
145
|
-
example { (sinatra |
|
157
|
+
example { (sinatra | shell) .inspect.should include('(sinatra:"x" | shell:"x")') }
|
158
|
+
example { (sinatra ^ shell) .inspect.should include('(sinatra:"x" ^ shell:"x")') }
|
159
|
+
example { (sinatra | shell | identity) .inspect.should include('(sinatra:"x" | shell:"x" | identity:"x")') }
|
160
|
+
example { (sinatra | shell & identity) .inspect.should include('(sinatra:"x" | (shell:"x" & identity:"x"))') }
|
146
161
|
end
|
147
162
|
end
|
data/spec/concat_spec.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
require 'support'
|
2
|
+
require 'mustermann'
|
3
|
+
|
4
|
+
describe Mustermann::Concat do
|
5
|
+
describe Mustermann::Concat::Native do
|
6
|
+
context "sinatra + sinatra" do
|
7
|
+
subject(:pattern) { Mustermann.new("/:foo") + Mustermann.new("/:bar") }
|
8
|
+
its(:class) { should be == Mustermann::Sinatra }
|
9
|
+
its(:to_s) { should be == "/{foo}/{bar}" }
|
10
|
+
end
|
11
|
+
|
12
|
+
context "sinatra + string" do
|
13
|
+
subject(:pattern) { Mustermann.new("/:foo") + "/:bar" }
|
14
|
+
its(:class) { should be == Mustermann::Sinatra }
|
15
|
+
its(:to_s) { should be == "/{foo}/\\:bar" }
|
16
|
+
end
|
17
|
+
|
18
|
+
context "regular + regular" do
|
19
|
+
subject(:pattern) { Mustermann.new(/foo/) + Mustermann.new(/bar/) }
|
20
|
+
its(:class) { should be == Mustermann::Regular }
|
21
|
+
its(:to_s) { should be == "foobar" }
|
22
|
+
end
|
23
|
+
|
24
|
+
context "sinatra + rails" do
|
25
|
+
subject(:pattern) { Mustermann.new("/:foo") + Mustermann.new("/:bar", type: :rails) }
|
26
|
+
its(:class) { should be == Mustermann::Sinatra }
|
27
|
+
its(:to_s) { should be == "/{foo}/{bar}" }
|
28
|
+
end
|
29
|
+
|
30
|
+
context "sinatra + flask" do
|
31
|
+
subject(:pattern) { Mustermann.new("/:foo") + Mustermann.new("/<bar>", type: :flask) }
|
32
|
+
its(:class) { should be == Mustermann::Sinatra }
|
33
|
+
its(:to_s) { should be == "/{foo}/{bar}" }
|
34
|
+
end
|
35
|
+
|
36
|
+
context "sinatra + flask (typed)" do
|
37
|
+
subject(:pattern) { Mustermann.new("/:foo") + Mustermann.new("/<int:bar>", type: :flask) }
|
38
|
+
its(:class) { should be == Mustermann::Concat }
|
39
|
+
its(:to_s) { should be == '(sinatra:"/:foo" + flask:"/<int:bar>")' }
|
40
|
+
end
|
41
|
+
|
42
|
+
context "sinatra + sinatra (different options)" do
|
43
|
+
subject(:pattern) { Mustermann.new("/:foo") + Mustermann.new("/:bar", uri_decode: false) }
|
44
|
+
its(:class) { should be == Mustermann::Concat }
|
45
|
+
its(:to_s) { should be == '(sinatra:"/:foo" + sinatra:"/:bar")' }
|
46
|
+
end
|
47
|
+
|
48
|
+
context "sinatra + rails (different options)" do
|
49
|
+
subject(:pattern) { Mustermann.new("/:foo") + Mustermann.new("/:bar", type: :rails, uri_decode: false) }
|
50
|
+
its(:class) { should be == Mustermann::Concat }
|
51
|
+
its(:to_s) { should be == '(sinatra:"/:foo" + rails:"/:bar")' }
|
52
|
+
end
|
53
|
+
|
54
|
+
context "sinatra + rails (different options) + sinatra" do
|
55
|
+
subject(:pattern) { Mustermann.new("/:foo") + Mustermann.new("/:bar", type: :rails, uri_decode: false) + Mustermann.new("/:baz") }
|
56
|
+
its(:class) { should be == Mustermann::Concat }
|
57
|
+
its(:to_s) { should be == '(sinatra:"/:foo" + rails:"/:bar" + sinatra:"/:baz")' }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
subject(:pattern) { Mustermann::Concat.new("/:foo", "/:bar") }
|
62
|
+
|
63
|
+
describe :=== do
|
64
|
+
example { (pattern === "/foo/bar") .should be true }
|
65
|
+
example { (pattern === "/foo/bar/") .should be false }
|
66
|
+
example { (pattern === "/foo") .should be false }
|
67
|
+
end
|
68
|
+
|
69
|
+
describe :match do
|
70
|
+
it { should match("/foo/bar").capturing(foo: "foo", bar: "bar") }
|
71
|
+
it { should_not match("/foo/bar/") }
|
72
|
+
it { should_not match("/foo/") }
|
73
|
+
end
|
74
|
+
|
75
|
+
describe :params do
|
76
|
+
example { pattern.params("/foo/bar") .should be == { "foo" => "foo", "bar" => "bar" }}
|
77
|
+
example { pattern.params("/foo/bar/") .should be_nil }
|
78
|
+
example { pattern.params("/foo") .should be_nil }
|
79
|
+
end
|
80
|
+
|
81
|
+
describe :peek do
|
82
|
+
example { pattern.peek("/foo/bar/baz") .should be == "/foo/bar" }
|
83
|
+
example { pattern.peek("/foo") .should be_nil }
|
84
|
+
end
|
85
|
+
|
86
|
+
describe :peek_params do
|
87
|
+
example { pattern.peek_params("/foo/bar/baz") .should be == [{ "foo" => "foo", "bar" => "bar" }, 8]}
|
88
|
+
example { pattern.peek_params("/foo") .should be_nil }
|
89
|
+
end
|
90
|
+
|
91
|
+
describe :peek_match do
|
92
|
+
example { pattern.peek_match("/foo/bar/baz").to_s .should be == "/foo/bar" }
|
93
|
+
example { pattern.peek_match("/foo") .should be_nil }
|
94
|
+
end
|
95
|
+
|
96
|
+
describe :peek_size do
|
97
|
+
example { pattern.peek_size("/foo/bar/baz") .should be == 8 }
|
98
|
+
example { pattern.peek_size("/foo") .should be_nil }
|
99
|
+
end
|
100
|
+
|
101
|
+
describe :expand do
|
102
|
+
it { should expand(foo: :bar, bar: :foo) .to('/bar/foo') }
|
103
|
+
it { should expand(:append, foo: :bar, bar: :foo, baz: 42) .to('/bar/foo?baz=42') }
|
104
|
+
it { should_not expand(foo: :bar) }
|
105
|
+
end
|
106
|
+
|
107
|
+
describe :to_templates do
|
108
|
+
subject(:pattern) { Mustermann::Concat.new("/:foo|:bar", "(/:baz)?") }
|
109
|
+
it { should generate_template("/{foo}/{baz}") }
|
110
|
+
it { should generate_template("{bar}/{baz}") }
|
111
|
+
it { should generate_template("/{foo}") }
|
112
|
+
it { should generate_template("{bar}") }
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'support'
|
2
|
+
require 'mustermann/equality_map'
|
3
|
+
|
4
|
+
RSpec.describe Mustermann::EqualityMap do
|
5
|
+
before { GC.disable }
|
6
|
+
after { GC.enable }
|
7
|
+
|
8
|
+
describe :fetch do
|
9
|
+
subject { Mustermann::EqualityMap.new }
|
10
|
+
specify 'with existing entry' do
|
11
|
+
next if subject.is_a? Hash
|
12
|
+
subject.fetch("foo") { "foo" }
|
13
|
+
result = subject.fetch("foo") { "bar" }
|
14
|
+
expect(result).to be == "foo"
|
15
|
+
end
|
16
|
+
|
17
|
+
specify 'with GC-removed entry' do
|
18
|
+
next if subject.is_a? Hash
|
19
|
+
subject.fetch("foo") { "foo" }
|
20
|
+
expect(subject.map).to receive(:[]).and_return(nil)
|
21
|
+
result = subject.fetch("foo") { "bar" }
|
22
|
+
expect(result).to be == "bar"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/mustermann_spec.rb
CHANGED
@@ -37,11 +37,14 @@ describe Mustermann do
|
|
37
37
|
end
|
38
38
|
|
39
39
|
context "multiple arguments" do
|
40
|
-
example { Mustermann.new('', '') .should be_a(Mustermann::Composite) }
|
41
|
-
example { Mustermann.new('', '').patterns.first .should be_a(Mustermann::Sinatra) }
|
42
|
-
example { Mustermann.new('', '').operator .should be == :| }
|
43
|
-
example { Mustermann.new('', '', operator: :&).operator .should be == :& }
|
44
|
-
example { Mustermann.new('', '', greedy: true) .should be_a(Mustermann::Composite) }
|
40
|
+
example { Mustermann.new(':a', ':b/:a') .should be_a(Mustermann::Composite) }
|
41
|
+
example { Mustermann.new(':a', ':b/:a').patterns.first .should be_a(Mustermann::Sinatra) }
|
42
|
+
example { Mustermann.new(':a', ':b/:a').operator .should be == :| }
|
43
|
+
example { Mustermann.new(':a', ':b/:a', operator: :&).operator .should be == :& }
|
44
|
+
example { Mustermann.new(':a', ':b/:a', greedy: true) .should be_a(Mustermann::Composite) }
|
45
|
+
|
46
|
+
example { Mustermann.new('/foo', ':bar') .should be_a(Mustermann::Sinatra) }
|
47
|
+
example { Mustermann.new('/foo', ':bar').to_s .should be == "/foo|{bar}" }
|
45
48
|
end
|
46
49
|
|
47
50
|
context "invalid arguments" do
|
data/spec/regular_spec.rb
CHANGED
@@ -37,6 +37,46 @@ describe Mustermann::Regular do
|
|
37
37
|
it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
|
38
38
|
end
|
39
39
|
|
40
|
+
describe :check_achnors do
|
41
|
+
context 'raises on anchors' do
|
42
|
+
example { expect { Mustermann::Regular.new('^foo') }.to raise_error(Mustermann::CompileError) }
|
43
|
+
example { expect { Mustermann::Regular.new('foo$') }.to raise_error(Mustermann::CompileError) }
|
44
|
+
example { expect { Mustermann::Regular.new('\Afoo') }.to raise_error(Mustermann::CompileError) }
|
45
|
+
example { expect { Mustermann::Regular.new('foo\Z') }.to raise_error(Mustermann::CompileError) }
|
46
|
+
example { expect { Mustermann::Regular.new('foo\z') }.to raise_error(Mustermann::CompileError) }
|
47
|
+
example { expect { Mustermann::Regular.new(/^foo/) }.to raise_error(Mustermann::CompileError) }
|
48
|
+
example { expect { Mustermann::Regular.new(/foo$/) }.to raise_error(Mustermann::CompileError) }
|
49
|
+
example { expect { Mustermann::Regular.new(/\Afoo/) }.to raise_error(Mustermann::CompileError) }
|
50
|
+
example { expect { Mustermann::Regular.new(/foo\Z/) }.to raise_error(Mustermann::CompileError) }
|
51
|
+
example { expect { Mustermann::Regular.new(/foo\z/) }.to raise_error(Mustermann::CompileError) }
|
52
|
+
example { expect { Mustermann::Regular.new('[^f]') }.not_to raise_error }
|
53
|
+
example { expect { Mustermann::Regular.new('\\\A') }.not_to raise_error }
|
54
|
+
example { expect { Mustermann::Regular.new('[[:digit:]]') }.not_to raise_error }
|
55
|
+
example { expect { Mustermann::Regular.new(/[^f]/) }.not_to raise_error }
|
56
|
+
example { expect { Mustermann::Regular.new(/\\A/) }.not_to raise_error }
|
57
|
+
example { expect { Mustermann::Regular.new(/[[:digit:]]/) }.not_to raise_error }
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'with check_anchors disabled' do
|
61
|
+
example { expect { Mustermann::Regular.new('^foo', check_anchors: false) }.not_to raise_error }
|
62
|
+
example { expect { Mustermann::Regular.new('foo$', check_anchors: false) }.not_to raise_error }
|
63
|
+
example { expect { Mustermann::Regular.new('\Afoo', check_anchors: false) }.not_to raise_error }
|
64
|
+
example { expect { Mustermann::Regular.new('foo\Z', check_anchors: false) }.not_to raise_error }
|
65
|
+
example { expect { Mustermann::Regular.new('foo\z', check_anchors: false) }.not_to raise_error }
|
66
|
+
example { expect { Mustermann::Regular.new(/^foo/, check_anchors: false) }.not_to raise_error }
|
67
|
+
example { expect { Mustermann::Regular.new(/foo$/, check_anchors: false) }.not_to raise_error }
|
68
|
+
example { expect { Mustermann::Regular.new(/\Afoo/, check_anchors: false) }.not_to raise_error }
|
69
|
+
example { expect { Mustermann::Regular.new(/foo\Z/, check_anchors: false) }.not_to raise_error }
|
70
|
+
example { expect { Mustermann::Regular.new(/foo\z/, check_anchors: false) }.not_to raise_error }
|
71
|
+
example { expect { Mustermann::Regular.new('[^f]', check_anchors: false) }.not_to raise_error }
|
72
|
+
example { expect { Mustermann::Regular.new('\\\A', check_anchors: false) }.not_to raise_error }
|
73
|
+
example { expect { Mustermann::Regular.new('[[:digit:]]', check_anchors: false) }.not_to raise_error }
|
74
|
+
example { expect { Mustermann::Regular.new(/[^f]/, check_anchors: false) }.not_to raise_error }
|
75
|
+
example { expect { Mustermann::Regular.new(/\\A/, check_anchors: false) }.not_to raise_error }
|
76
|
+
example { expect { Mustermann::Regular.new(/[[:digit:]]/, check_anchors: false) }.not_to raise_error }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
40
80
|
context "peeking" do
|
41
81
|
subject(:pattern) { Mustermann::Regular.new("(?<name>[^/]+)") }
|
42
82
|
|
data/spec/sinatra_spec.rb
CHANGED
@@ -463,6 +463,40 @@ describe Mustermann::Sinatra do
|
|
463
463
|
it { should_not expand(a: 'foo', b: 'bar', c: 'baz') }
|
464
464
|
end
|
465
465
|
|
466
|
+
pattern "/:a/:b|:c" do
|
467
|
+
it { should match("foo") .capturing c: 'foo' }
|
468
|
+
it { should match("/foo/bar") .capturing a: 'foo', b: 'bar' }
|
469
|
+
|
470
|
+
it { should generate_template('/{a}/{b}') }
|
471
|
+
it { should generate_template('{c}') }
|
472
|
+
|
473
|
+
it { should expand(a: 'foo', b: 'bar') .to('/foo/bar') }
|
474
|
+
it { should expand(c: 'foo') .to('foo') }
|
475
|
+
it { should_not expand(a: 'foo', b: 'bar', c: 'baz') }
|
476
|
+
end
|
477
|
+
|
478
|
+
pattern "/({foo}|{bar})", capture: { foo: /\d+/, bar: /\w+/ } do
|
479
|
+
it { should match("/a") .capturing foo: nil, bar: 'a' }
|
480
|
+
it { should match("/1234") .capturing foo: "1234", bar: nil }
|
481
|
+
|
482
|
+
it { should_not match("/a/b") }
|
483
|
+
end
|
484
|
+
|
485
|
+
pattern "/{foo|bar}", capture: { foo: /\d+/, bar: /\w+/ } do
|
486
|
+
it { should match("/a") .capturing foo: nil, bar: 'a' }
|
487
|
+
it { should match("/1234") .capturing foo: "1234", bar: nil }
|
488
|
+
|
489
|
+
it { should_not match("/a/b") }
|
490
|
+
end
|
491
|
+
|
492
|
+
pattern "/{foo|bar|baz}", capture: { foo: /\d+/, bar: /[ab]+/, baz: /[cde]+/ } do
|
493
|
+
it { should match("/ab") .capturing foo: nil, bar: 'ab', baz: nil }
|
494
|
+
it { should match("/1234") .capturing foo: "1234", bar: nil, baz: nil }
|
495
|
+
it { should match("/ccddee") .capturing foo: nil, bar: nil, baz: "ccddee" }
|
496
|
+
|
497
|
+
it { should_not match("/a/b") }
|
498
|
+
end
|
499
|
+
|
466
500
|
pattern '/:foo', capture: /\d+/ do
|
467
501
|
it { should match('/1') .capturing foo: '1' }
|
468
502
|
it { should match('/123') .capturing foo: '123' }
|
@@ -648,11 +682,6 @@ describe Mustermann::Sinatra do
|
|
648
682
|
to raise_error(Mustermann::ParseError, 'unexpected ? while parsing "foo??bar"')
|
649
683
|
end
|
650
684
|
|
651
|
-
example '| outside of group' do
|
652
|
-
expect { Mustermann::Sinatra.new('foo|bar') }.
|
653
|
-
to raise_error(Mustermann::ParseError, 'unexpected | while parsing "foo|bar"')
|
654
|
-
end
|
655
|
-
|
656
685
|
example 'dangling escape' do
|
657
686
|
expect { Mustermann::Sinatra.new('foo\\') }.
|
658
687
|
to raise_error(Mustermann::ParseError, 'unexpected end of string while parsing "foo\\\\"')
|
@@ -745,4 +774,55 @@ describe Mustermann::Sinatra do
|
|
745
774
|
example { pattern.peek_params("/foo bar") .should be_nil }
|
746
775
|
end
|
747
776
|
end
|
777
|
+
|
778
|
+
describe :| do
|
779
|
+
let(:first) { Mustermann.new("a") }
|
780
|
+
let(:second) { Mustermann.new("b") }
|
781
|
+
subject(:composite) { first | second }
|
782
|
+
|
783
|
+
context "with no capture names" do
|
784
|
+
its(:class) { should be == Mustermann::Sinatra }
|
785
|
+
its(:to_s) { should be == "a|b" }
|
786
|
+
end
|
787
|
+
|
788
|
+
context "only first has captures" do
|
789
|
+
let(:first) { Mustermann.new(":a") }
|
790
|
+
its(:class) { should be == Mustermann::Sinatra }
|
791
|
+
its(:to_s) { should be == "{a}|b" }
|
792
|
+
end
|
793
|
+
|
794
|
+
context "only second has captures" do
|
795
|
+
let(:second) { Mustermann.new(":b") }
|
796
|
+
its(:class) { should be == Mustermann::Sinatra }
|
797
|
+
its(:to_s) { should be == "a|{b}" }
|
798
|
+
end
|
799
|
+
|
800
|
+
context "both have captures" do
|
801
|
+
let(:first) { Mustermann.new(":a") }
|
802
|
+
let(:second) { Mustermann.new(":b") }
|
803
|
+
its(:class) { should be == Mustermann::Composite }
|
804
|
+
end
|
805
|
+
|
806
|
+
context "options mismatch" do
|
807
|
+
let(:second) { Mustermann.new(":b", greedy: false) }
|
808
|
+
its(:class) { should be == Mustermann::Composite }
|
809
|
+
end
|
810
|
+
|
811
|
+
context "argument is a string" do
|
812
|
+
let(:second) { ":b" }
|
813
|
+
its(:class) { should be == Mustermann::Sinatra }
|
814
|
+
its(:to_s) { should be == "a|\\:b" }
|
815
|
+
end
|
816
|
+
|
817
|
+
context "argument is an identity pattern" do
|
818
|
+
let(:second) { Mustermann::Identity.new(":b") }
|
819
|
+
its(:class) { should be == Mustermann::Sinatra }
|
820
|
+
its(:to_s) { should be == "a|\\:b" }
|
821
|
+
end
|
822
|
+
|
823
|
+
context "argument is an identity pattern, but options mismatch" do
|
824
|
+
let(:second) { Mustermann::Identity.new(":b", uri_decode: false) }
|
825
|
+
its(:class) { should be == Mustermann::Composite }
|
826
|
+
end
|
827
|
+
end
|
748
828
|
end
|