mustermann19 0.3.1 → 0.3.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +4 -3
  3. data/README.md +680 -376
  4. data/lib/mustermann/ast/compiler.rb +13 -7
  5. data/lib/mustermann/ast/expander.rb +11 -5
  6. data/lib/mustermann/ast/node.rb +27 -1
  7. data/lib/mustermann/ast/param_scanner.rb +20 -0
  8. data/lib/mustermann/ast/parser.rb +131 -12
  9. data/lib/mustermann/ast/pattern.rb +45 -6
  10. data/lib/mustermann/ast/template_generator.rb +28 -0
  11. data/lib/mustermann/ast/validation.rb +5 -3
  12. data/lib/mustermann/composite.rb +103 -0
  13. data/lib/mustermann/expander.rb +1 -1
  14. data/lib/mustermann/express.rb +34 -0
  15. data/lib/mustermann/flask.rb +204 -0
  16. data/lib/mustermann/identity.rb +54 -0
  17. data/lib/mustermann/pattern.rb +186 -12
  18. data/lib/mustermann/pattern_cache.rb +49 -0
  19. data/lib/mustermann/pyramid.rb +25 -0
  20. data/lib/mustermann/regexp_based.rb +18 -1
  21. data/lib/mustermann/regular.rb +1 -1
  22. data/lib/mustermann/shell.rb +8 -0
  23. data/lib/mustermann/simple.rb +1 -1
  24. data/lib/mustermann/simple_match.rb +5 -0
  25. data/lib/mustermann/sinatra.rb +19 -5
  26. data/lib/mustermann/string_scanner.rb +314 -0
  27. data/lib/mustermann/template.rb +10 -0
  28. data/lib/mustermann/to_pattern.rb +11 -6
  29. data/lib/mustermann/version.rb +1 -1
  30. data/lib/mustermann.rb +52 -3
  31. data/mustermann.gemspec +1 -1
  32. data/spec/composite_spec.rb +147 -0
  33. data/spec/expander_spec.rb +15 -0
  34. data/spec/express_spec.rb +209 -0
  35. data/spec/flask_spec.rb +361 -0
  36. data/spec/flask_subclass_spec.rb +368 -0
  37. data/spec/identity_spec.rb +44 -0
  38. data/spec/mustermann_spec.rb +14 -0
  39. data/spec/pattern_spec.rb +7 -3
  40. data/spec/pyramid_spec.rb +101 -0
  41. data/spec/rails_spec.rb +76 -2
  42. data/spec/regular_spec.rb +25 -0
  43. data/spec/shell_spec.rb +33 -0
  44. data/spec/simple_spec.rb +25 -0
  45. data/spec/sinatra_spec.rb +184 -9
  46. data/spec/string_scanner_spec.rb +271 -0
  47. data/spec/support/expand_matcher.rb +7 -5
  48. data/spec/support/generate_template_matcher.rb +27 -0
  49. data/spec/support/pattern.rb +3 -0
  50. data/spec/support/scan_matcher.rb +63 -0
  51. data/spec/support.rb +2 -1
  52. data/spec/template_spec.rb +22 -0
  53. data/spec/to_pattern_spec.rb +49 -0
  54. metadata +47 -61
  55. data/internals.md +0 -64
