mustermann-contrib 1.0.0.beta2

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/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,45 @@
1
+ module Mustermann
2
+ module Visualizer
3
+ # @!visibility private
4
+ module Highlighter
5
+ # @!visibility private
6
+ module Composite
7
+ extend self
8
+
9
+ # @!visibility private
10
+ def highlight?(pattern)
11
+ pattern.is_a? Mustermann::Composite
12
+ end
13
+
14
+ # @!visibility private
15
+ def highlight(pattern, renderer)
16
+ operator = " #{pattern.operator} "
17
+ patterns = pattern.patterns.map { |p| highlight_nested(p, renderer) }.join(quote(renderer, operator))
18
+ renderer.pre(:composite) + patterns + renderer.post(:composite)
19
+ end
20
+
21
+ # @!visibility private
22
+ def highlight_nested(pattern, renderer)
23
+ highlighter = Highlighter.highlighter_for(pattern)
24
+ if highlighter.respond_to? :nested_highlight
25
+ highlighter.nested_highlight(pattern, renderer)
26
+ else
27
+ type = quote(renderer, pattern.class.name[/[^:]+$/].downcase + ":", :type)
28
+ quote = quote(renderer, ?")
29
+ type + quote + highlighter.highlight(pattern, renderer) + quote
30
+ end
31
+ end
32
+
33
+ # @!visibility private
34
+ def nested_highlight(pattern, renderer)
35
+ quote(renderer, ?() + highlight(pattern, renderer) + quote(renderer, ?))
36
+ end
37
+
38
+ # @!visibility private
39
+ def quote(renderer, string, type = :quote)
40
+ renderer.pre(type) + renderer.escape(string, string) + renderer.post(type)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,18 @@
1
+ module Mustermann
2
+ module Visualizer
3
+ # @!visibility private
4
+ module Highlighter
5
+ # Provides highlighting for patterns that don't have a highlighter.
6
+ # @!visibility private
7
+ module Dummy
8
+ # @!visibility private
9
+ def self.highlight(pattern, renderer)
10
+ output = ""
11
+ output << renderer.pre(:root) << renderer.pre(:unknown)
12
+ output << renderer.escape(pattern.to_s)
13
+ output << renderer.post(:unknown) << renderer.post(:root)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,104 @@
1
+ require 'strscan'
2
+
3
+ module Mustermann
4
+ module Visualizer
5
+ # @!visibility private
6
+ module Highlighter
7
+ # Provides highlighting for {Mustermann::Regular}
8
+ # @!visibility private
9
+ class Regular
10
+ # @!visibility private
11
+ SPECIAL_ESCAPE = ['w', 'W', 'd', 'D', 'h', 'H', 's', 'S', 'G', 'b', 'B']
12
+ private_constant(:SPECIAL_ESCAPE)
13
+
14
+ # @!visibility private
15
+ def self.highlight?(pattern)
16
+ pattern.class.name == "Mustermann::Regular"
17
+ end
18
+
19
+ # @!visibility private
20
+ def self.highlight(pattern, renderer)
21
+ new(renderer).highlight(pattern)
22
+ end
23
+
24
+ # @!visibility private
25
+ attr_reader :renderer, :output, :scanner
26
+
27
+ # @!visibility private
28
+ def initialize(renderer)
29
+ @renderer = renderer
30
+ @output = ""
31
+ end
32
+
33
+ # @!visibility private
34
+ def highlight(pattern)
35
+ output << renderer.pre(:root)
36
+ @scanner = ::StringScanner.new(pattern.to_s)
37
+ scan
38
+ output << renderer.post(:root)
39
+ end
40
+
41
+ # @!visibility private
42
+ def scan(stop = nil)
43
+ until scanner.eos?
44
+ case char = scanner.getch
45
+ when stop then return char
46
+ when ?/ then element(:separator, char)
47
+ when Regexp.escape(char) then element(:char, char)
48
+ when ?\\ then escaped(scanner.getch)
49
+ when ?( then potential_capture
50
+ when ?[ then char_class
51
+ when ?^, ?$ then element(:illegal, char)
52
+ when ?{ then element(:special, "\{#{scanner.scan(/[^\}]*\}/)}")
53
+ else element(:special, char)
54
+ end
55
+ end
56
+ end
57
+
58
+ # @!visibility private
59
+ def char_class
60
+ if result = scanner.scan(/\[:\w+:\]\]/)
61
+ element(:special, "[#{result}")
62
+ else
63
+ element(:special, ?[)
64
+ element(:special, ?^) if scanner.scan(/\^/)
65
+ end
66
+ end
67
+
68
+ # @!visibility private
69
+ def potential_capture
70
+ if scanner.scan(/\?<(\w+)>/)
71
+ element(:capture, "(?<") do
72
+ element(:name, scanner[1])
73
+ output << ">" << scan(?))
74
+ end
75
+ elsif scanner.scan(/\?(?:(?:-\w+)?:|>|<=|<!|!|=)/)
76
+ element(:special, "(#{scanner[0]}")
77
+ else
78
+ element(:capture, "(") { output << scan(?)) }
79
+ end
80
+ end
81
+
82
+ # @!visibility private
83
+ def escaped(char)
84
+ case char
85
+ when *SPECIAL_ESCAPE then element(:special, "\\#{char}")
86
+ when 'A', 'Z', 'z' then element(:illegal, "\\#{char}")
87
+ when 'g' then element(:special, "\\#{char}#{scanner.scan(/<\w*>/)}")
88
+ when 'p', 'u' then element(:special, "\\#{char}#{scanner.scan(/\{[^\}]*\}/)}")
89
+ when ?/ then element(:separator, char)
90
+ else element(:escaped, ?\\) { element(:escaped_char, char) }
91
+ end
92
+ end
93
+
94
+ # @!visibility private
95
+ def element(type, content = nil)
96
+ output << renderer.pre(type)
97
+ output << renderer.escape(content) if content
98
+ yield if block_given?
99
+ output << renderer.post(type)
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,68 @@
1
+ module Mustermann
2
+ module Visualizer
3
+ # Mixin that will be added to {Mustermann::Pattern}.
4
+ module PatternExtension
5
+ prepend_features Composite
6
+ prepend_features Pattern
7
+
8
+ # @example
9
+ # puts Mustermann.new("/:page").to_ansi
10
+ #
11
+ # @return [String] ANSI colorized version of the pattern.
12
+ def to_ansi(inspect: nil, **theme)
13
+ Visualizer.highlight(self, **theme).to_ansi(inspect: inspect)
14
+ end
15
+
16
+ # @example
17
+ # puts Mustermann.new("/:page").to_html
18
+ #
19
+ # @return [String] HTML version of the pattern.
20
+ def to_html(inspect: nil, tag: :span, class_prefix: "mustermann_", css: :inline, **theme)
21
+ Visualizer.highlight(self, **theme).to_html(inspect: inspect, tag: tag, class_prefix: class_prefix, css: css)
22
+ end
23
+
24
+ # @example
25
+ # puts Mustermann.new("/:page").to_tree
26
+ #
27
+ # @return [String] tree version of the pattern.
28
+ def to_tree
29
+ Visualizer.tree(self).to_s
30
+ end
31
+
32
+ # If invoked directly by puts: ANSI colorized version of the pattern.
33
+ # If invoked by anything else: String version of the pattern.
34
+ #
35
+ # @example
36
+ # require 'mustermann/visualizer'
37
+ # pattern = Mustermann.new('/:page')
38
+ # puts pattern # will have color
39
+ # puts pattern.to_s # will not have color
40
+ #
41
+ # @return [String] non-colorized or colorized version of the pattern
42
+ def to_s
43
+ caller_locations.first.label == 'puts' ? to_ansi : super
44
+ end
45
+
46
+ # If invoked directly by IRB, same as {#color_inspect}, otherwise same as {Mustermann::Pattern#inspect}.
47
+ def inspect
48
+ caller_locations.first.base_label == '<module:IRB>' ? color_inspect : super
49
+ end
50
+
51
+ # @return [String] ANSI colorized version of {Mustermann::Pattern#inspect}
52
+ def color_inspect(base_color = nil, **theme)
53
+ base_color ||= Highlight::DEFAULT_THEME[:base01]
54
+ template = is_a?(Composite) ? "*#<%p:(*%s*)>*" : "*#<%p:*%s*>*"
55
+ Hansi.render(template, self.class, to_ansi(inspect: true, **theme), "*" => base_color)
56
+ end
57
+
58
+ # If invoked directly by IRB, same as {#color_inspect}, otherwise same as Object#pretty_print.
59
+ def pretty_print(q)
60
+ if q.class.name.to_s[/[^:]+$/] == "ColorPrinter"
61
+ q.text(color_inspect, inspect.length)
62
+ else
63
+ super
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,23 @@
1
+ module Mustermann
2
+ module Visualizer
3
+ # @!visibility private
4
+ module Renderer
5
+ # Generates ANSI colored strings.
6
+ # @!visibility private
7
+ class ANSI
8
+ # @!visibility private
9
+ def initialize(target, mode: Hansi.mode, **options)
10
+ @target = target
11
+ @mode = mode
12
+ @options = options
13
+ end
14
+
15
+ # @!visibility private
16
+ def render
17
+ template = @target.to_hansi_template(**@options)
18
+ Hansi.render(template, tags: true, theme: @target.theme, mode: @mode)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,46 @@
1
+ module Mustermann
2
+ module Visualizer
3
+ # @!visibility private
4
+ module Renderer
5
+ # Logic shared by most renderers.
6
+ class Generic
7
+ # @!visibility private
8
+ def initialize(target, inspect: nil, add_qoutes: true)
9
+ @target = target
10
+ @inspect = inspect
11
+ @add_qoutes = !target.pattern.is_a?(Mustermann::Composite)
12
+ end
13
+
14
+ # @!visibility private
15
+ def render
16
+ quote = "#{pre(:quote)}#{escape_string(?")}#{post(:quote)}" if @inspect and @add_qoutes
17
+ pre(:pattern).to_s + preamble.to_s + quote.to_s + @target.render(self) + quote.to_s + post(:pattern).to_s
18
+ end
19
+
20
+ # @!visibility private
21
+ def preamble
22
+ end
23
+
24
+ # @!visibility private
25
+ def escape(value, inspect_value = value.to_s.inspect[1..-2])
26
+ escape_string(@inspect ? inspect_value : value.to_s)
27
+ end
28
+
29
+ # @!visibility private
30
+ def escape_string(string)
31
+ string
32
+ end
33
+
34
+ # @!visibility private
35
+ def pre(type)
36
+ ""
37
+ end
38
+
39
+ # @!visibility private
40
+ def post(type)
41
+ ""
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,34 @@
1
+ require 'mustermann/visualizer/renderer/generic'
2
+
3
+ module Mustermann
4
+ module Visualizer
5
+ # @!visibility private
6
+ module Renderer
7
+ # Generates Hansi template string.
8
+ # @see Mustermann::Visualizer::Renderer::ANSI
9
+ # @!visibility private
10
+ class HansiTemplate < Generic
11
+ # @!visibility private
12
+ def initialize(*)
13
+ @hansi = Hansi::StringRenderer.new(tags: true)
14
+ super
15
+ end
16
+
17
+ # @!visibility private
18
+ def escape_string(string)
19
+ @hansi.escape(string)
20
+ end
21
+
22
+ # @!visibility private
23
+ def pre(type)
24
+ "<#{type}>"
25
+ end
26
+
27
+ # @!visibility private
28
+ def post(type)
29
+ "</#{type}>"
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,50 @@
1
+ require 'mustermann/visualizer/renderer/generic'
2
+ require 'cgi'
3
+
4
+ module Mustermann
5
+ module Visualizer
6
+ # @!visibility private
7
+ module Renderer
8
+ # Generates HTML output.
9
+ # @!visibility private
10
+ class HTML < Generic
11
+ # @!visibility private
12
+ def initialize(target, tag: :span, class_prefix: "mustermann_", css: :inline, **options)
13
+ raise ArgumentError, 'css option %p not supported, should be true, false or inline' if css != true and css != false and css != :inline
14
+ super(target, **options)
15
+ @css, @tag, @class_prefix = css, tag, class_prefix
16
+ end
17
+
18
+ # @!visibility private
19
+ def preamble
20
+ "<style type=\"text/css\">\n%s</style>" % stylesheet if @css == true
21
+ end
22
+
23
+ # @!visibility private
24
+ def stylesheet
25
+ @target.theme.to_css { |name| ".#{@class_prefix}pattern .#{@class_prefix}#{name}" }
26
+ end
27
+
28
+ # @!visibility private
29
+ def escape_string(string)
30
+ CGI.escape_html(string)
31
+ end
32
+
33
+ # @!visibility private
34
+ def pre(type)
35
+ if @css == :inline
36
+ return "" unless rule = @target.theme[type]
37
+ "<#{@tag} style=\"#{rule.to_css_rule}\">"
38
+ else
39
+ "<#{@tag} class=\"#{@class_prefix}#{type}\">"
40
+ end
41
+ end
42
+
43
+ # @!visibility private
44
+ def post(type)
45
+ "</#{@tag}>"
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,37 @@
1
+ require 'mustermann/visualizer/renderer/generic'
2
+
3
+ module Mustermann
4
+ module Visualizer
5
+ # @!visibility private
6
+ module Renderer
7
+ # Generates a s-expression like string.
8
+ # @!visibility private
9
+ class Sexp < Generic
10
+ # @!visibility private
11
+ def render
12
+ @inspect = false
13
+ super.gsub(/ ?\)( \))*/) { |s| s.gsub(' ', '') }.strip
14
+ end
15
+
16
+
17
+ # @!visibility private
18
+ def pre(type)
19
+ "(#{type} " if type != :pattern
20
+ end
21
+
22
+ # @!visibility private
23
+ def escape_string(input)
24
+ inspect = input.inspect
25
+ input = inspect if inspect != "\"#{input}\""
26
+ input = inspect if input =~ /[\s\"\'\(\)]/
27
+ input + " "
28
+ end
29
+
30
+ # @!visibility private
31
+ def post(type)
32
+ ") " if type != :pattern
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,63 @@
1
+ require 'hansi'
2
+
3
+ module Mustermann
4
+ module Visualizer
5
+ # Represents a (sub)tree and at the same time a node in the tree.
6
+ class Tree
7
+ # @!visibility private
8
+ attr_reader :line, :children, :prefix_color, :before, :after
9
+
10
+ # @!visibility private
11
+ def initialize(line, *children, prefix_color: :default, before: "", after: "")
12
+ @line = line
13
+ @children = children
14
+ @prefix_color = prefix_color
15
+ @before = before
16
+ @after = after
17
+ end
18
+
19
+ # used for positioning {#after}
20
+ # @!visibility private
21
+ def line_widths(offset = 0)
22
+ child_widths = children.flat_map { |c| c.line_widths(offset + 2) }
23
+ width = length(line + before) + offset
24
+ [width, *child_widths]
25
+ end
26
+
27
+ # Renders the tree.
28
+ # @return [String] rendered version of the tree
29
+ def to_s
30
+ render("", "", line_widths.max)
31
+ end
32
+
33
+ # Renders tree, including nesting.
34
+ # @!visibility private
35
+ def render(first_prefix, prefix, width)
36
+ output = before + Hansi.render(prefix_color, first_prefix) + line
37
+ output = ljust(output, width) + " " + after + "\n"
38
+ children[0..-2].each { |child| output += child.render(prefix + "├ ", prefix + "│ ", width) }
39
+ output += children.last.render(prefix + "└ ", prefix + " ", width) if children.last
40
+ output
41
+ end
42
+
43
+ # @!visibility private
44
+ def length(string)
45
+ deansi(string).length
46
+ end
47
+
48
+ # @!visibility private
49
+ def deansi(string)
50
+ string.gsub(/\e\[[^m]+m/, '')
51
+ end
52
+
53
+ # @!visibility private
54
+ def ljust(string, width)
55
+ missing = width - length(string)
56
+ append = missing > 0 ? " " * missing : ""
57
+ string + append
58
+ end
59
+
60
+ private :ljust, :deansi, :length
61
+ end
62
+ end
63
+ end