mustermann 1.0.1 → 1.0.2.rc1

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 (126) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +18 -0
  3. data/.rspec +5 -0
  4. data/.travis.yml +25 -0
  5. data/.yardopts +3 -0
  6. data/Gemfile +7 -0
  7. data/README.md +230 -799
  8. data/Rakefile +27 -0
  9. data/mustermann-contrib/LICENSE +23 -0
  10. data/mustermann-contrib/README.md +1155 -0
  11. data/mustermann-contrib/examples/highlighting.rb +35 -0
  12. data/mustermann-contrib/highlighting.png +0 -0
  13. data/mustermann-contrib/irb.png +0 -0
  14. data/mustermann-contrib/lib/mustermann/cake.rb +19 -0
  15. data/mustermann-contrib/lib/mustermann/express.rb +38 -0
  16. data/mustermann-contrib/lib/mustermann/file_utils.rb +218 -0
  17. data/mustermann-contrib/lib/mustermann/file_utils/glob_pattern.rb +40 -0
  18. data/mustermann-contrib/lib/mustermann/fileutils.rb +1 -0
  19. data/mustermann-contrib/lib/mustermann/flask.rb +199 -0
  20. data/mustermann-contrib/lib/mustermann/pyramid.rb +29 -0
  21. data/mustermann-contrib/lib/mustermann/rails.rb +47 -0
  22. data/mustermann-contrib/lib/mustermann/shell.rb +57 -0
  23. data/mustermann-contrib/lib/mustermann/simple.rb +51 -0
  24. data/mustermann-contrib/lib/mustermann/string_scanner.rb +314 -0
  25. data/mustermann-contrib/lib/mustermann/strscan.rb +1 -0
  26. data/mustermann-contrib/lib/mustermann/template.rb +63 -0
  27. data/mustermann-contrib/lib/mustermann/uri_template.rb +1 -0
  28. data/mustermann-contrib/lib/mustermann/versions.rb +47 -0
  29. data/mustermann-contrib/lib/mustermann/visualizer.rb +39 -0
  30. data/mustermann-contrib/lib/mustermann/visualizer/highlight.rb +138 -0
  31. data/mustermann-contrib/lib/mustermann/visualizer/highlighter.rb +38 -0
  32. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/ad_hoc.rb +95 -0
  33. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/ast.rb +103 -0
  34. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/composite.rb +46 -0
  35. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/dummy.rb +19 -0
  36. data/mustermann-contrib/lib/mustermann/visualizer/highlighter/regular.rb +105 -0
  37. data/mustermann-contrib/lib/mustermann/visualizer/pattern_extension.rb +69 -0
  38. data/mustermann-contrib/lib/mustermann/visualizer/renderer/ansi.rb +24 -0
  39. data/mustermann-contrib/lib/mustermann/visualizer/renderer/generic.rb +47 -0
  40. data/mustermann-contrib/lib/mustermann/visualizer/renderer/hansi_template.rb +35 -0
  41. data/mustermann-contrib/lib/mustermann/visualizer/renderer/html.rb +51 -0
  42. data/mustermann-contrib/lib/mustermann/visualizer/renderer/sexp.rb +38 -0
  43. data/mustermann-contrib/lib/mustermann/visualizer/tree.rb +64 -0
  44. data/mustermann-contrib/lib/mustermann/visualizer/tree_renderer.rb +79 -0
  45. data/mustermann-contrib/mustermann-contrib.gemspec +19 -0
  46. data/mustermann-contrib/spec/cake_spec.rb +91 -0
  47. data/mustermann-contrib/spec/express_spec.rb +210 -0
  48. data/mustermann-contrib/spec/file_utils_spec.rb +120 -0
  49. data/mustermann-contrib/spec/flask_spec.rb +362 -0
  50. data/mustermann-contrib/spec/flask_subclass_spec.rb +369 -0
  51. data/mustermann-contrib/spec/pattern_extension_spec.rb +50 -0
  52. data/mustermann-contrib/spec/pyramid_spec.rb +102 -0
  53. data/mustermann-contrib/spec/rails_spec.rb +648 -0
  54. data/mustermann-contrib/spec/shell_spec.rb +148 -0
  55. data/mustermann-contrib/spec/simple_spec.rb +269 -0
  56. data/mustermann-contrib/spec/string_scanner_spec.rb +272 -0
  57. data/mustermann-contrib/spec/template_spec.rb +842 -0
  58. data/mustermann-contrib/spec/visualizer_spec.rb +199 -0
  59. data/mustermann-contrib/theme.png +0 -0
  60. data/mustermann-contrib/tree.png +0 -0
  61. data/mustermann/LICENSE +23 -0
  62. data/mustermann/README.md +853 -0
  63. data/{bench → mustermann/bench}/capturing.rb +0 -0
  64. data/{bench → mustermann/bench}/regexp.rb +0 -0
  65. data/{bench → mustermann/bench}/simple_vs_sinatra.rb +0 -0
  66. data/{bench → mustermann/bench}/template_vs_addressable.rb +0 -0
  67. data/{lib → mustermann/lib}/mustermann.rb +0 -0
  68. data/{lib → mustermann/lib}/mustermann/ast/boundaries.rb +0 -0
  69. data/{lib → mustermann/lib}/mustermann/ast/compiler.rb +0 -0
  70. data/{lib → mustermann/lib}/mustermann/ast/expander.rb +0 -0
  71. data/{lib → mustermann/lib}/mustermann/ast/node.rb +0 -0
  72. data/{lib → mustermann/lib}/mustermann/ast/param_scanner.rb +0 -0
  73. data/{lib → mustermann/lib}/mustermann/ast/parser.rb +0 -0
  74. data/{lib → mustermann/lib}/mustermann/ast/pattern.rb +0 -0
  75. data/{lib → mustermann/lib}/mustermann/ast/template_generator.rb +0 -0
  76. data/{lib → mustermann/lib}/mustermann/ast/transformer.rb +0 -0
  77. data/{lib → mustermann/lib}/mustermann/ast/translator.rb +0 -0
  78. data/{lib → mustermann/lib}/mustermann/ast/validation.rb +0 -0
  79. data/{lib → mustermann/lib}/mustermann/caster.rb +0 -0
  80. data/{lib → mustermann/lib}/mustermann/composite.rb +0 -0
  81. data/{lib → mustermann/lib}/mustermann/concat.rb +13 -2
  82. data/{lib → mustermann/lib}/mustermann/equality_map.rb +0 -0
  83. data/{lib → mustermann/lib}/mustermann/error.rb +0 -0
  84. data/{lib → mustermann/lib}/mustermann/expander.rb +0 -0
  85. data/{lib → mustermann/lib}/mustermann/extension.rb +0 -0
  86. data/{lib → mustermann/lib}/mustermann/identity.rb +0 -0
  87. data/{lib → mustermann/lib}/mustermann/mapper.rb +0 -0
  88. data/{lib → mustermann/lib}/mustermann/pattern.rb +1 -1
  89. data/{lib → mustermann/lib}/mustermann/pattern_cache.rb +0 -0
  90. data/{lib → mustermann/lib}/mustermann/regexp.rb +0 -0
  91. data/{lib → mustermann/lib}/mustermann/regexp_based.rb +0 -0
  92. data/{lib → mustermann/lib}/mustermann/regular.rb +0 -0
  93. data/{lib → mustermann/lib}/mustermann/simple_match.rb +0 -0
  94. data/{lib → mustermann/lib}/mustermann/sinatra.rb +1 -1
  95. data/{lib → mustermann/lib}/mustermann/sinatra/parser.rb +0 -0
  96. data/{lib → mustermann/lib}/mustermann/sinatra/safe_renderer.rb +0 -0
  97. data/{lib → mustermann/lib}/mustermann/sinatra/try_convert.rb +0 -0
  98. data/{lib → mustermann/lib}/mustermann/to_pattern.rb +0 -0
  99. data/{lib → mustermann/lib}/mustermann/version.rb +1 -1
  100. data/{mustermann.gemspec → mustermann/mustermann.gemspec} +0 -0
  101. data/{spec → mustermann/spec}/ast_spec.rb +0 -0
  102. data/{spec → mustermann/spec}/composite_spec.rb +0 -0
  103. data/{spec → mustermann/spec}/concat_spec.rb +12 -0
  104. data/{spec → mustermann/spec}/equality_map_spec.rb +0 -0
  105. data/{spec → mustermann/spec}/expander_spec.rb +0 -0
  106. data/{spec → mustermann/spec}/extension_spec.rb +0 -0
  107. data/{spec → mustermann/spec}/identity_spec.rb +0 -0
  108. data/{spec → mustermann/spec}/mapper_spec.rb +0 -0
  109. data/{spec → mustermann/spec}/mustermann_spec.rb +0 -0
  110. data/{spec → mustermann/spec}/pattern_spec.rb +0 -0
  111. data/{spec → mustermann/spec}/regexp_based_spec.rb +0 -0
  112. data/{spec → mustermann/spec}/regular_spec.rb +0 -0
  113. data/{spec → mustermann/spec}/simple_match_spec.rb +0 -0
  114. data/{spec → mustermann/spec}/sinatra_spec.rb +0 -0
  115. data/{spec → mustermann/spec}/to_pattern_spec.rb +0 -0
  116. data/support/lib/support.rb +7 -0
  117. data/support/lib/support/coverage.rb +23 -0
  118. data/support/lib/support/env.rb +19 -0
  119. data/support/lib/support/expand_matcher.rb +28 -0
  120. data/support/lib/support/generate_template_matcher.rb +27 -0
  121. data/support/lib/support/match_matcher.rb +39 -0
  122. data/support/lib/support/pattern.rb +42 -0
  123. data/support/lib/support/projects.rb +20 -0
  124. data/support/lib/support/scan_matcher.rb +63 -0
  125. data/support/support.gemspec +27 -0
  126. metadata +128 -58
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+ require 'support'
3
+ require 'mustermann/shell'
4
+
5
+ describe Mustermann::Shell do
6
+ extend Support::Pattern
7
+
8
+ pattern '' do
9
+ it { should match('') }
10
+ it { should_not match('/') }
11
+
12
+ it { should_not respond_to(:expand) }
13
+ it { should_not respond_to(:to_templates) }
14
+ end
15
+
16
+ pattern '/' do
17
+ it { should match('/') }
18
+ it { should_not match('/foo') }
19
+
20
+ example { pattern.params('/').should be == {} }
21
+ example { pattern.params('').should be_nil }
22
+ end
23
+
24
+ pattern '/foo' do
25
+ it { should match('/foo') }
26
+ it { should_not match('/bar') }
27
+ it { should_not match('/foo.bar') }
28
+ end
29
+
30
+ pattern '/foo/bar' do
31
+ it { should match('/foo/bar') }
32
+ it { should match('/foo%2Fbar') }
33
+ it { should match('/foo%2fbar') }
34
+ end
35
+
36
+ pattern '/*/bar' do
37
+ it { should match('/foo/bar') }
38
+ it { should match('/bar/bar') }
39
+ it { should match('/foo%2Fbar') }
40
+ it { should match('/foo%2fbar') }
41
+ it { should_not match('/foo/foo/bar') }
42
+ it { should_not match('/bar/foo') }
43
+ end
44
+
45
+ pattern '/**/foo' do
46
+ it { should match('/a/b/c/foo') }
47
+ it { should match('/a/b/c/foo') }
48
+ it { should match('/a/.b/c/foo') }
49
+ it { should match('/a/.b/c/foo') }
50
+ end
51
+
52
+ pattern '/:foo' do
53
+ it { should match('/:foo') }
54
+ it { should match('/%3Afoo') }
55
+ it { should_not match('/foo') }
56
+ it { should_not match('/foo?') }
57
+ it { should_not match('/foo/bar') }
58
+ it { should_not match('/') }
59
+ it { should_not match('/foo/') }
60
+ end
61
+
62
+ pattern '/föö' do
63
+ it { should match("/f%C3%B6%C3%B6") }
64
+ end
65
+
66
+ pattern '/test$/' do
67
+ it { should match('/test$/') }
68
+ end
69
+
70
+ pattern '/te+st/' do
71
+ it { should match('/te+st/') }
72
+ it { should_not match('/test/') }
73
+ it { should_not match('/teest/') }
74
+ end
75
+
76
+ pattern "/path with spaces" do
77
+ it { should match('/path%20with%20spaces') }
78
+ it { should_not match('/path%2Bwith%2Bspaces') }
79
+ it { should_not match('/path+with+spaces') }
80
+ end
81
+
82
+ pattern '/foo&bar' do
83
+ it { should match('/foo&bar') }
84
+ end
85
+
86
+ pattern '/test.bar' do
87
+ it { should match('/test.bar') }
88
+ it { should_not match('/test0bar') }
89
+ end
90
+
91
+ pattern '/{foo,bar}' do
92
+ it { should match('/foo') }
93
+ it { should match('/bar') }
94
+ it { should_not match('/foobar') }
95
+ end
96
+
97
+ pattern '/foo/bar', uri_decode: false do
98
+ it { should match('/foo/bar') }
99
+ it { should_not match('/foo%2Fbar') }
100
+ it { should_not match('/foo%2fbar') }
101
+ end
102
+
103
+ pattern "/path with spaces", uri_decode: false do
104
+ it { should_not match('/path%20with%20spaces') }
105
+ it { should_not match('/path%2Bwith%2Bspaces') }
106
+ it { should_not match('/path+with+spaces') }
107
+ end
108
+
109
+ describe :=~ do
110
+ example { '/foo'.should be =~ Mustermann::Shell.new('/foo') }
111
+ end
112
+
113
+ context "peeking" do
114
+ subject(:pattern) { Mustermann::Shell.new("foo*/") }
115
+
116
+ describe :peek_size do
117
+ example { pattern.peek_size("foo bar/blah") .should be == "foo bar/".size }
118
+ example { pattern.peek_size("foo%20bar/blah") .should be == "foo%20bar/".size }
119
+ example { pattern.peek_size("/foo bar") .should be_nil }
120
+
121
+ context 'with just * as pattern' do
122
+ subject(:pattern) { Mustermann::Shell.new('*') }
123
+ example { pattern.peek_size('foo') .should be == 3 }
124
+ example { pattern.peek_size('foo/bar') .should be == 3 }
125
+ example { pattern.peek_size('foo/bar/baz') .should be == 3 }
126
+ example { pattern.peek_size('foo/bar/baz/blah') .should be == 3 }
127
+ end
128
+ end
129
+
130
+ describe :peek_match do
131
+ example { pattern.peek_match("foo bar/blah") .to_s .should be == "foo bar/" }
132
+ example { pattern.peek_match("foo%20bar/blah") .to_s .should be == "foo%20bar/" }
133
+ example { pattern.peek_match("/foo bar") .should be_nil }
134
+ end
135
+
136
+ describe :peek_params do
137
+ example { pattern.peek_params("foo bar/blah") .should be == [{}, "foo bar/".size] }
138
+ example { pattern.peek_params("foo%20bar/blah") .should be == [{}, "foo%20bar/".size] }
139
+ example { pattern.peek_params("/foo bar") .should be_nil }
140
+ end
141
+ end
142
+
143
+ context "highlighting" do
144
+ let(:pattern) { Mustermann::Shell.new("/**,*/\\*/{a,b}") }
145
+ subject(:sexp) { Mustermann::Visualizer.highlight(pattern).to_sexp }
146
+ it { should be == '(root (separator /) (special *) (special *) (char ,) (special *) (separator /) (escaped "\\\\" (escaped_char *)) (separator /) (union { (root (char a)) ,(root (char b)) }))' }
147
+ end
148
+ end
@@ -0,0 +1,269 @@
1
+ # frozen_string_literal: true
2
+ require 'support'
3
+ require 'mustermann/simple'
4
+ require 'mustermann/visualizer'
5
+
6
+ describe Mustermann::Simple do
7
+ extend Support::Pattern
8
+
9
+ pattern '' do
10
+ it { should match('') }
11
+ it { should_not match('/') }
12
+
13
+ it { should_not respond_to(:expand) }
14
+ it { should_not respond_to(:to_templates) }
15
+ end
16
+
17
+ pattern '/' do
18
+ it { should match('/') }
19
+ it { should_not match('/foo') }
20
+ end
21
+
22
+ pattern '/foo' do
23
+ it { should match('/foo') }
24
+ it { should_not match('/bar') }
25
+ it { should_not match('/foo.bar') }
26
+ end
27
+
28
+ pattern '/foo/bar' do
29
+ it { should match('/foo/bar') }
30
+ it { should_not match('/foo%2Fbar') }
31
+ it { should_not match('/foo%2fbar') }
32
+ end
33
+
34
+ pattern '/:foo' do
35
+ it { should match('/foo') .capturing foo: 'foo' }
36
+ it { should match('/bar') .capturing foo: 'bar' }
37
+ it { should match('/foo.bar') .capturing foo: 'foo.bar' }
38
+ it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
39
+ it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
40
+
41
+ it { should_not match('/foo?') }
42
+ it { should_not match('/foo/bar') }
43
+ it { should_not match('/') }
44
+ it { should_not match('/foo/') }
45
+ end
46
+
47
+ pattern '/föö' do
48
+ it { should match("/f%C3%B6%C3%B6") }
49
+ end
50
+
51
+ pattern "/:foo/:bar" do
52
+ it { should match('/foo/bar') .capturing foo: 'foo', bar: 'bar' }
53
+ it { should match('/foo.bar/bar.foo') .capturing foo: 'foo.bar', bar: 'bar.foo' }
54
+ it { should match('/user@example.com/name') .capturing foo: 'user@example.com', bar: 'name' }
55
+ it { should match('/10.1/te.st') .capturing foo: '10.1', bar: 'te.st' }
56
+ it { should match('/10.1.2/te.st') .capturing foo: '10.1.2', bar: 'te.st' }
57
+
58
+ it { should_not match('/foo%2Fbar') }
59
+ it { should_not match('/foo%2fbar') }
60
+
61
+ example { pattern.params('/bar/foo').should be == {"foo" => "bar", "bar" => "foo"} }
62
+ example { pattern.params('').should be_nil }
63
+ end
64
+
65
+ pattern '/hello/:person' do
66
+ it { should match('/hello/Frank').capturing person: 'Frank' }
67
+ end
68
+
69
+ pattern '/?:foo?/?:bar?' do
70
+ it { should match('/hello/world') .capturing foo: 'hello', bar: 'world' }
71
+ it { should match('/hello') .capturing foo: 'hello', bar: nil }
72
+ it { should match('/') .capturing foo: nil, bar: nil }
73
+ it { should match('') .capturing foo: nil, bar: nil }
74
+
75
+ it { should_not match('/hello/world/') }
76
+ end
77
+
78
+ pattern '/*' do
79
+ it { should match('/') .capturing splat: '' }
80
+ it { should match('/foo') .capturing splat: 'foo' }
81
+ it { should match('/foo/bar') .capturing splat: 'foo/bar' }
82
+
83
+ example { pattern.params('/foo').should be == {"splat" => ["foo"]} }
84
+ end
85
+
86
+ pattern '/:foo/*' do
87
+ it { should match("/foo/bar/baz") .capturing foo: 'foo', splat: 'bar/baz' }
88
+ it { should match("/foo/") .capturing foo: 'foo', splat: '' }
89
+ it { should match('/h%20w/h%20a%20y') .capturing foo: 'h%20w', splat: 'h%20a%20y' }
90
+ it { should_not match('/foo') }
91
+
92
+ example { pattern.params('/bar/foo').should be == {"splat" => ["foo"], "foo" => "bar"} }
93
+ example { pattern.params('/bar/foo/f%20o').should be == {"splat" => ["foo/f o"], "foo" => "bar"} }
94
+ end
95
+
96
+ pattern '/test$/' do
97
+ it { should match('/test$/') }
98
+ end
99
+
100
+ pattern '/te+st/' do
101
+ it { should match('/te+st/') }
102
+ it { should_not match('/test/') }
103
+ it { should_not match('/teest/') }
104
+ end
105
+
106
+ pattern "/path with spaces" do
107
+ it { should match('/path%20with%20spaces') }
108
+ it { should match('/path%2Bwith%2Bspaces') }
109
+ it { should match('/path+with+spaces') }
110
+ end
111
+
112
+ pattern '/foo&bar' do
113
+ it { should match('/foo&bar') }
114
+ end
115
+
116
+ pattern '/*/:foo/*/*' do
117
+ it { should match('/bar/foo/bling/baz/boom') }
118
+
119
+ it "should capture all splat parts" do
120
+ match = pattern.match('/bar/foo/bling/baz/boom')
121
+ match.captures.should be == ['bar', 'foo', 'bling', 'baz/boom']
122
+ match.names.should be == ['splat', 'foo']
123
+ end
124
+
125
+ it 'should map to proper params' do
126
+ pattern.params('/bar/foo/bling/baz/boom').should be == {
127
+ "foo" => "foo", "splat" => ['bar', 'bling', 'baz/boom']
128
+ }
129
+ end
130
+ end
131
+
132
+ pattern '/test.bar' do
133
+ it { should match('/test.bar') }
134
+ it { should_not match('/test0bar') }
135
+ end
136
+
137
+ pattern '/:file.:ext' do
138
+ it { should match('/pony.jpg') .capturing file: 'pony', 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
+
142
+ it { should match('/pony%E6%AD%A3%2Ejpg') .capturing file: 'pony%E6%AD%A3', ext: 'jpg' }
143
+ it { should match('/pony%e6%ad%a3%2ejpg') .capturing file: 'pony%e6%ad%a3', ext: 'jpg' }
144
+ it { should match('/pony正%2Ejpg') .capturing file: 'pony正', ext: 'jpg' }
145
+ it { should match('/pony正%2ejpg') .capturing file: 'pony正', ext: 'jpg' }
146
+ it { should match('/pony正..jpg') .capturing file: 'pony正.', ext: 'jpg' }
147
+
148
+ it { should_not match('/.jpg') }
149
+ end
150
+
151
+ pattern '/:id/test.bar' do
152
+ it { should match('/3/test.bar') .capturing id: '3' }
153
+ it { should match('/2/test.bar') .capturing id: '2' }
154
+ it { should match('/2E/test.bar') .capturing id: '2E' }
155
+ it { should match('/2e/test.bar') .capturing id: '2e' }
156
+ it { should match('/%2E/test.bar') .capturing id: '%2E' }
157
+ end
158
+
159
+ pattern '/10/:id' do
160
+ it { should match('/10/test') .capturing id: 'test' }
161
+ it { should match('/10/te.st') .capturing id: 'te.st' }
162
+ end
163
+
164
+ pattern '/10.1/:id' do
165
+ it { should match('/10.1/test') .capturing id: 'test' }
166
+ it { should match('/10.1/te.st') .capturing id: 'te.st' }
167
+ end
168
+
169
+ pattern '/foo?' do
170
+ it { should match('/fo') }
171
+ it { should match('/foo') }
172
+ it { should_not match('') }
173
+ it { should_not match('/') }
174
+ it { should_not match('/f') }
175
+ it { should_not match('/fooo') }
176
+ end
177
+
178
+ pattern '/:fOO' do
179
+ it { should match('/a').capturing fOO: 'a' }
180
+ end
181
+
182
+ pattern '/:_X' do
183
+ it { should match('/a').capturing _X: 'a' }
184
+ end
185
+
186
+ pattern '/:f00' do
187
+ it { should match('/a').capturing f00: 'a' }
188
+ end
189
+
190
+ pattern '/:foo.?' do
191
+ it { should match('/a.').capturing foo: 'a.' }
192
+ it { should match('/xy').capturing foo: 'xy' }
193
+ end
194
+
195
+ pattern '/(a)' do
196
+ it { should match('/(a)') }
197
+ it { should_not match('/a') }
198
+ end
199
+
200
+ pattern '/:foo.?', greedy: false do
201
+ it { should match('/a.').capturing foo: 'a' }
202
+ it { should match('/xy').capturing foo: 'xy' }
203
+ end
204
+
205
+ pattern '/foo?', uri_decode: false do
206
+ it { should match('/foo') }
207
+ it { should match('/fo') }
208
+ it { should_not match('/foo?') }
209
+ end
210
+
211
+ pattern '/foo/bar', uri_decode: false do
212
+ it { should match('/foo/bar') }
213
+ it { should_not match('/foo%2Fbar') }
214
+ it { should_not match('/foo%2fbar') }
215
+ end
216
+
217
+ pattern "/path with spaces", uri_decode: false do
218
+ it { should match('/path with spaces') }
219
+ it { should_not match('/path%20with%20spaces') }
220
+ it { should_not match('/path%2Bwith%2Bspaces') }
221
+ it { should_not match('/path+with+spaces') }
222
+ end
223
+
224
+ pattern "/path with spaces", space_matches_plus: false do
225
+ it { should match('/path%20with%20spaces') }
226
+ it { should_not match('/path%2Bwith%2Bspaces') }
227
+ it { should_not match('/path+with+spaces') }
228
+ end
229
+
230
+ context 'error handling' do
231
+ example '? at beginning of route' do
232
+ expect { Mustermann::Simple.new('?foobar') }.
233
+ to raise_error(Mustermann::ParseError)
234
+ end
235
+
236
+ example 'invalid capture name' do
237
+ expect { Mustermann::Simple.new('/:1a/') }.
238
+ to raise_error(Mustermann::CompileError)
239
+ end
240
+ end
241
+
242
+ context "peeking" do
243
+ subject(:pattern) { Mustermann::Simple.new(":name") }
244
+
245
+ describe :peek_size do
246
+ example { pattern.peek_size("foo bar/blah") .should be == "foo bar".size }
247
+ example { pattern.peek_size("foo%20bar/blah") .should be == "foo%20bar".size }
248
+ example { pattern.peek_size("/foo bar") .should be_nil }
249
+ end
250
+
251
+ describe :peek_match do
252
+ example { pattern.peek_match("foo bar/blah") .to_s .should be == "foo bar" }
253
+ example { pattern.peek_match("foo%20bar/blah") .to_s .should be == "foo%20bar" }
254
+ example { pattern.peek_match("/foo bar") .should be_nil }
255
+ end
256
+
257
+ describe :peek_params do
258
+ example { pattern.peek_params("foo bar/blah") .should be == [{"name" => "foo bar"}, "foo bar".size] }
259
+ example { pattern.peek_params("foo%20bar/blah") .should be == [{"name" => "foo bar"}, "foo%20bar".size] }
260
+ example { pattern.peek_params("/foo bar") .should be_nil }
261
+ end
262
+ end
263
+
264
+ context "highlighting" do
265
+ let(:pattern) { Mustermann::Simple.new("/:name?/*") }
266
+ subject(:sexp) { Mustermann::Visualizer.highlight(pattern).to_sexp }
267
+ it { should be == "(root (separator /) (capture : (name name)) (optional ?) (separator /) (splat *))" }
268
+ end
269
+ end
@@ -0,0 +1,272 @@
1
+ # frozen_string_literal: true
2
+ require 'support'
3
+ require 'mustermann/string_scanner'
4
+
5
+ describe Mustermann::StringScanner do
6
+ include Support::ScanMatcher
7
+
8
+ subject(:scanner) { Mustermann::StringScanner.new(example_string) }
9
+ let(:example_string) { "foo bar" }
10
+
11
+ describe :scan do
12
+ it { should scan("foo") }
13
+ it { should scan(/foo/) }
14
+ it { should scan(:name) }
15
+ it { should scan(":name") }
16
+
17
+ it { should_not scan(" ") }
18
+ it { should_not scan("bar") }
19
+
20
+ example do
21
+ should scan("foo")
22
+ should scan(" ")
23
+ should scan("bar")
24
+ end
25
+
26
+ example do
27
+ scanner.position = 4
28
+ should scan("bar")
29
+ end
30
+
31
+ example do
32
+ should scan("foo")
33
+ scanner.reset
34
+ should scan("foo")
35
+ end
36
+ end
37
+
38
+ describe :check do
39
+ it { should check("foo") }
40
+ it { should check(/foo/) }
41
+ it { should check(:name) }
42
+ it { should check(":name") }
43
+
44
+ it { should_not check(" ") }
45
+ it { should_not check("bar") }
46
+
47
+ example do
48
+ should check("foo")
49
+ should_not check(" ")
50
+ should_not check("bar")
51
+ should check("foo")
52
+ end
53
+
54
+ example do
55
+ scanner.position = 4
56
+ should check("bar")
57
+ end
58
+ end
59
+
60
+ describe :scan_until do
61
+ it { should scan_until("foo") }
62
+ it { should scan_until(":name") }
63
+ it { should scan_until(" ") }
64
+ it { should scan_until("bar") }
65
+ it { should_not scan_until("baz") }
66
+
67
+ example do
68
+ should scan_until(" ")
69
+ should check("bar")
70
+ end
71
+
72
+ example do
73
+ should scan_until(" ")
74
+ scanner.reset
75
+ should scan("foo")
76
+ end
77
+ end
78
+
79
+ describe :check_until do
80
+ it { should check_until("foo") }
81
+ it { should check_until(":name") }
82
+ it { should check_until(" ") }
83
+ it { should check_until("bar") }
84
+ it { should_not check_until("baz") }
85
+
86
+ example do
87
+ should check_until(" ")
88
+ should_not check("bar")
89
+ end
90
+ end
91
+
92
+ describe :getch do
93
+ example { scanner.getch.should be == "f" }
94
+
95
+ example do
96
+ scanner.scan("foo")
97
+ scanner.getch.should be == " "
98
+ should scan("bar")
99
+ end
100
+
101
+ example do
102
+ scanner.getch
103
+ scanner.reset
104
+ should scan("foo")
105
+ end
106
+ end
107
+
108
+ describe :<< do
109
+ example do
110
+ should_not scan_until("baz")
111
+ scanner << " baz"
112
+ scanner.to_s.should be == "foo bar baz"
113
+ should scan_until("baz")
114
+ end
115
+ end
116
+
117
+ describe :eos? do
118
+ it { should_not be_eos }
119
+ example do
120
+ scanner.position = 7
121
+ should be_eos
122
+ end
123
+ end
124
+
125
+ describe :beginning_of_line? do
126
+ let(:example_string) { "foo\nbar" }
127
+ it { should be_beginning_of_line }
128
+
129
+ example do
130
+ scanner.position = 2
131
+ should_not be_beginning_of_line
132
+ end
133
+
134
+ example do
135
+ scanner.position = 3
136
+ should_not be_beginning_of_line
137
+ end
138
+
139
+ example do
140
+ scanner.position = 4
141
+ should be_beginning_of_line
142
+ end
143
+ end
144
+
145
+ describe :rest do
146
+ example { scanner.rest.should be == "foo bar" }
147
+ example do
148
+ scanner.position = 4
149
+ scanner.rest.should be == "bar"
150
+ end
151
+ end
152
+
153
+ describe :rest_size do
154
+ example { scanner.rest_size.should be == 7 }
155
+ example do
156
+ scanner.position = 4
157
+ scanner.rest_size.should be == 3
158
+ end
159
+ end
160
+
161
+ describe :peek do
162
+ example { scanner.peek(3).should be == "foo" }
163
+
164
+ example do
165
+ scanner.peek(3).should be == "foo"
166
+ scanner.peek(3).should be == "foo"
167
+ end
168
+
169
+ example do
170
+ scanner.position = 4
171
+ scanner.peek(3).should be == "bar"
172
+ end
173
+ end
174
+
175
+ describe :inspect do
176
+ example { scanner.inspect.should be == '#<Mustermann::StringScanner 0/7 @ "foo bar">' }
177
+ example do
178
+ scanner.position = 4
179
+ scanner.inspect.should be == '#<Mustermann::StringScanner 4/7 @ "foo bar">'
180
+ end
181
+ end
182
+
183
+ describe :[] do
184
+ example do
185
+ should scan(:name)
186
+ scanner['name'].should be == "foo bar"
187
+ end
188
+
189
+ example do
190
+ should scan(:name, capture: /\S+/)
191
+ scanner['name'].should be == "foo"
192
+ should scan(" :name", capture: /\S+/)
193
+ scanner['name'].should be == "bar"
194
+ end
195
+
196
+ example do
197
+ should scan(":a", capture: /\S+/)
198
+ should scan(" :b", capture: /\S+/)
199
+ scanner['a'].should be == "foo"
200
+ scanner['b'].should be == "bar"
201
+ end
202
+
203
+ example do
204
+ a = scanner.scan(":a", capture: /\S+/)
205
+ b = scanner.scan(" :b", capture: /\S+/)
206
+ a.params['a'].should be == 'foo'
207
+ b.params['b'].should be == 'bar'
208
+ a.params['b'].should be_nil
209
+ b.params['a'].should be_nil
210
+ end
211
+
212
+ example do
213
+ result = scanner.check(":a", capture: /\S+/)
214
+ result.params['a'].should be == 'foo'
215
+ scanner['a'].should be_nil
216
+ end
217
+
218
+ example do
219
+ should scan(:name)
220
+ scanner.reset
221
+ scanner['name'].should be_nil
222
+ end
223
+ end
224
+
225
+ describe :unscan do
226
+ example do
227
+ should scan(:name, capture: /\S+/)
228
+ scanner['name'].should be == "foo"
229
+ should scan(" :name", capture: /\S+/)
230
+ scanner['name'].should be == "bar"
231
+ scanner.unscan
232
+ scanner['name'].should be == "foo"
233
+ scanner.rest.should be == " bar"
234
+ end
235
+
236
+ example do
237
+ should scan_until(" ")
238
+ scanner.unscan
239
+ scanner.rest.should be == "foo bar"
240
+ end
241
+
242
+ example do
243
+ expect { scanner.unscan }.to raise_error(Mustermann::StringScanner::ScanError,
244
+ 'unscan failed: previous match record not exist')
245
+ end
246
+ end
247
+
248
+ describe :terminate do
249
+ example do
250
+ scanner.terminate
251
+ scanner.should be_eos
252
+ end
253
+ end
254
+
255
+ describe :to_h do
256
+ example { scanner.to_h.should be == {} }
257
+ example do
258
+ end
259
+ end
260
+
261
+ describe :to_s do
262
+ example { scanner.to_s.should be == "foo bar" }
263
+ end
264
+
265
+ describe :clear_cache do
266
+ example do
267
+ scanner.scan("foo")
268
+ Mustermann::StringScanner.clear_cache
269
+ Mustermann::StringScanner.cache_size.should be == 0
270
+ end
271
+ end
272
+ end