mustermann-contrib 1.0.0.beta2

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/README.md +1239 -0
  3. data/examples/highlighting.rb +35 -0
  4. data/highlighting.png +0 -0
  5. data/irb.png +0 -0
  6. data/lib/mustermann/cake.rb +18 -0
  7. data/lib/mustermann/express.rb +37 -0
  8. data/lib/mustermann/file_utils.rb +217 -0
  9. data/lib/mustermann/file_utils/glob_pattern.rb +39 -0
  10. data/lib/mustermann/fileutils.rb +1 -0
  11. data/lib/mustermann/flask.rb +198 -0
  12. data/lib/mustermann/grape.rb +35 -0
  13. data/lib/mustermann/pyramid.rb +28 -0
  14. data/lib/mustermann/rails.rb +46 -0
  15. data/lib/mustermann/shell.rb +56 -0
  16. data/lib/mustermann/simple.rb +50 -0
  17. data/lib/mustermann/string_scanner.rb +313 -0
  18. data/lib/mustermann/strscan.rb +1 -0
  19. data/lib/mustermann/template.rb +62 -0
  20. data/lib/mustermann/uri_template.rb +1 -0
  21. data/lib/mustermann/versions.rb +46 -0
  22. data/lib/mustermann/visualizer.rb +38 -0
  23. data/lib/mustermann/visualizer/highlight.rb +137 -0
  24. data/lib/mustermann/visualizer/highlighter.rb +37 -0
  25. data/lib/mustermann/visualizer/highlighter/ad_hoc.rb +94 -0
  26. data/lib/mustermann/visualizer/highlighter/ast.rb +102 -0
  27. data/lib/mustermann/visualizer/highlighter/composite.rb +45 -0
  28. data/lib/mustermann/visualizer/highlighter/dummy.rb +18 -0
  29. data/lib/mustermann/visualizer/highlighter/regular.rb +104 -0
  30. data/lib/mustermann/visualizer/pattern_extension.rb +68 -0
  31. data/lib/mustermann/visualizer/renderer/ansi.rb +23 -0
  32. data/lib/mustermann/visualizer/renderer/generic.rb +46 -0
  33. data/lib/mustermann/visualizer/renderer/hansi_template.rb +34 -0
  34. data/lib/mustermann/visualizer/renderer/html.rb +50 -0
  35. data/lib/mustermann/visualizer/renderer/sexp.rb +37 -0
  36. data/lib/mustermann/visualizer/tree.rb +63 -0
  37. data/lib/mustermann/visualizer/tree_renderer.rb +78 -0
  38. data/mustermann-contrib.gemspec +19 -0
  39. data/spec/cake_spec.rb +90 -0
  40. data/spec/express_spec.rb +209 -0
  41. data/spec/file_utils_spec.rb +119 -0
  42. data/spec/flask_spec.rb +361 -0
  43. data/spec/flask_subclass_spec.rb +368 -0
  44. data/spec/grape_spec.rb +747 -0
  45. data/spec/pattern_extension_spec.rb +49 -0
  46. data/spec/pyramid_spec.rb +101 -0
  47. data/spec/rails_spec.rb +647 -0
  48. data/spec/shell_spec.rb +147 -0
  49. data/spec/simple_spec.rb +268 -0
  50. data/spec/string_scanner_spec.rb +271 -0
  51. data/spec/template_spec.rb +841 -0
  52. data/spec/visualizer_spec.rb +199 -0
  53. data/theme.png +0 -0
  54. data/tree.png +0 -0
  55. metadata +126 -0
