mustermann19 0.3.1 → 0.3.1.1

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 (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