mustermann 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +429 -672
  3. data/lib/mustermann.rb +95 -20
  4. data/lib/mustermann/ast/boundaries.rb +44 -0
  5. data/lib/mustermann/ast/compiler.rb +13 -7
  6. data/lib/mustermann/ast/expander.rb +22 -12
  7. data/lib/mustermann/ast/node.rb +69 -5
  8. data/lib/mustermann/ast/param_scanner.rb +20 -0
  9. data/lib/mustermann/ast/parser.rb +138 -19
  10. data/lib/mustermann/ast/pattern.rb +59 -7
  11. data/lib/mustermann/ast/template_generator.rb +28 -0
  12. data/lib/mustermann/ast/transformer.rb +2 -2
  13. data/lib/mustermann/ast/translator.rb +20 -0
  14. data/lib/mustermann/ast/validation.rb +4 -3
  15. data/lib/mustermann/composite.rb +101 -0
  16. data/lib/mustermann/expander.rb +2 -2
  17. data/lib/mustermann/identity.rb +56 -0
  18. data/lib/mustermann/pattern.rb +185 -10
  19. data/lib/mustermann/pattern_cache.rb +49 -0
  20. data/lib/mustermann/regexp.rb +1 -0
  21. data/lib/mustermann/regexp_based.rb +18 -1
  22. data/lib/mustermann/regular.rb +4 -1
  23. data/lib/mustermann/simple_match.rb +5 -0
  24. data/lib/mustermann/sinatra.rb +22 -5
  25. data/lib/mustermann/to_pattern.rb +11 -6
  26. data/lib/mustermann/version.rb +1 -1
  27. data/mustermann.gemspec +1 -14
  28. data/spec/ast_spec.rb +14 -0
  29. data/spec/composite_spec.rb +147 -0
  30. data/spec/expander_spec.rb +15 -0
  31. data/spec/identity_spec.rb +44 -0
  32. data/spec/mustermann_spec.rb +17 -2
  33. data/spec/pattern_spec.rb +7 -3
  34. data/spec/regular_spec.rb +25 -0
  35. data/spec/sinatra_spec.rb +184 -9
  36. data/spec/to_pattern_spec.rb +49 -0
  37. metadata +15 -180
  38. data/.gitignore +0 -18
  39. data/.rspec +0 -2
  40. data/.travis.yml +0 -4
  41. data/.yardopts +0 -1
  42. data/Gemfile +0 -2
  43. data/LICENSE +0 -22
  44. data/Rakefile +0 -6
  45. data/internals.md +0 -64
  46. data/lib/mustermann/ast/tree_renderer.rb +0 -29
  47. data/lib/mustermann/rails.rb +0 -17
  48. data/lib/mustermann/shell.rb +0 -29
  49. data/lib/mustermann/simple.rb +0 -35
  50. data/lib/mustermann/template.rb +0 -47
  51. data/spec/rails_spec.rb +0 -521
  52. data/spec/shell_spec.rb +0 -108
  53. data/spec/simple_spec.rb +0 -236
  54. data/spec/support.rb +0 -5
  55. data/spec/support/coverage.rb +0 -16
  56. data/spec/support/env.rb +0 -16
  57. data/spec/support/expand_matcher.rb +0 -27
  58. data/spec/support/match_matcher.rb +0 -39
  59. data/spec/support/pattern.rb +0 -39
  60. data/spec/template_spec.rb +0 -814