@@ -0,0 +1,78 @@
1
+ require 'mustermann/visualizer/tree'
2
+ require 'mustermann/ast/translator'
3
+ require 'hansi'
4
+
5
+ module Mustermann
6
+ module Visualizer
7
+ # Turns an AST into a Tree
8
+ # @!visibility private
9
+ class TreeRenderer < AST::Translator
10
+ TEMPLATE = '"<base01>%s</base01><underline><green>%s</green></underline><base01>%s</base01>" '
11
+ THEME = Hansi::Theme[:solarized]
12
+ PREFIX_COLOR = THEME[:violet]
13
+ FakeNode = Struct.new(:type, :start, :stop, :length)
14
+ private_constant(:TEMPLATE, :THEME, :PREFIX_COLOR, :FakeNode)
15
+
16
+ # Takes a pattern (or pattern string and option) and turns it into a tree.
17
+ # Runs translation if pattern implements to_ast, otherwise returns single
18
+ # node tree.
19
+ #
20
+ # @!visibility private
21
+ def self.render(pattern, **options)
22
+ pattern &&= Mustermann.new(pattern, **options)
23
+ renderer = new(pattern.to_s)
24
+ if pattern.respond_to? :to_ast
25
+ renderer.translate(pattern.to_ast)
26
+ else
27
+ length = renderer.string.length
28
+ node = FakeNode.new("pattern (not AST based)", 0, length, length)
29
+ renderer.tree(node)
30
+ end
31
+ end
32
+
33
+ # @!visibility private
34
+ attr_reader :string
35
+
36
+ # @!visibility private
37
+ def initialize(string)
38
+ @string = string
39
+ end
40
+
41
+ # access a substring of the pattern, in inspect mode
42
+ # @!visibility private
43
+ def sub(*args)
44
+ string[*args].inspect[1..-2]
45
+ end
46
+
47
+ # creates a tree node
48
+ # @!visibility private
49
+ def tree(node, *children, **typed_children)
50
+ children += children_for(typed_children)
51
+ children = children.flatten.grep(Tree)
52
+ infos = sub(0, node.start), sub(node.start, node.length), sub(node.stop..-1)
53
+ description = Hansi.render(THEME[:green], node.type.to_s.tr("_", " "))
54
+ after = Hansi.render(TEMPLATE, *infos, theme: THEME, tags: true)
55
+ Tree.new(description, *children, after: after, prefix_color: PREFIX_COLOR)
56
+ end
57
+
58
+ # Take a hash with trees as values and turn the keys into trees, too.
59
+ # Read again if that didn't make sense.
60
+ # @!visibility private
61
+ def children_for(list)
62
+ list.map do |key, value|
63
+ value = Array(value).flatten
64
+ if value.any?
65
+ after = " " * string.inspect.length + " "
66
+ description = Hansi.render(THEME[:orange], key.to_s)
67
+ Tree.new(description, *value, after: after, prefix_color: PREFIX_COLOR)
68
+ end
69
+ end
70
+ end
71
+
72
+ translate(:node) { t.tree(node, payload: t(payload)) }
73
+ translate(:with_look_ahead) { t.tree(node, head: t(head), payload: t(payload)) }
74
+ translate(Array) { map { |e| t(e) }}
75
+ translate(Object) { }
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,19 @@
1
+ $:.unshift File.expand_path("../../mustermann/lib", __FILE__)
2
+ require "mustermann/version"
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "mustermann-contrib"
6
+ s.version = Mustermann::VERSION
7
+ s.authors = ["Konstantin Haase", "Zachary Scott"]
8
+ s.email = "mail@zzak.io"
9
+ s.homepage = "https://github.com/sinatra/mustermann"
10
+ s.summary = %q{Collection of extensions for Mustermann}
11
+ s.description = %q{Adds many plugins to Mustermman}
12
+ s.license = 'MIT'
13
+ s.required_ruby_version = '>= 2.2.0'
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.add_dependency 'mustermann', Mustermann::VERSION
18
+ s.add_dependency 'hansi', '~> 0.2.0'
19
+ end
data/spec/cake_spec.rb ADDED
@@ -0,0 +1,90 @@
1
+ require 'support'
2
+ require 'mustermann/cake'
3
+
4
+ describe Mustermann::Cake do
5
+ extend Support::Pattern
6
+
7
+ pattern '' do
8
+ it { should match('') }
9
+ it { should_not match('/') }
10
+
11
+ it { should expand.to('') }
12
+ it { should_not expand(a: 1) }
13
+
14
+ it { should generate_template('') }
15
+
16
+ it { should respond_to(:expand) }
17
+ it { should respond_to(:to_templates) }
18
+ end
19
+
20
+ pattern '/' do
21
+ it { should match('/') }
22
+ it { should_not match('/foo') }
23
+
24
+ it { should expand.to('/') }
25
+ it { should_not expand(a: 1) }
26
+ end
27
+
28
+ pattern '/foo' do
29
+ it { should match('/foo') }
30
+ it { should_not match('/bar') }
31
+ it { should_not match('/foo.bar') }
32
+
33
+ it { should expand.to('/foo') }
34
+ it { should_not expand(a: 1) }
35
+ end
36
+
37
+ pattern '/foo/bar' do
38
+ it { should match('/foo/bar') }
39
+ it { should_not match('/foo%2Fbar') }
40
+ it { should_not match('/foo%2fbar') }
41
+
42
+ it { should expand.to('/foo/bar') }
43
+ it { should_not expand(a: 1) }
44
+ end
45
+
46
+ pattern '/:foo' do
47
+ it { should match('/foo') .capturing foo: 'foo' }
48
+ it { should match('/bar') .capturing foo: 'bar' }
49
+ it { should match('/foo.bar') .capturing foo: 'foo.bar' }
50
+ it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
51
+ it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
52
+
53
+ it { should_not match('/foo?') }
54
+ it { should_not match('/foo/bar') }
55
+ it { should_not match('/') }
56
+ it { should_not match('/foo/') }
57
+
58
+ example { pattern.params('/foo') .should be == {"foo" => "foo"} }
59
+ example { pattern.params('/f%20o') .should be == {"foo" => "f o"} }
60
+ example { pattern.params('').should be_nil }
61
+
62
+ it { should expand(foo: 'bar') .to('/bar') }
63
+ it { should expand(foo: 'b r') .to('/b%20r') }
64
+ it { should expand(foo: 'foo/bar') .to('/foo%2Fbar') }
65
+
66
+ it { should_not expand(foo: 'foo', bar: 'bar') }
67
+ it { should_not expand(bar: 'bar') }
68
+ it { should_not expand }
69
+
70
+ it { should generate_template('/{foo}') }
71
+ end
72
+
73
+ pattern '/*' do
74
+ it { should match('/') }
75
+ it { should match('/foo') }
76
+ it { should match('/foo/bar') }
77
+
78
+ example { pattern.params('/foo/bar') .should be == {"splat" => ["foo", "bar"]}}
79
+ it { should generate_template('/{+splat}') }
80
+ end
81
+
82
+ pattern '/**' do
83
+ it { should match('/') .capturing splat: '' }
84
+ it { should match('/foo') .capturing splat: 'foo' }
85
+ it { should match('/foo/bar') .capturing splat: 'foo/bar' }
86
+
87
+ example { pattern.params('/foo/bar') .should be == {"splat" => ["foo/bar"]} }
88
+ it { should generate_template('/{+splat}') }
89
+ end
90
+ end
@@ -0,0 +1,209 @@
1
+ require 'support'
2
+ require 'mustermann/express'
3
+
4
+ describe Mustermann::Express do
5
+ extend Support::Pattern
6
+
7
+ pattern '' do
8
+ it { should match('') }
9
+ it { should_not match('/') }
10
+
11
+ it { should expand.to('') }
12
+ it { should_not expand(a: 1) }
13
+
14
+ it { should generate_template('') }
15
+
16
+ it { should respond_to(:expand) }
17
+ it { should respond_to(:to_templates) }
18
+ end
19
+
20
+ pattern '/' do
21
+ it { should match('/') }
22
+ it { should_not match('/foo') }
23
+
24
+ it { should expand.to('/') }
25
+ it { should_not expand(a: 1) }
26
+ end
27
+
28
+ pattern '/foo' do
29
+ it { should match('/foo') }
30
+ it { should_not match('/bar') }
31
+ it { should_not match('/foo.bar') }
32
+
33
+ it { should expand.to('/foo') }
34
+ it { should_not expand(a: 1) }
35
+ end
36
+
37
+ pattern '/foo/bar' do
38
+ it { should match('/foo/bar') }
39
+ it { should_not match('/foo%2Fbar') }
40
+ it { should_not match('/foo%2fbar') }
41
+
42
+ it { should expand.to('/foo/bar') }
43
+ it { should_not expand(a: 1) }
44
+ end
45
+
46
+ pattern '/:foo' do
47
+ it { should match('/foo') .capturing foo: 'foo' }
48
+ it { should match('/bar') .capturing foo: 'bar' }
49
+ it { should match('/foo.bar') .capturing foo: 'foo.bar' }
50
+ it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
51
+ it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
52
+
53
+ it { should_not match('/foo?') }
54
+ it { should_not match('/foo/bar') }
55
+ it { should_not match('/') }
56
+ it { should_not match('/foo/') }
57
+
58
+ example { pattern.params('/foo') .should be == {"foo" => "foo"} }
59
+ example { pattern.params('/f%20o') .should be == {"foo" => "f o"} }
60
+ example { pattern.params('').should be_nil }
61
+
62
+ it { should expand(foo: 'bar') .to('/bar') }
63
+ it { should expand(foo: 'b r') .to('/b%20r') }
64
+ it { should expand(foo: 'foo/bar') .to('/foo%2Fbar') }
65
+
66
+ it { should_not expand(foo: 'foo', bar: 'bar') }
67
+ it { should_not expand(bar: 'bar') }
68
+ it { should_not expand }
69
+
70
+ it { should generate_template('/{foo}') }
71
+ end
72
+
73
+ pattern '/:foo+' do
74
+ it { should_not match('/') }
75
+ it { should match('/foo') .capturing foo: 'foo' }
76
+ it { should match('/foo/bar') .capturing foo: 'foo/bar' }
77
+
78
+ it { should expand .to('/') }
79
+ it { should expand(foo: nil) .to('/') }
80
+ it { should expand(foo: '') .to('/') }
81
+ it { should expand(foo: 'foo') .to('/foo') }
82
+ it { should expand(foo: 'foo/bar') .to('/foo/bar') }
83
+ it { should expand(foo: 'foo.bar') .to('/foo.bar') }
84
+
85
+ it { should generate_template('/{+foo}') }
86
+ end
87
+
88
+ pattern '/:foo?' do
89
+ it { should match('/foo') .capturing foo: 'foo' }
90
+ it { should match('/bar') .capturing foo: 'bar' }
91
+ it { should match('/foo.bar') .capturing foo: 'foo.bar' }
92
+ it { should match('/%0Afoo') .capturing foo: '%0Afoo' }
93
+ it { should match('/foo%2Fbar') .capturing foo: 'foo%2Fbar' }
94
+ it { should match('/') }
95
+
96
+ it { should_not match('/foo?') }
97
+ it { should_not match('/foo/bar') }
98
+ it { should_not match('/foo/') }
99
+
100
+ example { pattern.params('/foo') .should be == {"foo" => "foo"} }
101
+ example { pattern.params('/f%20o') .should be == {"foo" => "f o"} }
102
+ example { pattern.params('/') .should be == {"foo" => nil } }
103
+
104
+ it { should expand(foo: 'bar') .to('/bar') }
105
+ it { should expand(foo: 'b r') .to('/b%20r') }
106
+ it { should expand(foo: 'foo/bar') .to('/foo%2Fbar') }
107
+ it { should expand .to('/') }
108
+
109
+ it { should_not expand(foo: 'foo', bar: 'bar') }
110
+ it { should_not expand(bar: 'bar') }
111
+
112
+ it { should generate_template('/{foo}') }
113
+ it { should generate_template('/') }
114
+ end
115
+
116
+ pattern '/:foo*' do
117
+ it { should match('/') .capturing foo: '' }
118
+ it { should match('/foo') .capturing foo: 'foo' }
119
+ it { should match('/foo/bar') .capturing foo: 'foo/bar' }
120
+
121
+ it { should expand .to('/') }
122
+ it { should expand(foo: nil) .to('/') }
123
+ it { should expand(foo: '') .to('/') }
124
+ it { should expand(foo: 'foo') .to('/foo') }
125
+ it { should expand(foo: 'foo/bar') .to('/foo/bar') }
126
+ it { should expand(foo: 'foo.bar') .to('/foo.bar') }
127
+
128
+ it { should generate_template('/{+foo}') }
129
+ end
130
+
131
+ pattern '/:foo(.*)' do
132
+ it { should match('/') .capturing foo: '' }
133
+ it { should match('/foo') .capturing foo: 'foo' }
134
+ it { should match('/foo/bar') .capturing foo: 'foo/bar' }
135
+
136
+ it { should expand(foo: '') .to('/') }
137
+ it { should expand(foo: 'foo') .to('/foo') }
138
+ it { should expand(foo: 'foo/bar') .to('/foo/bar') }
139
+ it { should expand(foo: 'foo.bar') .to('/foo.bar') }
140
+
141
+ it { should generate_template('/{foo}') }
142
+ end
143
+
144
+ pattern '/:foo(\d+)' do
145
+ it { should_not match('/') }
146
+ it { should_not match('/foo') }
147
+ it { should match('/15') .capturing foo: '15' }
148
+ it { should generate_template('/{foo}') }
149
+ end
150
+
151
+ pattern '/:foo(\d+|bar)' do
152
+ it { should_not match('/') }
153
+ it { should_not match('/foo') }
154
+ it { should match('/15') .capturing foo: '15' }
155
+ it { should match('/bar') .capturing foo: 'bar' }
156
+ it { should generate_template('/{foo}') }
157
+ end
158
+
159
+ pattern '/:foo(\))' do
160
+ it { should_not match('/') }
161
+ it { should_not match('/foo') }
162
+ it { should match('/)').capturing foo: ')' }
163
+ it { should generate_template('/{foo}') }
164
+ end
165
+
166
+ pattern '/:foo(prefix(\d+|bar))' do
167
+ it { should_not match('/prefix') }
168
+ it { should_not match('/prefixfoo') }
169
+ it { should match('/prefix15') .capturing foo: 'prefix15' }
170
+ it { should match('/prefixbar') .capturing foo: 'prefixbar' }
171
+ it { should generate_template('/{foo}') }
172
+ end
173
+
174
+ pattern '/(.+)' do
175
+ it { should_not match('/') }
176
+ it { should match('/foo') .capturing splat: 'foo' }
177
+ it { should match('/foo/bar') .capturing splat: 'foo/bar' }
178
+ it { should generate_template('/{+splat}') }
179
+ end
180
+
181
+ pattern '/(foo(a|b))' do
182
+ it { should_not match('/') }
183
+ it { should match('/fooa') .capturing splat: 'fooa' }
184
+ it { should match('/foob') .capturing splat: 'foob' }
185
+ it { should generate_template('/{+splat}') }
186
+ end
187
+
188
+ context 'invalid syntax' do
189
+ example 'unexpected closing parenthesis' do
190
+ expect { Mustermann::Express.new('foo)bar') }.
191
+ to raise_error(Mustermann::ParseError, 'unexpected ) while parsing "foo)bar"')
192
+ end
193
+
194
+ example 'missing closing parenthesis' do
195
+ expect { Mustermann::Express.new('foo(bar') }.
196
+ to raise_error(Mustermann::ParseError, 'unexpected end of string while parsing "foo(bar"')
197
+ end
198
+
199
+ example 'unexpected ?' do
200
+ expect { Mustermann::Express.new('foo?bar') }.
201
+ to raise_error(Mustermann::ParseError, 'unexpected ? while parsing "foo?bar"')
202
+ end
203
+
204
+ example 'unexpected *' do
205
+ expect { Mustermann::Express.new('foo*bar') }.
206
+ to raise_error(Mustermann::ParseError, 'unexpected * while parsing "foo*bar"')
207
+ end
208
+ end
209
+ end
@@ -0,0 +1,119 @@
1
+ require 'support'
2
+ require 'mustermann/file_utils'
3
+
4
+ describe Mustermann::FileUtils do
5
+ subject(:utils) { Mustermann::FileUtils }
6
+ include FileUtils
7
+
8
+ before do
9
+ @pwd = Dir.pwd
10
+ @tmp_dir = File.join(__dir__, 'tmp')
11
+
12
+ mkdir_p(@tmp_dir)
13
+ chdir(@tmp_dir)
14
+
15
+ touch("foo.txt")
16
+ touch("foo.rb")
17
+ touch("bar.txt")
18
+ touch("bar.rb")
19
+ end
20
+
21
+ after do
22
+ chdir(@pwd) if @pwd
23
+ rm_rf(@tmp_dir) if @tmp_dir
24
+ end
25
+
26
+ describe :glob_pattern do
27
+ example { utils.glob_pattern('/:foo') .should be == '/*' }
28
+ example { utils.glob_pattern('/*foo') .should be == '/**/*' }
29
+ example { utils.glob_pattern('/(ab|c)?/:foo/d/*bar') .should be == '/{{ab,c},}/*/d/**/*' }
30
+ example { utils.glob_pattern('/a', '/b') .should be == '{/a,/b}' }
31
+ example { utils.glob_pattern('**/*', type: :shell) .should be == '**/*' }
32
+ example { utils.glob_pattern(/can't parse this/) .should be == '**/*' }
33
+ example { utils.glob_pattern('/foo', type: :identity) .should be == '/foo' }
34
+ example { utils.glob_pattern('/fo*', type: :identity) .should be == '/fo\\*' }
35
+ end
36
+
37
+ describe :glob do
38
+ example do
39
+ utils.glob(":name.txt").sort.should be == ['bar.txt', 'foo.txt']
40
+ end
41
+
42
+ example do
43
+ extensions = []
44
+ utils.glob("foo.:ext") { |file, params| extensions << params['ext'] }
45
+ extensions.sort.should be == ['rb', 'txt']
46
+ end
47
+
48
+ example do
49
+ utils.glob(":name.:ext", capture: { ext: 'rb', name: 'foo' }).should be == ['foo.rb']
50
+ end
51
+ end
52
+
53
+ describe :glob_map do
54
+ example do
55
+ utils.glob_map(':name.rb' => ':name/init.rb').should be == {
56
+ "bar.rb" => "bar/init.rb",
57
+ "foo.rb" => "foo/init.rb"
58
+ }
59
+ end
60
+
61
+ example do
62
+ result = {}
63
+ returned = utils.glob_map(':name.rb' => ':name/init.rb') { |k, v| result[v] = k.upcase }
64
+ returned.sort .should be == ["BAR.RB", "FOO.RB"]
65
+ result["bar/init.rb"] .should be == "BAR.RB"
66
+ end
67
+ end
68
+
69
+ describe :cp do
70
+ example do
71
+ utils.cp(':name.rb' => ':name.ruby', ':name.txt' => ':name.md')
72
+ File.should be_exist('foo.ruby')
73
+ File.should be_exist('bar.md')
74
+ File.should be_exist('bar.txt')
75
+ end
76
+ end
77
+
78
+ describe :cp_r do
79
+ example do
80
+ mkdir_p "foo/bar"
81
+ utils.cp_r('foo/:name' => :name)
82
+ File.should be_directory('bar')
83
+ end
84
+ end
85
+
86
+ describe :mv do
87
+ example do
88
+ utils.mv(':name.rb' => ':name.ruby', ':name.txt' => ':name.md')
89
+ File.should be_exist('foo.ruby')
90
+ File.should be_exist('bar.md')
91
+ File.should_not be_exist('bar.txt')
92
+ end
93
+ end
94
+
95
+ describe :ln do
96
+ example do
97
+ utils.ln(':name.rb' => ':name.ruby', ':name.txt' => ':name.md')
98
+ File.should be_exist('foo.ruby')
99
+ File.should be_exist('bar.md')
100
+ File.should be_exist('bar.txt')
101
+ end
102
+ end
103
+
104
+ describe :ln_s do
105
+ example do
106
+ utils.ln_s(':name.rb' => ':name.ruby', ':name.txt' => ':name.md')
107
+ File.should be_symlink('foo.ruby')
108
+ File.should be_symlink('bar.md')
109
+ File.should be_exist('bar.txt')
110
+ end
111
+ end
112
+
113
+ describe :ln_sf do
114
+ example do
115
+ utils.ln_sf(':name.rb' => ':name.txt')
116
+ File.should be_symlink('foo.txt')
117
+ end
118
+ end
119
+ end