@@ -0,0 +1,271 @@
1
+ require 'support'
2
+ require 'mustermann/string_scanner'
3
+
4
+ describe Mustermann::StringScanner do
5
+ include Support::ScanMatcher
6
+
7
+ subject(:scanner) { Mustermann::StringScanner.new(example_string) }
8
+ let(:example_string) { "foo bar" }
9
+
10
+ describe :scan do
11
+ it { should scan("foo") }
12
+ it { should scan(/foo/) }
13
+ it { should scan(:name) }
14
+ it { should scan(":name") }
15
+
16
+ it { should_not scan(" ") }
17
+ it { should_not scan("bar") }
18
+
19
+ example do
20
+ should scan("foo")
21
+ should scan(" ")
22
+ should scan("bar")
23
+ end
24
+
25
+ example do
26
+ scanner.position = 4
27
+ should scan("bar")
28
+ end
29
+
30
+ example do
31
+ should scan("foo")
32
+ scanner.reset
33
+ should scan("foo")
34
+ end
35
+ end
36
+
37
+ describe :check do
38
+ it { should check("foo") }
39
+ it { should check(/foo/) }
40
+ it { should check(:name) }
41
+ it { should check(":name") }
42
+
43
+ it { should_not check(" ") }
44
+ it { should_not check("bar") }
45
+
46
+ example do
47
+ should check("foo")
48
+ should_not check(" ")
49
+ should_not check("bar")
50
+ should check("foo")
51
+ end
52
+
53
+ example do
54
+ scanner.position = 4
55
+ should check("bar")
56
+ end
57
+ end
58
+
59
+ describe :scan_until do
60
+ it { should scan_until("foo") }
61
+ it { should scan_until(":name") }
62
+ it { should scan_until(" ") }
63
+ it { should scan_until("bar") }
64
+ it { should_not scan_until("baz") }
65
+
66
+ example do
67
+ should scan_until(" ")
68
+ should check("bar")
69
+ end
70
+
71
+ example do
72
+ should scan_until(" ")
73
+ scanner.reset
74
+ should scan("foo")
75
+ end
76
+ end
77
+
78
+ describe :check_until do
79
+ it { should check_until("foo") }
80
+ it { should check_until(":name") }
81
+ it { should check_until(" ") }
82
+ it { should check_until("bar") }
83
+ it { should_not check_until("baz") }
84
+
85
+ example do
86
+ should check_until(" ")
87
+ should_not check("bar")
88
+ end
89
+ end
90
+
91
+ describe :getch do
92
+ example { scanner.getch.should be == "f" }
93
+
94
+ example do
95
+ scanner.scan("foo")
96
+ scanner.getch.should be == " "
97
+ should scan("bar")
98
+ end
99
+
100
+ example do
101
+ scanner.getch
102
+ scanner.reset
103
+ should scan("foo")
104
+ end
105
+ end
106
+
107
+ describe :<< do
108
+ example do
109
+ should_not scan_until("baz")
110
+ scanner << " baz"
111
+ scanner.to_s.should be == "foo bar baz"
112
+ should scan_until("baz")
113
+ end
114
+ end
115
+
116
+ describe :eos? do
117
+ it { should_not be_eos }
118
+ example do
119
+ scanner.position = 7
120
+ should be_eos
121
+ end
122
+ end
123
+
124
+ describe :beginning_of_line? do
125
+ let(:example_string) { "foo\nbar" }
126
+ it { should be_beginning_of_line }
127
+
128
+ example do
129
+ scanner.position = 2
130
+ should_not be_beginning_of_line
131
+ end
132
+
133
+ example do
134
+ scanner.position = 3
135
+ should_not be_beginning_of_line
136
+ end
137
+
138
+ example do
139
+ scanner.position = 4
140
+ should be_beginning_of_line
141
+ end
142
+ end
143
+
144
+ describe :rest do
145
+ example { scanner.rest.should be == "foo bar" }
146
+ example do
147
+ scanner.position = 4
148
+ scanner.rest.should be == "bar"
149
+ end
150
+ end
151
+
152
+ describe :rest_size do
153
+ example { scanner.rest_size.should be == 7 }
154
+ example do
155
+ scanner.position = 4
156
+ scanner.rest_size.should be == 3
157
+ end
158
+ end
159
+
160
+ describe :peek do
161
+ example { scanner.peek(3).should be == "foo" }
162
+
163
+ example do
164
+ scanner.peek(3).should be == "foo"
165
+ scanner.peek(3).should be == "foo"
166
+ end
167
+
168
+ example do
169
+ scanner.position = 4
170
+ scanner.peek(3).should be == "bar"
171
+ end
172
+ end
173
+
174
+ describe :inspect do
175
+ example { scanner.inspect.should be == '#<Mustermann::StringScanner 0/7 @ "foo bar">' }
176
+ example do
177
+ scanner.position = 4
178
+ scanner.inspect.should be == '#<Mustermann::StringScanner 4/7 @ "foo bar">'
179
+ end
180
+ end
181
+
182
+ describe :[] do
183
+ example do
184
+ should scan(:name)
185
+ scanner['name'].should be == "foo bar"
186
+ end
187
+
188
+ example do
189
+ should scan(:name, capture: /\S+/)
190
+ scanner['name'].should be == "foo"
191
+ should scan(" :name", capture: /\S+/)
192
+ scanner['name'].should be == "bar"
193
+ end
194
+
195
+ example do
196
+ should scan(":a", capture: /\S+/)
197
+ should scan(" :b", capture: /\S+/)
198
+ scanner['a'].should be == "foo"
199
+ scanner['b'].should be == "bar"
200
+ end
201
+
202
+ example do
203
+ a = scanner.scan(":a", capture: /\S+/)
204
+ b = scanner.scan(" :b", capture: /\S+/)
205
+ a.params['a'].should be == 'foo'
206
+ b.params['b'].should be == 'bar'
207
+ a.params['b'].should be_nil
208
+ b.params['a'].should be_nil
209
+ end
210
+
211
+ example do
212
+ result = scanner.check(":a", capture: /\S+/)
213
+ result.params['a'].should be == 'foo'
214
+ scanner['a'].should be_nil
215
+ end
216
+
217
+ example do
218
+ should scan(:name)
219
+ scanner.reset
220
+ scanner['name'].should be_nil
221
+ end
222
+ end
223
+
224
+ describe :unscan do
225
+ example do
226
+ should scan(:name, capture: /\S+/)
227
+ scanner['name'].should be == "foo"
228
+ should scan(" :name", capture: /\S+/)
229
+ scanner['name'].should be == "bar"
230
+ scanner.unscan
231
+ scanner['name'].should be == "foo"
232
+ scanner.rest.should be == " bar"
233
+ end
234
+
235
+ example do
236
+ should scan_until(" ")
237
+ scanner.unscan
238
+ scanner.rest.should be == "foo bar"
239
+ end
240
+
241
+ example do
242
+ expect { scanner.unscan }.to raise_error(Mustermann::StringScanner::ScanError,
243
+ 'unscan failed: previous match record not exist')
244
+ end
245
+ end
246
+
247
+ describe :terminate do
248
+ example do
249
+ scanner.terminate
250
+ scanner.should be_eos
251
+ end
252
+ end
253
+
254
+ describe :to_h do
255
+ example { scanner.to_h.should be == {} }
256
+ example do
257
+ end
258
+ end
259
+
260
+ describe :to_s do
261
+ example { scanner.to_s.should be == "foo bar" }
262
+ end
263
+
264
+ describe :clear_cache do
265
+ example do
266
+ scanner.scan("foo")
267
+ Mustermann::StringScanner.clear_cache
268
+ Mustermann::StringScanner.cache_size.should be == 0
269
+ end
270
+ end
271
+ end
@@ -1,8 +1,9 @@
1
- RSpec::Matchers.define :expand do |values = {}|
1
+ RSpec::Matchers.define :expand do |behavior = nil, values = {}|
2
+ values, behavior = behavior, nil if behavior.kind_of?(Hash)
2
3
  match do |pattern|
