mustermann 0.3.1 → 0.4.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.
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