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