3
4
  @string ||= nil
4
5
  begin
5
- expanded = pattern.expand(values)
6
+ expanded = pattern.expand(behavior, values)
6
7
  rescue Exception
7
8
  false
8
9
  else
@@ -15,8 +16,9 @@ RSpec::Matchers.define :expand do |values = {}|
15
16
  end
16
17
 
17
18
  failure_message do |pattern|
18
- message = "expected %p to be expandable with %p" % [pattern, values]
19
- expanded = pattern.expand(values)
19
+ message = "expected %p to be expandable with %p" % [pattern, values]
20
+ message << " (%p behavior)" % behavior if behavior
21
+ expanded = pattern.expand(behavior, values)
20
22
  message << " and result in %p, but got %p" % [@string, expanded] if @string
21
23
  message
22
24
  end
@@ -24,4 +26,4 @@ RSpec::Matchers.define :expand do |values = {}|
24
26
  failure_message_when_negated do |pattern|
25
27
  "expected %p not to be expandable with %p" % [pattern, values]
26
28
  end
27
- end
29
+ end
@@ -0,0 +1,27 @@
1
+ RSpec::Matchers.define :generate_template do |template|
2
+ match { |pattern| pattern.to_templates.include? template }
3
+
4
+ failure_message do |pattern|
5
+ "expected %p to generate template %p, but only generated %s" % [
6
+ pattern, template, pattern.to_templates.map(&:inspect).join(', ')
7
+ ]
8
+ end
9
+
10
+ failure_message_when_negated do |pattern|
11
+ "expected %p not to generate template %p" % [ pattern, template ]
12
+ end
13
+ end
14
+
15
+ RSpec::Matchers.define :generate_templates do |*templates|
16
+ match { |pattern| pattern.to_templates.sort == templates.sort }
17
+
18
+ failure_message do |pattern|
19
+ "expected %p to generate templates %p, but generated %p" % [
20
+ pattern, templates.sort, pattern.to_templates.sort
21
+ ]
22
+ end
23
+
24
+ failure_message_when_negated do |pattern|
25
+ "expected %p not to generate templates %p" % [ pattern, templates ]
26
+ end
27
+ end
@@ -2,6 +2,8 @@ require 'timeout'
2
2
 