@@ -1,108 +0,0 @@
1
- require 'support'
2
- require 'mustermann/shell'
3
-
4
- describe Mustermann::Shell do
5
- extend Support::Pattern
6
-
7
- pattern '' do
8
- it { should match('') }
9
- it { should_not match('/') }
10
- end
11
-
12
- pattern '/' do
13
- it { should match('/') }
14
- it { should_not match('/foo') }
15
-
16
- example { pattern.params('/').should be == {} }
17
- example { pattern.params('').should be_nil }
18
- end
19
-
20
- pattern '/foo' do
21
- it { should match('/foo') }
22
- it { should_not match('/bar') }
23
- it { should_not match('/foo.bar') }
24
- end
25
-
26
- pattern '/foo/bar' do
27
- it { should match('/foo/bar') }
28
- it { should match('/foo%2Fbar') }
29
- it { should match('/foo%2fbar') }
30
- end
31
-
32
- pattern '/*/bar' do
33
- it { should match('/foo/bar') }
34
- it { should match('/bar/bar') }
35
- it { should match('/foo%2Fbar') }
36
- it { should match('/foo%2fbar') }
37
- it { should_not match('/foo/foo/bar') }
38
- it { should_not match('/bar/foo') }
39
- end
40
-
41
- pattern '/**/foo' do
42
- it { should match('/a/b/c/foo') }
43
- it { should match('/a/b/c/foo') }
44
- it { should match('/a/.b/c/foo') }
45
- it { should match('/a/.b/c/foo') }
46
- end
47
-
48
- pattern '/:foo' do
49
- it { should match('/:foo') }
50
- it { should match('/%3Afoo') }
51
- it { should_not match('/foo') }
52
- it { should_not match('/foo?') }
53
- it { should_not match('/foo/bar') }
54
- it { should_not match('/') }
55
- it { should_not match('/foo/') }
56
- end
57
-
58
- pattern '/föö' do
59
- it { should match("/f%C3%B6%C3%B6") }
60
- end
61
-
62
- pattern '/test$/' do
63
- it { should match('/test$/') }
64
- end
65
-
66
- pattern '/te+st/' do
67
- it { should match('/te+st/') }
68
- it { should_not match('/test/') }
69
- it { should_not match('/teest/') }
70
- end
71
-
72
- pattern "/path with spaces" do
73
- it { should match('/path%20with%20spaces') }
74
- it { should_not match('/path%2Bwith%2Bspaces') }
75
- it { should_not match('/path+with+spaces') }
76
- end
77
-
78
- pattern '/foo&bar' do
79
- it { should match('/foo&bar') }
80
- end
81
-
82
- pattern '/test.bar' do
83
- it { should match('/test.bar') }
84
- it { should_not match('/test0bar') }
85
- end
86
-
87
- pattern '/{foo,bar}' do
88
- it { should match('/foo') }
89
- it { should match('/bar') }
90
- it { should_not match('/foobar') }
91
- end
92
-
93
- pattern '/foo/bar', uri_decode: false do
94
- it { should match('/foo/bar') }
95
- it { should_not match('/foo%2Fbar') }
96
- it { should_not match('/foo%2fbar') }
97
- end
98
-
99
- pattern "/path with spaces", uri_decode: false do
100
- it { should_not match('/path%20with%20spaces') }
101
- it { should_not match('/path%2Bwith%2Bspaces') }
102
- it { should_not match('/path+with+spaces') }
103
- end
104
-
105
- describe :=~ do
106
- example { '/foo'.should be =~ Mustermann::Shell.new('/foo') }
107
- end
108
- end
@@ -1,236 +0,0 @@
1
- require 'support'
2
- require 'mustermann/simple'
3
-
4
- describe Mustermann::Simple do
5
- extend Support::Pattern
6
-
7
- pattern '' do
8
- it { should match('') }
9
- it { should_not match('/') }
10
- end
11
-
12
- pattern '/' do
13
- it { should match('/') }
14
- it { should_not match('/foo') }
15
- end
16
-
17
- pattern '/foo' do
18
- it { should match('/foo') }
19
- it { should_not match('/bar') }
20
- it { should_not match('/foo.bar') }
21
- end
22
-
23
- pattern '/foo/bar' do
24
- it { should match('/foo/bar') }
25
- it { should_not match('/foo%2Fbar') }
26
- it { should_not match('/foo%2fbar') }
27
- end
28
-
29
- pattern '/:foo' do
30
- it { should match('/foo') .capturing foo: 'foo' }
31
- it { should match('/bar') .capturing foo: 'bar' }
32
- it { should match('/foo.bar') .capturing foo: 'foo.bar' }
33
- it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
34
- it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
35
-
36
- it { should_not match('/foo?') }
37
- it { should_not match('/foo/bar') }
38
- it { should_not match('/') }
39
- it { should_not match('/foo/') }
40
- end
41
-
42
- pattern '/föö' do
43
- it { should match("/f%C3%B6%C3%B6") }
44
- end
45
-
46
- pattern "/:foo/:bar" do
47
- it { should match('/foo/bar') .capturing foo: 'foo', bar: 'bar' }
48
- it { should match('/foo.bar/bar.foo') .capturing foo: 'foo.bar', bar: 'bar.foo' }
49
- it { should match('/user@example.com/name') .capturing foo: 'user@example.com', bar: 'name' }
50
- it { should match('/10.1/te.st') .capturing foo: '10.1', bar: 'te.st' }
51
- it { should match('/10.1.2/te.st') .capturing foo: '10.1.2', bar: 'te.st' }
52
-
53
- it { should_not match('/foo%2Fbar') }
54
- it { should_not match('/foo%2fbar') }
55
-
56
- example { pattern.params('/bar/foo').should be == {"foo" => "bar", "bar" => "foo"} }
57
- example { pattern.params('').should be_nil }
58
- end
59
-
60
- pattern '/hello/:person' do
61
- it { should match('/hello/Frank').capturing person: 'Frank' }
62
- end
63
-
64
- pattern '/?:foo?/?:bar?' do
65
- it { should match('/hello/world') .capturing foo: 'hello', bar: 'world' }
66
- it { should match('/hello') .capturing foo: 'hello', bar: nil }
67
- it { should match('/') .capturing foo: nil, bar: nil }
68
- it { should match('') .capturing foo: nil, bar: nil }
69
-
70
- it { should_not match('/hello/world/') }
71
- end
72
-
73
- pattern '/*' do
74
- it { should match('/') .capturing splat: '' }
75
- it { should match('/foo') .capturing splat: 'foo' }
76
- it { should match('/foo/bar') .capturing splat: 'foo/bar' }
77
-
78
- example { pattern.params('/foo').should be == {"splat" => ["foo"]} }
79
- end
80
-
81
- pattern '/:foo/*' do
82
- it { should match("/foo/bar/baz") .capturing foo: 'foo', splat: 'bar/baz' }
83
- it { should match("/foo/") .capturing foo: 'foo', splat: '' }
84
- it { should match('/h%20w/h%20a%20y') .capturing foo: 'h%20w', splat: 'h%20a%20y' }
85
- it { should_not match('/foo') }
86
-
87
- example { pattern.params('/bar/foo').should be == {"splat" => ["foo"], "foo" => "bar"} }
88
- example { pattern.params('/bar/foo/f%20o').should be == {"splat" => ["foo/f o"], "foo" => "bar"} }
89
- end
90
-
91
- pattern '/test$/' do
92
- it { should match('/test$/') }
93
- end
94
-
95
- pattern '/te+st/' do
96
- it { should match('/te+st/') }
97
- it { should_not match('/test/') }
98
- it { should_not match('/teest/') }
99
- end
100
-
101
- pattern "/path with spaces" do
102
- it { should match('/path%20with%20spaces') }
103
- it { should match('/path%2Bwith%2Bspaces') }
104
- it { should match('/path+with+spaces') }
105
- end
106
-
107
- pattern '/foo&bar' do
108
- it { should match('/foo&bar') }
109
- end
110
-
111
- pattern '/*/:foo/*/*' do
112
- it { should match('/bar/foo/bling/baz/boom') }
113
-
114
- it "should capture all splat parts" do
115
- match = pattern.match('/bar/foo/bling/baz/boom')
116
- match.captures.should be == ['bar', 'foo', 'bling', 'baz/boom']
117
- match.names.should be == ['splat', 'foo']
118
- end
119
-
120
- it 'should map to proper params' do
121
- pattern.params('/bar/foo/bling/baz/boom').should be == {
122
- "foo" => "foo", "splat" => ['bar', 'bling', 'baz/boom']
123
- }
124
- end
125
- end
126
-
127
- pattern '/test.bar' do
128
- it { should match('/test.bar') }
129
- it { should_not match('/test0bar') }
130
- end
131
-
132
- pattern '/:file.:ext' do
133
- it { should match('/pony.jpg') .capturing file: 'pony', ext: 'jpg' }
134
- it { should match('/pony%2Ejpg') .capturing file: 'pony', ext: 'jpg' }
135
- it { should match('/pony%2ejpg') .capturing file: 'pony', ext: 'jpg' }
136
-
137
- it { should match('/pony%E6%AD%A3%2Ejpg') .capturing file: 'pony%E6%AD%A3', ext: 'jpg' }
138
- it { should match('/pony%e6%ad%a3%2ejpg') .capturing file: 'pony%e6%ad%a3', ext: 'jpg' }
139
- it { should match('/pony正%2Ejpg') .capturing file: 'pony正', ext: 'jpg' }
140
- it { should match('/pony正%2ejpg') .capturing file: 'pony正', ext: 'jpg' }
141
- it { should match('/pony正..jpg') .capturing file: 'pony正.', ext: 'jpg' }
142
-
143
- it { should_not match('/.jpg') }
144
- end
145
-
146
- pattern '/:id/test.bar' do
147
- it { should match('/3/test.bar') .capturing id: '3' }
148
- it { should match('/2/test.bar') .capturing id: '2' }
149
- it { should match('/2E/test.bar') .capturing id: '2E' }
150
- it { should match('/2e/test.bar') .capturing id: '2e' }
151
- it { should match('/%2E/test.bar') .capturing id: '%2E' }
152
- end
153
-
154
- pattern '/10/:id' do
155
- it { should match('/10/test') .capturing id: 'test' }
156
- it { should match('/10/te.st') .capturing id: 'te.st' }
157
- end
158
-
159
- pattern '/10.1/:id' do
160
- it { should match('/10.1/test') .capturing id: 'test' }
161
- it { should match('/10.1/te.st') .capturing id: 'te.st' }
162
- end
163
-
164
- pattern '/foo?' do
165
- it { should match('/fo') }
166
- it { should match('/foo') }
167
- it { should_not match('') }
168
- it { should_not match('/') }
169
- it { should_not match('/f') }
170
- it { should_not match('/fooo') }
171
- end
172
-
173
- pattern '/:fOO' do
174
- it { should match('/a').capturing fOO: 'a' }
175
- end
176
-
177
- pattern '/:_X' do
178
- it { should match('/a').capturing _X: 'a' }
179
- end
180
-
181
- pattern '/:f00' do
182
- it { should match('/a').capturing f00: 'a' }
183
- end
184
-
185
- pattern '/:foo.?' do
186
- it { should match('/a.').capturing foo: 'a.' }
187
- it { should match('/xy').capturing foo: 'xy' }
188
- end
189
-
190
- pattern '/(a)' do
191
- it { should match('/(a)') }
192
- it { should_not match('/a') }
193
- end
194
-
195
- pattern '/:foo.?', greedy: false do
196
- it { should match('/a.').capturing foo: 'a' }
197
- it { should match('/xy').capturing foo: 'xy' }
198
- end
199
-
200
- pattern '/foo?', uri_decode: false do
201
- it { should match('/foo') }
202
- it { should match('/fo') }
203
- it { should_not match('/foo?') }
204
- end
205
-
206
- pattern '/foo/bar', uri_decode: false do
207
- it { should match('/foo/bar') }
208
- it { should_not match('/foo%2Fbar') }
209
- it { should_not match('/foo%2fbar') }
210
- end
211
-
212
- pattern "/path with spaces", uri_decode: false do
213
- it { should match('/path with spaces') }
214
- it { should_not match('/path%20with%20spaces') }
215
- it { should_not match('/path%2Bwith%2Bspaces') }
216
- it { should_not match('/path+with+spaces') }
217
- end
218
-
219
- pattern "/path with spaces", space_matches_plus: false do
220
- it { should match('/path%20with%20spaces') }
221
- it { should_not match('/path%2Bwith%2Bspaces') }
222
- it { should_not match('/path+with+spaces') }
223
- end
224
-
225
- context 'error handling' do
226
- example '? at beginning of route' do
227
- expect { Mustermann::Simple.new('?foobar') }.
228
- to raise_error(Mustermann::ParseError)
229
- end
230
-
231
- example 'invalid capture name' do
232
- expect { Mustermann::Simple.new('/:1a/') }.
233
- to raise_error(Mustermann::CompileError)
234
- end
235
- end
236
- end
@@ -1,5 +0,0 @@
1
- require 'support/env'
2
- require 'support/coverage'
3
- require 'support/expand_matcher'
4
- require 'support/match_matcher'
5
- require 'support/pattern'
@@ -1,16 +0,0 @@
1
- require 'simplecov'
2
- require 'coveralls'
3
-
4
- SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
5
- SimpleCov::Formatter::HTMLFormatter,
6
- Coveralls::SimpleCov::Formatter
7
- ]
8
-
9
- SimpleCov.start do
10
- project_name 'mustermann'
11
- minimum_coverage 100
12
- coverage_dir '.coverage'
13
-
14
- add_filter "/spec/"
15
- add_group 'Library', 'lib'
16
- end
@@ -1,16 +0,0 @@
1
- if RUBY_VERSION < '2.0.0'
2
- $stderr.puts "needs Ruby 2.0.0, you're running #{RUBY_VERSION}"
3
- exit 1
4
- end
5
-
6
- ENV['RACK_ENV'] = 'test'
7
-
8
- require 'tool/warning_filter'
9
- require 'rspec'
10
- require 'rspec/its'
11
-
12
- RSpec.configure do |config|
13
- config.expect_with :rspec do |c|
14
- c.syntax = [:should, :expect]
15
- end
16
- end
@@ -1,27 +0,0 @@
1
- RSpec::Matchers.define :expand do |values = {}|
2
- match do |pattern|
3
- @string ||= nil
4
- begin
5
- expanded = pattern.expand(values)
6
- rescue Exception
7
- false
8
- else
9
- @string ? @string == expanded : !!expanded
10
- end
11
- end
12
-
13
- chain :to do |string|
14
- @string = string
15
- end
16
-
17
- failure_message do |pattern|
18
- message = "expected %p to be expandable with %p" % [pattern, values]
19
- expanded = pattern.expand(values)
20
- message << " and result in %p, but got %p" % [@string, expanded] if @string
21
- message
22
- end
23
-
24
- failure_message_when_negated do |pattern|
25
- "expected %p not to be expandable with %p" % [pattern, values]
26
- end
27
- end
@@ -1,39 +0,0 @@
1
- RSpec::Matchers.define :match do |expected|
2
- match do |actual|
3
- @captures ||= false
4
- match = actual.match(expected)
5
- match &&= @captures.all? { |k, v| match[k] == v } if @captures
6
- match
7
- end
8
-
9
- chain :capturing do |captures|
10
- @captures = captures
11
- end
12
-
13
- failure_message do |actual|
14
- require 'pp'
15
- match = actual.match(expected)
16
- if match
17
- key, value = @captures.detect { |k, v| match[k] != v }
18
- message = "expected %p to capture %p as %p when matching %p, but got %p\n\nRegular Expression:\n%p" % [
19
- actual.to_s, key, value, expected, match[key], actual.regexp
20
- ]
21
-
22
- if ast = actual.instance_variable_get(:@ast)
23
- require 'mustermann/ast/tree_renderer'
24
- tree = Mustermann::AST::TreeRenderer.render(ast)
25
- message << "\n\nAST:\n#{tree}"
26
- end
27
-
28
- message
29
- else
30
- "expected %p to match %p" % [ actual, expected ]
31
- end
32
- end
33
-
34
- failure_message_when_negated do |actual|
35
- "expected %p not to match %p" % [
36
- actual.to_s, expected
37
- ]
38
- end
39
- end