mustermann 1.0.3 → 3.0.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 +5 -5
- data/README.md +1 -24
- data/lib/mustermann/ast/compiler.rb +1 -1
- data/lib/mustermann/ast/expander.rb +11 -8
- data/lib/mustermann/ast/node.rb +3 -3
- data/lib/mustermann/ast/parser.rb +3 -2
- data/lib/mustermann/ast/translator.rb +4 -3
- data/lib/mustermann/composite.rb +2 -2
- data/lib/mustermann/concat.rb +1 -1
- data/lib/mustermann/extension.rb +1 -48
- data/lib/mustermann/mapper.rb +1 -5
- data/lib/mustermann/pattern.rb +1 -1
- data/lib/mustermann/version.rb +1 -1
- data/lib/mustermann.rb +2 -3
- data/mustermann.gemspec +3 -2
- data/spec/expander_spec.rb +2 -0
- data/spec/mapper_spec.rb +2 -9
- data/spec/mustermann_spec.rb +2 -5
- metadata +22 -11
- data/spec/extension_spec.rb +0 -297
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ea330a8bf09165e676628df2668f0afa5d8fd32372bb7b07ed6a286cf274f74c
|
4
|
+
data.tar.gz: 58b0252ef3ba1d5bd06b3dceab50c06ba011d98b9e2bc54c6caa314443fa722b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f9e8638a1cb73cf39256c6cb422d4d1a4601a2d9348aba8361d31083103e0495a843d595b579f08d2614ba1b665b41959a79eaffd869348551a2cb558c1cf1ea
|
7
|
+
data.tar.gz: 5b569063e6dd7d3beb3bdd983d4f5c30a9d12ce7b5e06d0f1f215e56004ba678011df824d26a94d5dc136d957c7835e5bcc38081cbbda2d838cfe78b3ca00ec7
|
data/README.md
CHANGED
@@ -377,30 +377,7 @@ mapper['/foo/bar'] # => "/foo/bar"
|
|
377
377
|
<a name="-sinatra-integration"></a>
|
378
378
|
## Sinatra Integration
|
379
379
|
|
380
|
-
|
381
|
-
|
382
|
-
``` ruby
|
383
|
-
require 'sinatra'
|
384
|
-
require 'mustermann'
|
385
|
-
|
386
|
-
get Mustermann.new('/:foo') do
|
387
|
-
params[:foo]
|
388
|
-
end
|
389
|
-
```
|
390
|
-
|
391
|
-
In fact, since using this with Sinatra is the main use case, it comes with a build-in extension for **Sinatra 1.x**.
|
392
|
-
|
393
|
-
``` ruby
|
394
|
-
require 'sinatra'
|
395
|
-
require 'mustermann'
|
396
|
-
|
397
|
-
register Mustermann
|
398
|
-
|
399
|
-
# this will use Mustermann rather than the built-in pattern matching
|
400
|
-
get '/:slug(.ext)?' do
|
401
|
-
params[:slug]
|
402
|
-
end
|
403
|
-
```
|
380
|
+
Mustermann is used in Sinatra by default since version 2.0, for previous versions an [extension](https://github.com/sinatra/mustermann-sinatra-extension) is available.
|
404
381
|
|
405
382
|
### Configuration
|
406
383
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'mustermann/ast/translator'
|
3
3
|
require 'mustermann/ast/compiler'
|
4
|
+
require 'ruby2_keywords'
|
4
5
|
|
5
6
|
module Mustermann
|
6
7
|
module AST
|
@@ -11,11 +12,11 @@ module Mustermann
|
|
11
12
|
class Expander < Translator
|
12
13
|
raises ExpandError
|
13
14
|
|
14
|
-
translate
|
15
|
+
translate(Array, &-> (*args) do
|
15
16
|
inject(t.pattern) do |pattern, element|
|
16
17
|
t.add_to(pattern, t(element, *args))
|
17
18
|
end
|
18
|
-
end
|
19
|
+
end.ruby2_keywords)
|
19
20
|
|
20
21
|
translate :capture do |**options|
|
21
22
|
t.for_capture(node, **options)
|
@@ -92,11 +93,11 @@ module Mustermann
|
|
92
93
|
# @see Mustermann::Pattern#expand
|
93
94
|
# @!visibility private
|
94
95
|
def expand(values)
|
95
|
-
|
96
|
+
adjusted = values.each_with_object({}){ |(key, value), new_hash|
|
96
97
|
new_hash[value.instance_of?(Array) ? [key] * value.length : key] = value }
|
97
|
-
keys, pattern, filters = mappings.fetch(
|
98
|
-
filters.each { |key, filter|
|
99
|
-
pattern % (
|
98
|
+
keys, pattern, filters = mappings.fetch(adjusted.keys.flatten.sort) { error_for(values) }
|
99
|
+
filters.each { |key, filter| adjusted[key] &&= escape(adjusted[key], also_escape: filter) }
|
100
|
+
pattern % (adjusted[keys] || adjusted.values_at(*keys))
|
100
101
|
end
|
101
102
|
|
102
103
|
# @see Mustermann::Pattern#expandable?
|
@@ -122,7 +123,9 @@ module Mustermann
|
|
122
123
|
|
123
124
|
# @see Mustermann::AST::Translator#expand
|
124
125
|
# @!visibility private
|
125
|
-
def escape(string, *args)
|
126
|
+
ruby2_keywords def escape(string, *args)
|
127
|
+
return super unless string.respond_to?(:=~)
|
128
|
+
|
126
129
|
# URI::Parser is pretty slow, let's not send every string to it, even if it's unnecessary
|
127
130
|
string =~ /\A\w*\Z/ ? string : super
|
128
131
|
end
|
@@ -137,7 +140,7 @@ module Mustermann
|
|
137
140
|
# @!visibility private
|
138
141
|
def add_to(list, result)
|
139
142
|
list << [[], ""] if list.empty?
|
140
|
-
list.inject([]) { |l, (k1, p1, f1)| l + result.map { |k2, p2, f2| [k1+k2, p1+p2,
|
143
|
+
list.inject([]) { |l, (k1, p1, f1)| l + result.map { |k2, p2, f2| [k1+k2, p1+p2, f1.merge(f2)] } }
|
141
144
|
end
|
142
145
|
end
|
143
146
|
end
|
data/lib/mustermann/ast/node.rb
CHANGED
@@ -35,8 +35,8 @@ module Mustermann
|
|
35
35
|
# Helper for creating a new instance and calling #parse on it.
|
36
36
|
# @return [Mustermann::AST::Node]
|
37
37
|
# @!visibility private
|
38
|
-
def self.parse(
|
39
|
-
new(
|
38
|
+
def self.parse(payload = nil, **options, &block)
|
39
|
+
new(payload, **options).tap { |n| n.parse(&block) }
|
40
40
|
end
|
41
41
|
|
42
42
|
# @!visibility private
|
@@ -108,7 +108,7 @@ module Mustermann
|
|
108
108
|
# @see Mustermann::AST::Node#parse
|
109
109
|
# @!visibility private
|
110
110
|
def parse
|
111
|
-
self.payload ||=
|
111
|
+
self.payload ||= String.new
|
112
112
|
super
|
113
113
|
end
|
114
114
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'mustermann/ast/node'
|
3
3
|
require 'forwardable'
|
4
|
+
require 'ruby2_keywords'
|
4
5
|
require 'strscan'
|
5
6
|
|
6
7
|
module Mustermann
|
@@ -64,7 +65,7 @@ module Mustermann
|
|
64
65
|
# @param [Symbol] type node type
|
65
66
|
# @return [Mustermann::AST::Node]
|
66
67
|
# @!visibility private
|
67
|
-
def node(type, *args, &block)
|
68
|
+
ruby2_keywords def node(type, *args, &block)
|
68
69
|
type = Node[type] unless type.respond_to? :new
|
69
70
|
start = pos
|
70
71
|
node = block ? type.parse(*args, &block) : type.new(*args)
|
@@ -153,7 +154,7 @@ module Mustermann
|
|
153
154
|
def read_brackets(open, close, char: nil, escape: ?\\, quote: false, **options)
|
154
155
|
result = String.new
|
155
156
|
escape = false if escape.nil?
|
156
|
-
while current = getch
|
157
|
+
while (current = getch)
|
157
158
|
case current
|
158
159
|
when close then return result
|
159
160
|
when open then result << open << read_brackets(open, close) << close
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'mustermann/ast/node'
|
3
3
|
require 'mustermann/error'
|
4
|
+
require 'ruby2_keywords'
|
4
5
|
require 'delegate'
|
5
6
|
|
6
7
|
module Mustermann
|
@@ -36,7 +37,7 @@ module Mustermann
|
|
36
37
|
|
37
38
|
# shorthand for translating a nested object
|
38
39
|
# @!visibility private
|
39
|
-
def t(*args, &block)
|
40
|
+
ruby2_keywords def t(*args, &block)
|
40
41
|
return translator unless args.any?
|
41
42
|
translator.translate(*args, &block)
|
42
43
|
end
|
@@ -109,7 +110,7 @@ module Mustermann
|
|
109
110
|
|
110
111
|
# Start the translation dance for a (sub)tree.
|
111
112
|
# @!visibility private
|
112
|
-
def translate(node, *args, &block)
|
113
|
+
ruby2_keywords def translate(node, *args, &block)
|
113
114
|
result = decorator_for(node).translate(*args, &block)
|
114
115
|
result = result.node while result.is_a? NodeTranslator
|
115
116
|
result
|
@@ -119,7 +120,7 @@ module Mustermann
|
|
119
120
|
# @!visibility private
|
120
121
|
def escape(char, parser: URI::DEFAULT_PARSER, escape: parser.regexp[:UNSAFE], also_escape: nil)
|
121
122
|
escape = Regexp.union(also_escape, escape) if also_escape
|
122
|
-
char =~ escape ? parser.escape(char, Regexp.union(*escape)) : char
|
123
|
+
char.to_s =~ escape ? parser.escape(char, Regexp.union(*escape)) : char
|
123
124
|
end
|
124
125
|
end
|
125
126
|
end
|
data/lib/mustermann/composite.rb
CHANGED
@@ -102,9 +102,9 @@ module Mustermann
|
|
102
102
|
end
|
103
103
|
|
104
104
|
# @!visibility private
|
105
|
-
def patterns_from(pattern, options
|
105
|
+
def patterns_from(pattern, **options)
|
106
106
|
return pattern.patterns if pattern.is_a? Composite and pattern.operator == self.operator
|
107
|
-
[options ? Mustermann.new(pattern, **options)
|
107
|
+
[options.empty? && pattern.is_a?(Pattern) ? pattern : Mustermann.new(pattern, **options)]
|
108
108
|
end
|
109
109
|
|
110
110
|
private :with_matching, :patterns_from
|
data/lib/mustermann/concat.rb
CHANGED
data/lib/mustermann/extension.rb
CHANGED
@@ -1,50 +1,3 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
require 'sinatra/version'
|
3
|
-
fail "no need to load the Mustermann extension for #{::Sinatra::VERSION}" if ::Sinatra::VERSION >= '2.0.0'
|
4
2
|
|
5
|
-
|
6
|
-
|
7
|
-
module Mustermann
|
8
|
-
# Sinatra 1.x extension switching default pattern parsing over to Mustermann.
|
9
|
-
#
|
10
|
-
# @example With classic Sinatra application
|
11
|
-
# require 'sinatra'
|
12
|
-
# require 'mustermann'
|
13
|
-
#
|
14
|
-
# register Mustermann
|
15
|
-
# get('/:id', capture: /\d+/) { ... }
|
16
|
-
#
|
17
|
-
# @example With modular Sinatra application
|
18
|
-
# require 'sinatra/base'
|
19
|
-
# require 'mustermann'
|
20
|
-
#
|
21
|
-
# class MyApp < Sinatra::Base
|
22
|
-
# register Mustermann
|
23
|
-
# get('/:id', capture: /\d+/) { ... }
|
24
|
-
# end
|
25
|
-
#
|
26
|
-
# @see file:README.md#Sinatra_Integration "Sinatra Integration" in the README
|
27
|
-
module Extension
|
28
|
-
def compile!(verb, path, block, except: nil, capture: nil, pattern: { }, **options)
|
29
|
-
if path.respond_to? :to_str
|
30
|
-
pattern[:except] = except if except
|
31
|
-
pattern[:capture] = capture if capture
|
32
|
-
|
33
|
-
if settings.respond_to? :pattern and settings.pattern?
|
34
|
-
pattern.merge! settings.pattern do |key, local, global|
|
35
|
-
next local unless local.is_a? Hash
|
36
|
-
next global.merge(local) if global.is_a? Hash
|
37
|
-
Hash.new(global).merge! local
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
path = Mustermann.new(path, **pattern)
|
42
|
-
condition { params.merge! path.params(captures: Array(params[:captures]), offset: -1) }
|
43
|
-
end
|
44
|
-
|
45
|
-
super(verb, path, block, options)
|
46
|
-
end
|
47
|
-
|
48
|
-
private :compile!
|
49
|
-
end
|
50
|
-
end
|
3
|
+
fail "Mustermann extension for Sinatra has been extracted into its own gem. More information at https://github.com/sinatra/mustermann-sinatra-extension"
|
data/lib/mustermann/mapper.rb
CHANGED
@@ -40,11 +40,7 @@ module Mustermann
|
|
40
40
|
#
|
41
41
|
# @example map before options
|
42
42
|
# require 'mustermann/mapper'
|
43
|
-
# Mustermann::Mapper.new("/:foo" => "/:foo.html", type: :rails)
|
44
|
-
#
|
45
|
-
# @example map after options
|
46
|
-
# require 'mustermann/mapper'
|
47
|
-
# Mustermann::Mapper.new(type: :rails, "/:foo" => "/:foo.html")
|
43
|
+
# Mustermann::Mapper.new({"/:foo" => "/:foo.html"}, type: :rails)
|
48
44
|
def initialize(map = {}, additional_values: :ignore, **options, &block)
|
49
45
|
@map = []
|
50
46
|
@options = options
|
data/lib/mustermann/pattern.rb
CHANGED
@@ -56,7 +56,7 @@ module Mustermann
|
|
56
56
|
end
|
57
57
|
|
58
58
|
@map ||= EqualityMap.new
|
59
|
-
@map.fetch([string, options]) { super(string, options) { options } }
|
59
|
+
@map.fetch([string, options]) { super(string, **options) { options } }
|
60
60
|
end
|
61
61
|
|
62
62
|
supported_options :uri_decode, :ignore_unknown_options
|
data/lib/mustermann/version.rb
CHANGED
data/lib/mustermann.rb
CHANGED
@@ -75,8 +75,8 @@ module Mustermann
|
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
|
-
@mutex
|
79
|
-
@types
|
78
|
+
@mutex = Mutex.new
|
79
|
+
@types = {}
|
80
80
|
|
81
81
|
# Maps a type to its factory.
|
82
82
|
#
|
@@ -120,7 +120,6 @@ module Mustermann
|
|
120
120
|
def self.extend_object(object)
|
121
121
|
return super unless defined? ::Sinatra::Base and object.is_a? Class and object < ::Sinatra::Base
|
122
122
|
require 'mustermann/extension'
|
123
|
-
object.register Extension
|
124
123
|
end
|
125
124
|
end
|
126
125
|
|
data/mustermann.gemspec
CHANGED
@@ -10,8 +10,9 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.summary = %q{Your personal string matching expert.}
|
11
11
|
s.description = %q{A 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.6.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
|
+
|
17
|
+
s.add_runtime_dependency('ruby2_keywords', '~> 0.0.1')
|
17
18
|
end
|
data/spec/expander_spec.rb
CHANGED
@@ -64,6 +64,8 @@ describe Mustermann::Expander do
|
|
64
64
|
example { expander.expand(a: ?a).should be == '/a' }
|
65
65
|
example { expander.expand(a: ?a, b: ?b).should be == '/a' }
|
66
66
|
example { expect { expander.expand(b: ?b) }.to raise_error(Mustermann::ExpandError) }
|
67
|
+
example { expect { expander.expand(b: ?b, c: []) }.to raise_error(Mustermann::ExpandError) }
|
68
|
+
example { expect { expander.expand(b: ?b, c: [], d: ?d) }.to raise_error(Mustermann::ExpandError) }
|
67
69
|
end
|
68
70
|
|
69
71
|
context :append do
|
data/spec/mapper_spec.rb
CHANGED
@@ -19,21 +19,14 @@ describe Mustermann::Mapper do
|
|
19
19
|
end
|
20
20
|
|
21
21
|
context 'accepts mappings followed by options' do
|
22
|
-
subject(:mapper) { Mustermann::Mapper.new("/foo" => "/bar", additional_values: :raise) }
|
23
|
-
its(:to_h) { should be == { Mustermann.new("/foo") => Mustermann::Expander.new("/bar") } }
|
24
|
-
example { mapper['/foo'].should be == '/bar' }
|
25
|
-
example { mapper['/fox'].should be == '/fox' }
|
26
|
-
end
|
27
|
-
|
28
|
-
context 'accepts options followed by mappings' do
|
29
|
-
subject(:mapper) { Mustermann::Mapper.new(additional_values: :raise, "/foo" => "/bar") }
|
22
|
+
subject(:mapper) { Mustermann::Mapper.new({ "/foo" => "/bar" }, additional_values: :raise) }
|
30
23
|
its(:to_h) { should be == { Mustermann.new("/foo") => Mustermann::Expander.new("/bar") } }
|
31
24
|
example { mapper['/foo'].should be == '/bar' }
|
32
25
|
example { mapper['/fox'].should be == '/fox' }
|
33
26
|
end
|
34
27
|
|
35
28
|
context 'allows specifying type' do
|
36
|
-
subject(:mapper) { Mustermann::Mapper.new(additional_values: :raise, type: :rails
|
29
|
+
subject(:mapper) { Mustermann::Mapper.new({ "/foo" => "/bar" }, additional_values: :raise, type: :rails) }
|
37
30
|
its(:to_h) { should be == { Mustermann.new("/foo", type: :rails) => Mustermann::Expander.new("/bar", type: :rails) } }
|
38
31
|
example { mapper['/foo'].should be == '/bar' }
|
39
32
|
example { mapper['/fox'].should be == '/fox' }
|
data/spec/mustermann_spec.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
require 'support'
|
3
3
|
require 'mustermann'
|
4
|
-
require 'mustermann/extension'
|
5
4
|
require 'sinatra/base'
|
6
5
|
|
7
6
|
describe Mustermann do
|
@@ -70,11 +69,9 @@ describe Mustermann do
|
|
70
69
|
describe :extend_object do
|
71
70
|
context 'special behavior for Sinatra only' do
|
72
71
|
example { Object .new.extend(Mustermann).should be_a(Mustermann) }
|
73
|
-
example { Object .new.extend(Mustermann).should_not be_a(Mustermann::Extension) }
|
74
72
|
example { Class .new.extend(Mustermann).should be_a(Mustermann) }
|
75
|
-
|
76
|
-
example { Sinatra
|
77
|
-
example { Sinatra .new.extend(Mustermann).should be_a(Mustermann::Extension) }
|
73
|
+
|
74
|
+
example { expect { Sinatra.new.extend(Mustermann) }.to raise_error(RuntimeError, "Mustermann extension for Sinatra has been extracted into its own gem. More information at https://github.com/sinatra/mustermann-sinatra-extension") }
|
78
75
|
end
|
79
76
|
end
|
80
77
|
|
metadata
CHANGED
@@ -1,16 +1,30 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mustermann
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Konstantin Haase
|
8
8
|
- Zachary Scott
|
9
|
-
autorequire:
|
9
|
+
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
13
|
-
dependencies:
|
12
|
+
date: 2022-07-24 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: ruby2_keywords
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 0.0.1
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 0.0.1
|
14
28
|
description: A library implementing patterns that behave like regular expressions.
|
15
29
|
email: sinatrarb@googlegroups.com
|
16
30
|
executables: []
|
@@ -62,7 +76,6 @@ files:
|
|
62
76
|
- spec/concat_spec.rb
|
63
77
|
- spec/equality_map_spec.rb
|
64
78
|
- spec/expander_spec.rb
|
65
|
-
- spec/extension_spec.rb
|
66
79
|
- spec/identity_spec.rb
|
67
80
|
- spec/mapper_spec.rb
|
68
81
|
- spec/mustermann_spec.rb
|
@@ -76,7 +89,7 @@ homepage: https://github.com/sinatra/mustermann
|
|
76
89
|
licenses:
|
77
90
|
- MIT
|
78
91
|
metadata: {}
|
79
|
-
post_install_message:
|
92
|
+
post_install_message:
|
80
93
|
rdoc_options: []
|
81
94
|
require_paths:
|
82
95
|
- lib
|
@@ -84,16 +97,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
84
97
|
requirements:
|
85
98
|
- - ">="
|
86
99
|
- !ruby/object:Gem::Version
|
87
|
-
version: 2.
|
100
|
+
version: 2.6.0
|
88
101
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
102
|
requirements:
|
90
103
|
- - ">="
|
91
104
|
- !ruby/object:Gem::Version
|
92
105
|
version: '0'
|
93
106
|
requirements: []
|
94
|
-
|
95
|
-
|
96
|
-
signing_key:
|
107
|
+
rubygems_version: 3.0.3.1
|
108
|
+
signing_key:
|
97
109
|
specification_version: 4
|
98
110
|
summary: Your personal string matching expert.
|
99
111
|
test_files:
|
@@ -102,7 +114,6 @@ test_files:
|
|
102
114
|
- spec/concat_spec.rb
|
103
115
|
- spec/equality_map_spec.rb
|
104
116
|
- spec/expander_spec.rb
|
105
|
-
- spec/extension_spec.rb
|
106
117
|
- spec/identity_spec.rb
|
107
118
|
- spec/mapper_spec.rb
|
108
119
|
- spec/mustermann_spec.rb
|
data/spec/extension_spec.rb
DELETED
@@ -1,297 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
require 'support'
|
3
|
-
require 'mustermann/extension'
|
4
|
-
require 'sinatra/base'
|
5
|
-
require 'rack/test'
|
6
|
-
|
7
|
-
describe Mustermann::Extension do
|
8
|
-
include Rack::Test::Methods
|
9
|
-
|
10
|
-
subject :app do
|
11
|
-
Sinatra.new do
|
12
|
-
set :environment, :test
|
13
|
-
register Mustermann
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'sets up the extension' do
|
18
|
-
app.should be_a(Mustermann::Extension)
|
19
|
-
end
|
20
|
-
|
21
|
-
context 'uses Sinatra-style patterns by default' do
|
22
|
-
before { app.get('/:slug(.:extension)?') { params[:slug] } }
|
23
|
-
example { get('/foo') .body.should be == 'foo' }
|
24
|
-
example { get('/foo.') .body.should be == 'foo.' }
|
25
|
-
example { get('/foo.bar') .body.should be == 'foo' }
|
26
|
-
example { get('/a%20b') .body.should be == 'a b' }
|
27
|
-
end
|
28
|
-
|
29
|
-
describe :except do
|
30
|
-
before { app.get('/auth/*', except: '/auth/login') { 'ok' } }
|
31
|
-
example { get('/auth/dunno').should be_ok }
|
32
|
-
example { get('/auth/login').should_not be_ok }
|
33
|
-
end
|
34
|
-
|
35
|
-
describe :capture do
|
36
|
-
context 'global' do
|
37
|
-
before do
|
38
|
-
app.set(:pattern, capture: { ext: %w[png jpg gif] })
|
39
|
-
app.get('/:slug(.:ext)?') { params[:slug] }
|
40
|
-
end
|
41
|
-
|
42
|
-
example { get('/foo.bar').body.should be == 'foo.bar' }
|
43
|
-
example { get('/foo.png').body.should be == 'foo' }
|
44
|
-
end
|
45
|
-
|
46
|
-
context 'route local' do
|
47
|
-
before do
|
48
|
-
app.set(:pattern, nil)
|
49
|
-
app.get('/:id', capture: /\d+/) { 'ok' }
|
50
|
-
end
|
51
|
-
|
52
|
-
example { get('/42').should be_ok }
|
53
|
-
example { get('/foo').should_not be_ok }
|
54
|
-
end
|
55
|
-
|
56
|
-
context 'global and route local' do
|
57
|
-
context 'global is a hash' do
|
58
|
-
before do
|
59
|
-
app.set(:pattern, capture: { id: /\d+/ })
|
60
|
-
app.get('/:id(.:ext)?', capture: { ext: 'png' }) { ?a }
|
61
|
-
app.get('/:id', capture: { id: 'foo' }) { ?b }
|
62
|
-
app.get('/:id', capture: :alpha) { ?c }
|
63
|
-
end
|
64
|
-
|
65
|
-
example { get('/20') .body.should be == ?a }
|
66
|
-
example { get('/20.png') .body.should be == ?a }
|
67
|
-
example { get('/foo') .body.should be == ?b }
|
68
|
-
example { get('/bar') .body.should be == ?c }
|
69
|
-
end
|
70
|
-
|
71
|
-
context 'global is not a hash' do
|
72
|
-
before do
|
73
|
-
app.set(:pattern, capture: /\d+/)
|
74
|
-
app.get('/:slug(.:ext)?', capture: { ext: 'png' }) { params[:slug] }
|
75
|
-
app.get('/:slug', capture: :alpha) { 'ok' }
|
76
|
-
end
|
77
|
-
|
78
|
-
example { get('/20.png').should be_ok }
|
79
|
-
example { get('/foo.png').should_not be_ok }
|
80
|
-
example { get('/foo').should be_ok }
|
81
|
-
|
82
|
-
example { get('/20.png') .body.should be == '20' }
|
83
|
-
example { get('/42') .body.should be == '42' }
|
84
|
-
example { get('/foo') .body.should be == 'ok' }
|
85
|
-
end
|
86
|
-
end
|
87
|
-
end
|
88
|
-
|
89
|
-
describe :pattern do
|
90
|
-
describe :except do
|
91
|
-
before { app.get('/auth/*', pattern: { except: '/auth/login' }) { 'ok' } }
|
92
|
-
example { get('/auth/dunno').should be_ok }
|
93
|
-
example { get('/auth/login').should_not be_ok }
|
94
|
-
end
|
95
|
-
|
96
|
-
describe :capture do
|
97
|
-
context 'route local' do
|
98
|
-
before do
|
99
|
-
app.set(:pattern, nil)
|
100
|
-
app.get('/:id', pattern: { capture: /\d+/ }) { 'ok' }
|
101
|
-
end
|
102
|
-
|
103
|
-
example { get('/42').should be_ok }
|
104
|
-
example { get('/foo').should_not be_ok }
|
105
|
-
end
|
106
|
-
|
107
|
-
context 'global and route local' do
|
108
|
-
context 'global is a hash' do
|
109
|
-
before do
|
110
|
-
app.set(:pattern, capture: { id: /\d+/ })
|
111
|
-
app.get('/:id(.:ext)?', pattern: { capture: { ext: 'png' }}) { ?a }
|
112
|
-
app.get('/:id', pattern: { capture: { id: 'foo' }}) { ?b }
|
113
|
-
app.get('/:id', pattern: { capture: :alpha }) { ?c }
|
114
|
-
end
|
115
|
-
|
116
|
-
example { get('/20') .body.should be == ?a }
|
117
|
-
example { get('/20.png') .body.should be == ?a }
|
118
|
-
example { get('/foo') .body.should be == ?b }
|
119
|
-
example { get('/bar') .body.should be == ?c }
|
120
|
-
end
|
121
|
-
|
122
|
-
context 'global is not a hash' do
|
123
|
-
before do
|
124
|
-
app.set(:pattern, capture: /\d+/)
|
125
|
-
app.get('/:slug(.:ext)?', pattern: { capture: { ext: 'png' }}) { params[:slug] }
|
126
|
-
app.get('/:slug', pattern: { capture: :alpha }) { 'ok' }
|
127
|
-
end
|
128
|
-
|
129
|
-
example { get('/20.png').should be_ok }
|
130
|
-
example { get('/foo.png').should_not be_ok }
|
131
|
-
example { get('/foo').should be_ok }
|
132
|
-
|
133
|
-
example { get('/20.png') .body.should be == '20' }
|
134
|
-
example { get('/42') .body.should be == '42' }
|
135
|
-
example { get('/foo') .body.should be == 'ok' }
|
136
|
-
end
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
describe :greedy do
|
141
|
-
context 'default' do
|
142
|
-
before { app.get('/:name.:ext') { params[:name] }}
|
143
|
-
example { get('/foo.bar') .body.should be == 'foo' }
|
144
|
-
example { get('/foo.bar.baz') .body.should be == 'foo.bar' }
|
145
|
-
end
|
146
|
-
|
147
|
-
context 'enabled' do
|
148
|
-
before { app.get('/:name.:ext', pattern: { greedy: true }) { params[:name] }}
|
149
|
-
example { get('/foo.bar') .body.should be == 'foo' }
|
150
|
-
example { get('/foo.bar.baz') .body.should be == 'foo.bar' }
|
151
|
-
end
|
152
|
-
|
153
|
-
context 'disabled' do
|
154
|
-
before { app.get('/:name.:ext', pattern: { greedy: false }) { params[:name] }}
|
155
|
-
example { get('/foo.bar') .body.should be == 'foo' }
|
156
|
-
example { get('/foo.bar.baz') .body.should be == 'foo' }
|
157
|
-
end
|
158
|
-
|
159
|
-
context 'global' do
|
160
|
-
before do
|
161
|
-
app.set(:pattern, greedy: false)
|
162
|
-
app.get('/:name.:ext') { params[:name] }
|
163
|
-
end
|
164
|
-
|
165
|
-
example { get('/foo.bar') .body.should be == 'foo' }
|
166
|
-
example { get('/foo.bar.baz') .body.should be == 'foo' }
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
describe :space_matches_plus do
|
171
|
-
context 'default' do
|
172
|
-
before { app.get('/foo bar') { 'ok' }}
|
173
|
-
example { get('/foo%20bar') .should be_ok }
|
174
|
-
example { get('/foo+bar') .should be_ok }
|
175
|
-
end
|
176
|
-
|
177
|
-
context 'enabled' do
|
178
|
-
before { app.get('/foo bar', pattern: { space_matches_plus: true }) { 'ok' }}
|
179
|
-
example { get('/foo%20bar') .should be_ok }
|
180
|
-
example { get('/foo+bar') .should be_ok }
|
181
|
-
end
|
182
|
-
|
183
|
-
context 'disabled' do
|
184
|
-
before { app.get('/foo bar', pattern: { space_matches_plus: false }) { 'ok' }}
|
185
|
-
example { get('/foo%20bar') .should be_ok }
|
186
|
-
example { get('/foo+bar') .should_not be_ok }
|
187
|
-
end
|
188
|
-
|
189
|
-
context 'global' do
|
190
|
-
before do
|
191
|
-
app.set(:pattern, space_matches_plus: false)
|
192
|
-
app.get('/foo bar') { 'ok' }
|
193
|
-
end
|
194
|
-
|
195
|
-
example { get('/foo%20bar') .should be_ok }
|
196
|
-
example { get('/foo+bar') .should_not be_ok }
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
describe :uri_decode do
|
201
|
-
context 'default' do
|
202
|
-
before { app.get('/&') { 'ok' }}
|
203
|
-
example { get('/&') .should be_ok }
|
204
|
-
example { get('/%26') .should be_ok }
|
205
|
-
end
|
206
|
-
|
207
|
-
context 'enabled' do
|
208
|
-
before { app.get('/&', pattern: { uri_decode: true }) { 'ok' }}
|
209
|
-
example { get('/&') .should be_ok }
|
210
|
-
example { get('/%26') .should be_ok }
|
211
|
-
end
|
212
|
-
|
213
|
-
context 'disabled' do
|
214
|
-
before { app.get('/&', pattern: { uri_decode: false }) { 'ok' }}
|
215
|
-
example { get('/&') .should be_ok }
|
216
|
-
example { get('/%26') .should_not be_ok }
|
217
|
-
end
|
218
|
-
|
219
|
-
context 'global' do
|
220
|
-
before do
|
221
|
-
app.set(:pattern, uri_decode: false)
|
222
|
-
app.get('/&') { 'ok' }
|
223
|
-
end
|
224
|
-
|
225
|
-
example { get('/&') .should be_ok }
|
226
|
-
example { get('/%26') .should_not be_ok }
|
227
|
-
end
|
228
|
-
end
|
229
|
-
end
|
230
|
-
|
231
|
-
describe :type do
|
232
|
-
describe :identity do
|
233
|
-
before do
|
234
|
-
app.set(:pattern, type: :identity)
|
235
|
-
app.get('/:foo') { 'ok' }
|
236
|
-
end
|
237
|
-
|
238
|
-
example { get('/:foo').should be_ok }
|
239
|
-
example { get('/foo').should_not be_ok }
|
240
|
-
end
|
241
|
-
|
242
|
-
describe :rails do
|
243
|
-
before do
|
244
|
-
app.set(:pattern, type: :rails)
|
245
|
-
app.get('/:slug(.:extension)') { params[:slug] }
|
246
|
-
end
|
247
|
-
|
248
|
-
example { get('/foo') .body.should be == 'foo' }
|
249
|
-
example { get('/foo.') .body.should be == 'foo.' }
|
250
|
-
example { get('/foo.bar') .body.should be == 'foo' }
|
251
|
-
example { get('/a%20b') .body.should be == 'a b' }
|
252
|
-
end
|
253
|
-
|
254
|
-
describe :shell do
|
255
|
-
before do
|
256
|
-
app.set(:pattern, type: :shell)
|
257
|
-
app.get('/{foo,bar}') { 'ok' }
|
258
|
-
end
|
259
|
-
|
260
|
-
example { get('/foo').should be_ok }
|
261
|
-
example { get('/bar').should be_ok }
|
262
|
-
end
|
263
|
-
|
264
|
-
describe :simple do
|
265
|
-
before do
|
266
|
-
app.set(:pattern, type: :simple)
|
267
|
-
app.get('/(a)') { 'ok' }
|
268
|
-
end
|
269
|
-
|
270
|
-
example { get('/(a)').should be_ok }
|
271
|
-
example { get('/a').should_not be_ok }
|
272
|
-
end
|
273
|
-
|
274
|
-
describe :simple do
|
275
|
-
before do
|
276
|
-
app.set(:pattern, type: :template)
|
277
|
-
app.get('/foo{/segments*}{.ext}') { "%p %p" % [params[:segments], params[:ext]] }
|
278
|
-
end
|
279
|
-
|
280
|
-
example { get('/foo/a.png').should be_ok }
|
281
|
-
example { get('/foo/a').should_not be_ok }
|
282
|
-
|
283
|
-
example { get('/foo/a.png').body.should be == '["a"] "png"' }
|
284
|
-
example { get('/foo/a/b.png').body.should be == '["a", "b"] "png"' }
|
285
|
-
end
|
286
|
-
end
|
287
|
-
|
288
|
-
context 'works with filters' do
|
289
|
-
before do
|
290
|
-
app.before('/auth/*', except: '/auth/login') { halt 'auth required' }
|
291
|
-
app.get('/auth/login') { 'please log in' }
|
292
|
-
end
|
293
|
-
|
294
|
-
example { get('/auth/dunno').body.should be == 'auth required' }
|
295
|
-
example { get('/auth/login').body.should be == 'please log in' }
|
296
|
-
end
|
297
|
-
end
|