3
3
  module Support
4
4
  module Pattern
5
+ extend RSpec::Matchers::DSL
6
+
5
7
  def pattern(pattern, options = nil, &block)
6
8
  description = "pattern %p" % pattern
7
9
 
@@ -17,6 +19,7 @@ module Support
17
19
  its(:to_s) { should be == pattern }
18
20
  its(:inspect) { should be == "#<#{described_class}:#{pattern.inspect}>" }
19
21
  its(:names) { should be_an(Array) }
22
+ its(:to_templates) { should be == [pattern] } if described_class.name == "Mustermann::Template"
20
23
 
21
24
  example 'string should be immune to external change' do
22
25
  subject.to_s.replace "NOT THE PATTERN"
@@ -0,0 +1,63 @@
1
+ module Support
2
+ module ScanMatcher
3
+ extend RSpec::Matchers::DSL
4
+
5
+ def scan(pattern, options = {})
6
+ give_scan_result(:scan, pattern, options)
7
+ end
8
+
9
+ def check(pattern, options = {})
10
+ give_scan_result(:check, pattern, options)
11
+ end
12
+
13
+ def scan_until(pattern, options = {})
14
+ give_scan_result(:scan_until, pattern, options)
15
+ end
16
+
17
+ def check_until(pattern, options = {})
18
+ give_scan_result(:check_until, pattern, options)
19
+ end
20
+
21
+ matcher :give_scan_result do |method_name, pattern, options = {}|
22
+ def result_expectations
23
+ @result_expectations ||= []
24
+ end
25
+
26
+ def expect_result(description, expected, &block)
27
+ result_expectations << Proc.new do |result|
28
+ if !block.call(result)
29
+ "expected %p to %s %p matching %s" % [ result.scanner, method_name, pattern, description ]
30
+ end
31
+ end
32
+ end
33
+
34
+ match do |scanner|
35
+ scanned = scanner.public_send(method_name, pattern, options)
36
+ scanned and result_expectations.all? { |e| !e.call(scanned) }
37
+ end
38
+
39
+ chain(:matching_substring) do |substring|
40
+ expected_result("the substring %p" % substring) { |r| r.to_s == substring }
41
+ end
42
+
43
+ chain(:matching_length) do |length|
44
+ expected_result("%d characters" % length) { |r| r.length == length }
45
+ end
46
+
47
+ chain(:matching_params) do |params|
48
+ expected_result("with params %p" % [params]) { |r| r.params == params }
49
+ end
50
+
51
+ failure_message do |scanner|
52
+ if scanned = scanner.public_send(method_name, pattern, options = {})
53
+ message = result_expectations.inject(nil) { |m,e| m || e.call(scanned) }
54
+ end
55
+ message || "expected %p to %s %p" % [ scanner, method_name, pattern ]
56
+ end
57
+
58
+ failure_message_when_negated do |scanner|
59
+ "expected %p not to %s %p" % [ scanner, method_name, pattern ]
60
+ end
61
+ end
62
+ end
63
+ end
data/spec/support.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'support/env'
2
- require 'support/coverage'
3
2
  require 'support/expand_matcher'
3
+ require 'support/generate_template_matcher'
4
4
  require 'support/match_matcher'
5
5
  require 'support/pattern'
6
+ require 'support/scan_matcher'
@@ -8,6 +8,9 @@ describe Mustermann::Template do
8
8
  pattern '' do
