mustermann 0.0.1 → 0.1.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/.gitignore +2 -1
- data/.travis.yml +4 -3
- data/.yardopts +1 -0
- data/README.md +53 -10
- data/Rakefile +4 -1
- data/bench/capturing.rb +42 -9
- data/bench/template_vs_addressable.rb +3 -0
- data/internals.md +64 -0
- data/lib/mustermann.rb +14 -5
- data/lib/mustermann/ast/compiler.rb +150 -0
- data/lib/mustermann/ast/expander.rb +112 -0
- data/lib/mustermann/ast/node.rb +155 -0
- data/lib/mustermann/ast/parser.rb +136 -0
- data/lib/mustermann/ast/pattern.rb +89 -0
- data/lib/mustermann/ast/transformer.rb +121 -0
- data/lib/mustermann/ast/translator.rb +111 -0
- data/lib/mustermann/ast/tree_renderer.rb +29 -0
- data/lib/mustermann/ast/validation.rb +40 -0
- data/lib/mustermann/error.rb +4 -12
- data/lib/mustermann/extension.rb +3 -6
- data/lib/mustermann/identity.rb +4 -4
- data/lib/mustermann/pattern.rb +34 -5
- data/lib/mustermann/rails.rb +7 -16
- data/lib/mustermann/regexp_based.rb +4 -4
- data/lib/mustermann/shell.rb +4 -4
- data/lib/mustermann/simple.rb +1 -1
- data/lib/mustermann/simple_match.rb +2 -2
- data/lib/mustermann/sinatra.rb +10 -20
- data/lib/mustermann/template.rb +11 -104
- data/lib/mustermann/version.rb +1 -1
- data/mustermann.gemspec +1 -1
- data/spec/extension_spec.rb +143 -0
- data/spec/mustermann_spec.rb +41 -0
- data/spec/pattern_spec.rb +16 -6
- data/spec/rails_spec.rb +77 -9
- data/spec/sinatra_spec.rb +6 -0
- data/spec/support.rb +5 -78
- data/spec/support/coverage.rb +18 -0
- data/spec/support/env.rb +6 -0
- data/spec/support/expand_matcher.rb +27 -0
- data/spec/support/match_matcher.rb +39 -0
- data/spec/support/pattern.rb +28 -0
- metadata +43 -43
- data/.test_queue_stats +0 -0
- data/lib/mustermann/ast.rb +0 -403
- data/spec/ast_spec.rb +0 -8
@@ -3,16 +3,16 @@ require 'forwardable'
|
|
3
3
|
|
4
4
|
module Mustermann
|
5
5
|
# Superclass for patterns that internally compile to a regular expression.
|
6
|
-
# @see Pattern
|
6
|
+
# @see Mustermann::Pattern
|
7
7
|
# @abstract
|
8
8
|
class RegexpBased < Pattern
|
9
9
|
# @return [Regexp] regular expression equivalent to the pattern.
|
10
10
|
attr_reader :regexp
|
11
11
|
alias_method :to_regexp, :regexp
|
12
12
|
|
13
|
-
# @param (see Pattern#initialize)
|
14
|
-
# @return (see Pattern#initialize)
|
15
|
-
# @see (see Pattern#initialize)
|
13
|
+
# @param (see Mustermann::Pattern#initialize)
|
14
|
+
# @return (see Mustermann::Pattern#initialize)
|
15
|
+
# @see (see Mustermann::Pattern#initialize)
|
16
16
|
def initialize(string, **options)
|
17
17
|
@regexp = compile(string, **options)
|
18
18
|
super
|
data/lib/mustermann/shell.rb
CHANGED
@@ -7,14 +7,14 @@ module Mustermann
|
|
7
7
|
# @example
|
8
8
|
# Mustermann.new('/*.*', type: :shell) === '/bar' # => false
|
9
9
|
#
|
10
|
-
# @see Pattern
|
10
|
+
# @see Mustermann::Pattern
|
11
11
|
# @see file:README.md#shell Syntax description in the README
|
12
12
|
class Shell < Pattern
|
13
13
|
FLAGS ||= File::FNM_PATHNAME | File::FNM_DOTMATCH | File::FNM_EXTGLOB
|
14
14
|
|
15
|
-
# @param (see Pattern#===)
|
16
|
-
# @return (see Pattern#===)
|
17
|
-
# @see (see Pattern#===)
|
15
|
+
# @param (see Mustermann::Pattern#===)
|
16
|
+
# @return (see Mustermann::Pattern#===)
|
17
|
+
# @see (see Mustermann::Pattern#===)
|
18
18
|
def ===(string)
|
19
19
|
File.fnmatch? @string, unescape(string), FLAGS
|
20
20
|
end
|
data/lib/mustermann/simple.rb
CHANGED
@@ -6,7 +6,7 @@ module Mustermann
|
|
6
6
|
# @example
|
7
7
|
# Mustermann.new('/:foo', type: :simple) === '/bar' # => true
|
8
8
|
#
|
9
|
-
# @see Pattern
|
9
|
+
# @see Mustermann::Pattern
|
10
10
|
# @see file:README.md#simple Syntax description in the README
|
11
11
|
class Simple < RegexpBased
|
12
12
|
supported_options :greedy, :space_matches_plus
|
@@ -12,12 +12,12 @@ module Mustermann
|
|
12
12
|
@string.dup
|
13
13
|
end
|
14
14
|
|
15
|
-
# @return [Array<String>] empty array for
|
15
|
+
# @return [Array<String>] empty array for imitating MatchData interface
|
16
16
|
def names
|
17
17
|
[]
|
18
18
|
end
|
19
19
|
|
20
|
-
# @return [Array<String>] empty array for
|
20
|
+
# @return [Array<String>] empty array for imitating MatchData interface
|
21
21
|
def captures
|
22
22
|
[]
|
23
23
|
end
|
data/lib/mustermann/sinatra.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'mustermann/ast'
|
1
|
+
require 'mustermann/ast/pattern'
|
2
2
|
|
3
3
|
module Mustermann
|
4
4
|
# Sinatra 2.0 style pattern implementation.
|
@@ -6,27 +6,17 @@ module Mustermann
|
|
6
6
|
# @example
|
7
7
|
# Mustermann.new('/:foo') === '/bar' # => true
|
8
8
|
#
|
9
|
-
# @see Pattern
|
9
|
+
# @see Mustermann::Pattern
|
10
10
|
# @see file:README.md#sinatra Syntax description in the README
|
11
|
-
class Sinatra < AST
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
when ?/ then Separator.new(char)
|
18
|
-
when ?( then Group.parse { parse_buffer(buffer) }
|
19
|
-
when ?: then Capture.parse { buffer.scan(/\w+/) }
|
20
|
-
when ?\\ then Char.new expect(buffer, /./)
|
21
|
-
else Char.new(char)
|
22
|
-
end
|
23
|
-
end
|
11
|
+
class Sinatra < AST::Pattern
|
12
|
+
on(nil, ??, ?)) { |c| unexpected(c) }
|
13
|
+
on(?*) { |c| node(:splat) }
|
14
|
+
on(?() { |c| node(:group) { read unless scan(?)) } }
|
15
|
+
on(?:) { |c| node(:capture) { scan(/\w+/) } }
|
16
|
+
on(?\\) { |c| node(:char, expect(/./)) }
|
24
17
|
|
25
|
-
|
26
|
-
|
27
|
-
Optional.new(element)
|
18
|
+
suffix ?? do |char, element|
|
19
|
+
node(:optional, element)
|
28
20
|
end
|
29
|
-
|
30
|
-
private :parse_element, :parse_suffix
|
31
21
|
end
|
32
22
|
end
|
data/lib/mustermann/template.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'mustermann/ast'
|
1
|
+
require 'mustermann/ast/pattern'
|
2
2
|
|
3
3
|
module Mustermann
|
4
4
|
# URI template pattern implementation.
|
@@ -6,115 +6,22 @@ module Mustermann
|
|
6
6
|
# @example
|
7
7
|
# Mustermann.new('/{foo}') === '/bar' # => true
|
8
8
|
#
|
9
|
-
# @see Pattern
|
9
|
+
# @see Mustermann::Pattern
|
10
10
|
# @see file:README.md#template Syntax description in the README
|
11
11
|
# @see http://tools.ietf.org/html/rfc6570 RFC 6570
|
12
|
-
class Template < AST
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
?/ => Operator.new(?/, false, ?/, false), ?; => Operator.new(?;, false, ?;, true),
|
18
|
-
?? => Operator.new(?&, false, ??, true), ?& => Operator.new(?&, false, ?&, true)
|
19
|
-
}
|
20
|
-
|
21
|
-
# AST node for template expressions.
|
22
|
-
# @!visibility private
|
23
|
-
class Expression < Group
|
24
|
-
# @!visibility private
|
25
|
-
attr_accessor :operator
|
26
|
-
|
27
|
-
# makes sure we have the proper surrounding characters for the operator
|
28
|
-
# @!visibility private
|
29
|
-
def transform
|
30
|
-
self.operator = OPERATORS.fetch(operator) { raise CompileError, "#{operator} operator not supported" }
|
31
|
-
new_payload = payload.inject { |list, element| Array(list) << separator << element }
|
32
|
-
@payload = Array(new_payload).map!(&:transform)
|
33
|
-
payload.unshift separator(operator.prefix) if operator.prefix
|
34
|
-
self
|
35
|
-
end
|
36
|
-
|
37
|
-
# @!visibility private
|
38
|
-
def compile(greedy: true, **options)
|
39
|
-
super(allow_reserved: operator.allow_reserved, greedy: greedy && !operator.allow_reserved,
|
40
|
-
parametric: operator.parametric, separator: operator.separator, **options)
|
41
|
-
end
|
42
|
-
|
43
|
-
# @!visibility private
|
44
|
-
def separator(char = operator.separator)
|
45
|
-
AST.const_get(:Separator).new(char) # uhm
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# AST node for template variables.
|
50
|
-
# @!visibility private
|
51
|
-
class Variable < Capture
|
52
|
-
# @!visibility private
|
53
|
-
attr_accessor :prefix, :explode
|
54
|
-
|
55
|
-
# @!visibility private
|
56
|
-
def compile(**options)
|
57
|
-
return super(**options) if explode or not options[:parametric]
|
58
|
-
parametric super(parametric: false, **options)
|
59
|
-
end
|
60
|
-
|
61
|
-
# @!visibility private
|
62
|
-
def pattern(parametric: false, **options)
|
63
|
-
register_param(parametric: parametric, **options)
|
64
|
-
pattern = super(**options)
|
65
|
-
pattern = parametric(pattern) if parametric
|
66
|
-
pattern = "#{pattern}(?:#{Regexp.escape(options.fetch(:separator))}#{pattern})*" if explode
|
67
|
-
pattern
|
68
|
-
end
|
69
|
-
|
70
|
-
# @!visibility private
|
71
|
-
def parametric(string)
|
72
|
-
"#{Regexp.escape(name)}(?:=#{string})?"
|
12
|
+
class Template < AST::Pattern
|
13
|
+
on ?{ do |char|
|
14
|
+
variable = proc do
|
15
|
+
match = expect(/(?<name>\w+)(?:\:(?<prefix>\d{1,4})|(?<explode>\*))?/)
|
16
|
+
node(:variable, match[:name], prefix: match[:prefix], explode: match[:explode])
|
73
17
|
end
|
74
18
|
|
75
|
-
# @!visibility private
|
76
|
-
def qualified(string, **options)
|
77
|
-
prefix ? "#{string}{1,#{prefix}}" : super(string, **options)
|
78
|
-
end
|
79
|
-
|
80
|
-
# @!visibility private
|
81
|
-
def default(allow_reserved: false, **options)
|
82
|
-
allow_reserved ? '[\w\-\.~%\:/\?#\[\]@\!\$\&\'\(\)\*\+,;=]' : '[\w\-\.~%]'
|
83
|
-
end
|
84
|
-
|
85
|
-
# @!visibility private
|
86
|
-
def register_param(parametric: false, split_params: nil, separator: nil, **options)
|
87
|
-
return unless explode and split_params
|
88
|
-
split_params[name] = { separator: separator, parametric: parametric }
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
# @!visibility private
|
93
|
-
def parse_element(buffer)
|
94
|
-
parse_expression(buffer) || parse_literal(buffer)
|
95
|
-
end
|
96
|
-
|
97
|
-
# @!visibility private
|
98
|
-
def parse_expression(buffer)
|
99
|
-
return unless buffer.scan(/\{/)
|
100
19
|
operator = buffer.scan(/[\+\#\.\/;\?\&\=\,\!\@\|]/)
|
101
|
-
expression =
|
102
|
-
expression
|
103
|
-
expression if expect(buffer, ?})
|
104
|
-
end
|
105
|
-
|
106
|
-
# @!visibility private
|
107
|
-
def parse_variable(buffer)
|
108
|
-
match = expect(buffer, /(?<name>\w+)(?:\:(?<prefix>\d{1,4})|(?<explode>\*))?/)
|
109
|
-
Variable.new(match[:name], prefix: match[:prefix], explode: match[:explode])
|
20
|
+
expression = node(:expression, [variable[]], operator: operator) { variable[] if scan(?,) }
|
21
|
+
expression if expect(?})
|
110
22
|
end
|
111
23
|
|
112
|
-
|
113
|
-
def parse_literal(buffer)
|
114
|
-
return unless char = buffer.getch
|
115
|
-
raise unexpected(?}) if char == ?}
|
116
|
-
char == ?/ ? Separator.new('/') : Char.new(char)
|
117
|
-
end
|
24
|
+
on(?}) { |c| unexpected(c) }
|
118
25
|
|
119
26
|
# @!visibility private
|
120
27
|
def compile(*args, **options)
|
@@ -135,6 +42,6 @@ module Mustermann
|
|
135
42
|
@split_params.include? key
|
136
43
|
end
|
137
44
|
|
138
|
-
private :
|
45
|
+
private :compile, :map_param, :always_array?
|
139
46
|
end
|
140
47
|
end
|
data/lib/mustermann/version.rb
CHANGED
data/mustermann.gemspec
CHANGED
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
|
|
13
13
|
s.files = `git ls-files`.split("\n")
|
14
14
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
15
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
|
-
s.extra_rdoc_files = %w[README.md]
|
16
|
+
s.extra_rdoc_files = %w[README.md internals.md]
|
17
17
|
s.require_path = 'lib'
|
18
18
|
s.required_ruby_version = '>= 2.0.0'
|
19
19
|
|
data/spec/extension_spec.rb
CHANGED
@@ -44,6 +44,7 @@ describe Mustermann::Extension do
|
|
44
44
|
|
45
45
|
context 'route local' do
|
46
46
|
before do
|
47
|
+
app.set(:pattern, nil)
|
47
48
|
app.get('/:id', capture: /\d+/) { 'ok' }
|
48
49
|
end
|
49
50
|
|
@@ -84,6 +85,148 @@ describe Mustermann::Extension do
|
|
84
85
|
end
|
85
86
|
end
|
86
87
|
|
88
|
+
describe :pattern do
|
89
|
+
describe :except do
|
90
|
+
before { app.get('/auth/*', pattern: { except: '/auth/login' }) { 'ok' } }
|
91
|
+
example { get('/auth/dunno').should be_ok }
|
92
|
+
example { get('/auth/login').should_not be_ok }
|
93
|
+
end
|
94
|
+
|
95
|
+
describe :capture do
|
96
|
+
context 'route local' do
|
97
|
+
before do
|
98
|
+
app.set(:pattern, nil)
|
99
|
+
app.get('/:id', pattern: { capture: /\d+/ }) { 'ok' }
|
100
|
+
end
|
101
|
+
|
102
|
+
example { get('/42').should be_ok }
|
103
|
+
example { get('/foo').should_not be_ok }
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'global and route local' do
|
107
|
+
context 'global is a hash' do
|
108
|
+
before do
|
109
|
+
app.set(:pattern, capture: { id: /\d+/ })
|
110
|
+
app.get('/:id(.:ext)?', pattern: { capture: { ext: 'png' }}) { ?a }
|
111
|
+
app.get('/:id', pattern: { capture: { id: 'foo' }}) { ?b }
|
112
|
+
app.get('/:id', pattern: { capture: :alpha }) { ?c }
|
113
|
+
end
|
114
|
+
|
115
|
+
example { get('/20') .body.should be == ?a }
|
116
|
+
example { get('/20.png') .body.should be == ?a }
|
117
|
+
example { get('/foo') .body.should be == ?b }
|
118
|
+
example { get('/bar') .body.should be == ?c }
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'global is not a hash' do
|
122
|
+
before do
|
123
|
+
app.set(:pattern, capture: /\d+/)
|
124
|
+
app.get('/:slug(.:ext)?', pattern: { capture: { ext: 'png' }}) { params[:slug] }
|
125
|
+
app.get('/:slug', pattern: { capture: :alpha }) { 'ok' }
|
126
|
+
end
|
127
|
+
|
128
|
+
example { get('/20.png').should be_ok }
|
129
|
+
example { get('/foo.png').should_not be_ok }
|
130
|
+
example { get('/foo').should be_ok }
|
131
|
+
|
132
|
+
example { get('/20.png') .body.should be == '20' }
|
133
|
+
example { get('/42') .body.should be == '42' }
|
134
|
+
example { get('/foo') .body.should be == 'ok' }
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe :greedy do
|
140
|
+
context 'default' do
|
141
|
+
before { app.get('/:name.:ext') { params[:name] }}
|
142
|
+
example { get('/foo.bar') .body.should be == 'foo' }
|
143
|
+
example { get('/foo.bar.baz') .body.should be == 'foo.bar' }
|
144
|
+
end
|
145
|
+
|
146
|
+
context 'enabled' do
|
147
|
+
before { app.get('/:name.:ext', pattern: { greedy: true }) { params[:name] }}
|
148
|
+
example { get('/foo.bar') .body.should be == 'foo' }
|
149
|
+
example { get('/foo.bar.baz') .body.should be == 'foo.bar' }
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'disabled' do
|
153
|
+
before { app.get('/:name.:ext', pattern: { greedy: false }) { params[:name] }}
|
154
|
+
example { get('/foo.bar') .body.should be == 'foo' }
|
155
|
+
example { get('/foo.bar.baz') .body.should be == 'foo' }
|
156
|
+
end
|
157
|
+
|
158
|
+
context 'global' do
|
159
|
+
before do
|
160
|
+
app.set(:pattern, greedy: false)
|
161
|
+
app.get('/:name.:ext') { params[:name] }
|
162
|
+
end
|
163
|
+
|
164
|
+
example { get('/foo.bar') .body.should be == 'foo' }
|
165
|
+
example { get('/foo.bar.baz') .body.should be == 'foo' }
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe :space_matches_plus do
|
170
|
+
context 'default' do
|
171
|
+
before { app.get('/foo bar') { 'ok' }}
|
172
|
+
example { get('/foo%20bar') .should be_ok }
|
173
|
+
example { get('/foo+bar') .should be_ok }
|
174
|
+
end
|
175
|
+
|
176
|
+
context 'enabled' do
|
177
|
+
before { app.get('/foo bar', pattern: { space_matches_plus: true }) { 'ok' }}
|
178
|
+
example { get('/foo%20bar') .should be_ok }
|
179
|
+
example { get('/foo+bar') .should be_ok }
|
180
|
+
end
|
181
|
+
|
182
|
+
context 'disabled' do
|
183
|
+
before { app.get('/foo bar', pattern: { space_matches_plus: false }) { 'ok' }}
|
184
|
+
example { get('/foo%20bar') .should be_ok }
|
185
|
+
example { get('/foo+bar') .should_not be_ok }
|
186
|
+
end
|
187
|
+
|
188
|
+
context 'global' do
|
189
|
+
before do
|
190
|
+
app.set(:pattern, space_matches_plus: false)
|
191
|
+
app.get('/foo bar') { 'ok' }
|
192
|
+
end
|
193
|
+
|
194
|
+
example { get('/foo%20bar') .should be_ok }
|
195
|
+
example { get('/foo+bar') .should_not be_ok }
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
describe :uri_decode do
|
200
|
+
context 'default' do
|
201
|
+
before { app.get('/&') { 'ok' }}
|
202
|
+
example { get('/&') .should be_ok }
|
203
|
+
example { get('/%26') .should be_ok }
|
204
|
+
end
|
205
|
+
|
206
|
+
context 'enabled' do
|
207
|
+
before { app.get('/&', pattern: { uri_decode: true }) { 'ok' }}
|
208
|
+
example { get('/&') .should be_ok }
|
209
|
+
example { get('/%26') .should be_ok }
|
210
|
+
end
|
211
|
+
|
212
|
+
context 'disabled' do
|
213
|
+
before { app.get('/&', pattern: { uri_decode: false }) { 'ok' }}
|
214
|
+
example { get('/&') .should be_ok }
|
215
|
+
example { get('/%26') .should_not be_ok }
|
216
|
+
end
|
217
|
+
|
218
|
+
context 'global' do
|
219
|
+
before do
|
220
|
+
app.set(:pattern, uri_decode: false)
|
221
|
+
app.get('/&') { 'ok' }
|
222
|
+
end
|
223
|
+
|
224
|
+
example { get('/&') .should be_ok }
|
225
|
+
example { get('/%26') .should_not be_ok }
|
226
|
+
end
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
87
230
|
describe :type do
|
88
231
|
describe :identity do
|
89
232
|
before do
|
data/spec/mustermann_spec.rb
CHANGED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'support'
|
2
|
+
require 'mustermann'
|
3
|
+
require 'mustermann/extension'
|
4
|
+
require 'sinatra/base'
|
5
|
+
|
6
|
+
describe Mustermann do
|
7
|
+
describe :new do
|
8
|
+
example { Mustermann.new('') .should be_a(Mustermann::Sinatra) }
|
9
|
+
example { Mustermann.new('', type: :identity) .should be_a(Mustermann::Identity) }
|
10
|
+
example { Mustermann.new('', type: :rails) .should be_a(Mustermann::Rails) }
|
11
|
+
example { Mustermann.new('', type: :shell) .should be_a(Mustermann::Shell) }
|
12
|
+
example { Mustermann.new('', type: :sinatra) .should be_a(Mustermann::Sinatra) }
|
13
|
+
example { Mustermann.new('', type: :simple) .should be_a(Mustermann::Simple) }
|
14
|
+
example { Mustermann.new('', type: :template) .should be_a(Mustermann::Template) }
|
15
|
+
|
16
|
+
example { expect { Mustermann.new('', foo: :bar) }.to raise_error(ArgumentError, "unsupported option :foo for Mustermann::Sinatra") }
|
17
|
+
example { expect { Mustermann.new('', type: :ast) }.to raise_error(ArgumentError, "unsupported type :ast") }
|
18
|
+
end
|
19
|
+
|
20
|
+
describe :[] do
|
21
|
+
example { Mustermann[:identity] .should be == Mustermann::Identity }
|
22
|
+
example { Mustermann[:rails] .should be == Mustermann::Rails }
|
23
|
+
example { Mustermann[:shell] .should be == Mustermann::Shell }
|
24
|
+
example { Mustermann[:sinatra] .should be == Mustermann::Sinatra }
|
25
|
+
example { Mustermann[:simple] .should be == Mustermann::Simple }
|
26
|
+
example { Mustermann[:template] .should be == Mustermann::Template }
|
27
|
+
|
28
|
+
example { expect { Mustermann[:ast] }.to raise_error(ArgumentError, "unsupported type :ast") }
|
29
|
+
end
|
30
|
+
|
31
|
+
describe :extend_object do
|
32
|
+
context 'special behavior for Sinatra only' do
|
33
|
+
example { Object .new.extend(Mustermann).should be_a(Mustermann) }
|
34
|
+
example { Object .new.extend(Mustermann).should_not be_a(Mustermann::Extension) }
|
35
|
+
example { Class .new.extend(Mustermann).should be_a(Mustermann) }
|
36
|
+
example { Class .new.extend(Mustermann).should_not be_a(Mustermann::Extension) }
|
37
|
+
example { Sinatra .new.extend(Mustermann).should_not be_a(Mustermann) }
|
38
|
+
example { Sinatra .new.extend(Mustermann).should be_a(Mustermann::Extension) }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|