9
9
  it { should match('') }
10
10
  it { should_not match('/') }
11
+
12
+ it { should respond_to(:expand) }
13
+ it { should respond_to(:to_templates) }
11
14
  end
12
15
 
13
16
  pattern '/' do
@@ -812,4 +815,23 @@ describe Mustermann::Template do
812
815
  to raise_error(Mustermann::ParseError, 'unexpected end of string while parsing "foo{bar"')
813
816
  end
814
817
  end
818
+
819
+ context "peeking" do
820
+ subject(:pattern) { Mustermann::Template.new("{name}bar") }
821
+
822
+ describe :peek_size do
823
+ example { pattern.peek_size("foo%20bar/blah") .should be == "foo%20bar".size }
824
+ example { pattern.peek_size("/foo bar") .should be_nil }
825
+ end
826
+
827
+ describe :peek_match do
828
+ example { pattern.peek_match("foo%20bar/blah") .to_s .should be == "foo%20bar" }
829
+ example { pattern.peek_match("/foo bar") .should be_nil }
830
+ end
831
+
832
+ describe :peek_params do
833
+ example { pattern.peek_params("foo%20bar/blah") .should be == [{"name" => "foo "}, "foo%20bar".size] }
834
+ example { pattern.peek_params("/foo bar") .should be_nil }
835
+ end
836
+ end
815
837
  end
@@ -1,5 +1,6 @@
1
1
  require 'support'
2
2
  require 'mustermann/to_pattern'
3
+ require 'delegate'
3
4
 
4
5
  describe Mustermann::ToPattern do
5
6
  context String do
@@ -12,9 +13,57 @@ describe Mustermann::ToPattern do
12
13
  example { //.to_pattern(type: :rails) .should be_a(Mustermann::Regular) }
13
14
  end
14
15
 
16
+ context Symbol do
17
+ example { :foo.to_pattern .should be_a(Mustermann::Sinatra) }
18
+ example { :foo.to_pattern(type: :rails) .should be_a(Mustermann::Sinatra) }
19
+ end
20
+
21
+ context Array do
22
+ example { [:foo, :bar].to_pattern .should be_a(Mustermann::Composite) }
23
+ example { [:foo, :bar].to_pattern(type: :rails) .should be_a(Mustermann::Composite) }
24
+ end
25
+
15
26
  context Mustermann::Pattern do
16
27
  subject(:pattern) { Mustermann.new('') }
17
28
  example { pattern.to_pattern.should be == pattern }
18
29
  example { pattern.to_pattern(type: :rails).should be_a(Mustermann::Sinatra) }
19
30
  end
31
+
32
+ context 'custom class' do
33
+ let(:example_class) do
34
+ Class.new do
35
+ include Mustermann::ToPattern
36
+ def to_s
37
+ ":foo/:bar"
38
+ end
39
+ end
40
+ end
41
+
42
+ example { example_class.new.to_pattern .should be_a(Mustermann::Sinatra) }
43
+ example { example_class.new.to_pattern(type: :rails) .should be_a(Mustermann::Rails) }
44
+ example { Mustermann.new(example_class.new) .should be_a(Mustermann::Sinatra) }
45
+ example { Mustermann.new(example_class.new, type: :rails) .should be_a(Mustermann::Rails) }
46
+ end
47
+
48
+ context 'primitive delegate' do
49
+ let(:example_class) do
50
+ Class.new(DelegateClass(Array)) do
51
+ include Mustermann::ToPattern
52
+ end
53
+ end
54
+
55
+ example { example_class.new([:foo, :bar]).to_pattern .should be_a(Mustermann::Composite) }
56
+ example { example_class.new([:foo, :bar]).to_pattern(type: :rails) .should be_a(Mustermann::Composite) }
57
+ end
58
+
59
+ context 'primitive subclass' do
60
+ let(:example_class) do
61
+ Class.new(Array) do
62
+ include Mustermann::ToPattern
63
+ end
64
+ end
65
+
66
+ example { example_class.new([:foo, :bar]).to_pattern .should be_a(Mustermann::Composite) }
67
+ example { example_class.new([:foo, :bar]).to_pattern(type: :rails) .should be_a(Mustermann::Composite) }
68
+ end
20
69
  end