html-renderer 0.0.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4d2be4cf32a612a0e2fa528a0d50e85df36f732e40c408ed3183b6a041c60558
4
- data.tar.gz: 1500874bc627082bf07f2e0912ba5b2020cad6987cd9cd29c4add0abe0e5d0db
3
+ metadata.gz: d3c17a79e5d484dfafbdf2161683d28c8ee9d24b3ec099c7e392236fe167f547
4
+ data.tar.gz: 4c0cc61cc44691ab2e35b2c289f352fe17aec1d499cd01d86b6a01f4a3a9dab6
5
5
  SHA512:
6
- metadata.gz: eda1c880bebd51ed462ca2c07fcec4abfcb80cc30cc9b16b2c7366d4abc46637f3d3f778d36689e3cb6472b60fa28fda2282fea64c49262f6a4246c45468d2d9
7
- data.tar.gz: 26dec9aa2c27f6356101c92ed5aabf164249aaec89a99febbb55ecbd4aafe9db1e44d9e914344b49f4919f501cd01d568427f48e267db353d4d7952ba9f36b25
6
+ metadata.gz: 52f7781a488c72b42398dad8a8a10393a3ccc1eca7147696e94398d28633d57956133a05c026364c8125b5dfcdf333a6772ec44ee786ced02ceb130e2c6c1ce2
7
+ data.tar.gz: 228891adf55ef0e77c958399071c0eb2a4acbc2620e3681aa59b65b56bd47dc308e5cee5b968043603388cd92e44443b7906acf93963c69513d94cceafa3f0b4
data/.gemspec CHANGED
@@ -12,9 +12,10 @@ Gem::Specification.new do |s|
12
12
  s.email = "chris@ill-logic.com"
13
13
  s.authors = ["epitron"]
14
14
 
15
- s.files = `git ls`.lines.map(&:strip)
15
+ s.files = `git ls`.lines.map(&:strip)
16
16
  s.extra_rdoc_files = ["README.md", "LICENSE"]
17
-
18
- s.add_dependency "oga", "~> 2"
19
- s.add_development_dependency "ansi", "~> 1"
17
+
18
+ s.add_dependency "oga"
19
+ s.add_dependency "terminal-table", "~> 1.8"
20
+ s.add_dependency "term-ansicolor", "~> 1.7"
20
21
  end
@@ -0,0 +1 @@
1
+ /pkg/
data/README.md CHANGED
@@ -2,11 +2,29 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- Allows you to subclass `HTMLRenderer::Base`, add some methods, and then parse HTML to generate custom output.
5
+ An extensible HTML renderer.
6
6
 
7
- (Uses a similar API to the [RedCarpet](https://github.com/vmg/redcarpet) gem.)
7
+ Comes with two built-in renderers:
8
+ * `HTMLRenderer::ANSI` (outputs colored text to the termnial)
9
+ * `HTMLRenderer::Text` (outputs plain text)
8
10
 
9
- ## Examples
11
+ ## Usage
10
12
 
11
- * [HTML to Plain Text Renderer](https://github.com/epitron/html-renderer/blob/master/examples/plain_text_renderer.rb)
12
- * [HTML to ANSI Renderer](https://github.com/epitron/html-renderer/blob/master/examples/ansi_renderer.rb)
13
+ Render to ANSI:
14
+ ```
15
+ ansi_text = HTMLRenderer::ANSI.render("<b>hello html</b>")
16
+ ansi_text = HTMLRenderer::ANSI.render(open("file.html"))
17
+ ```
18
+
19
+ Render to plain text:
20
+ ```
21
+ plain_text = HTMLRenderer::Text.render(open("file.html"))
22
+ ```
23
+
24
+ ## Extending it
25
+
26
+ The API design uses the same philosophy as [RedCarpet](https://github.com/vmg/redcarpet).
27
+
28
+ To create a new renderer, subclass `HTMLRenderer::Base`, then add a method to handle each type of element. Whatever the method returns is output by the renderer.
29
+
30
+ Example renderer: [HTMLRenderer::ANSI](https://github.com/epitron/html-renderer/blob/master/lib/html-renderer/ansi.rb)
data/Rakefile CHANGED
@@ -1,14 +1,13 @@
1
- pkgname = "html-renderer"
2
- gem_version = File.read("VERSION").strip
1
+ gem = eval(File.read '.gemspec')
3
2
 
4
- gemfile = "#{pkgname}-#{gem_version}.gem"
3
+ gemfile = "#{gem.name}-#{gem.version}.gem"
5
4
 
6
5
  task :build do
7
6
  system "gem build .gemspec"
8
7
  system "mkdir pkg/" unless File.directory? "pkg"
9
8
  system "mv #{gemfile} pkg/"
10
9
  end
11
-
10
+
12
11
  task :release => :build do
13
12
  system "gem push pkg/#{gemfile}"
14
13
  end
@@ -18,3 +17,9 @@ task :gem => :build
18
17
  task :install => :build do
19
18
  system "gem install pkg/#{gemfile}"
20
19
  end
20
+
21
+ task :test do
22
+ Dir.chdir "test"
23
+ $: << "../lib"
24
+ load "test.rb"
25
+ end
data/TODO.md ADDED
@@ -0,0 +1,14 @@
1
+ # TODO list
2
+
3
+ ## Base class
4
+
5
+ ! add HTMLRenderer.reflow_paragraph
6
+ * #render takes options (and passes them to subclasses)
7
+ * :debug option (hide parse errors unless 'true')
8
+
9
+ ## ANSI renderer
10
+
11
+ * use HTMLRenderer.reflow_paragraph
12
+ * :wrap option (with optional terminal width)
13
+ * Better table renderer (with line-drawing)
14
+ * Follow `<table>` style attributes when drawing tables (eg: borders)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.1.3
@@ -1 +1,6 @@
1
1
  require 'html-renderer/base'
2
+
3
+ module HTMLRenderer
4
+ autoload :ANSI, 'html-renderer/ansi'
5
+ autoload :Text, 'html-renderer/text'
6
+ end
@@ -1,16 +1,24 @@
1
1
  require 'html-renderer'
2
2
  require 'ansi/mixin'
3
3
  require 'terminal-table'
4
- require 'coderay'
5
4
 
6
- class String
7
- include ANSI::Mixin
5
+ ###########################################################################
8
6
 
9
- def grey; self.black.bold; end
7
+ module ANSI::Mixin
8
+ def grey; ANSI::Code.bold { ANSI::Code.black { to_s } }; end
10
9
  end
11
10
 
11
+ module HTMLRenderer::ANSIStrings
12
+ refine String do
13
+ include ANSI::Mixin
14
+ end
15
+ end
16
+
17
+ ###########################################################################
18
+
19
+ class HTMLRenderer::ANSI < HTMLRenderer::Base
12
20
 
13
- class ANSIRenderer < HTMLRenderer::Base
21
+ using HTMLRenderer::ANSIStrings
14
22
 
15
23
  private
16
24
 
@@ -32,6 +40,9 @@ private
32
40
  s&.downcase&.scan(/\w+/)&.join
33
41
  end
34
42
 
43
+ def subscript(s)
44
+ "[#{s}]"
45
+ end
35
46
 
36
47
  public
37
48
 
@@ -78,13 +89,7 @@ public
78
89
  end
79
90
 
80
91
  def block_code(code, language)
81
- language ||= :ruby
82
-
83
- language = language[1..-1] if language[0] == "." # strip leading "."
84
- language = :cpp if language == "C++"
85
-
86
- require 'coderay'
87
- "#{indent CodeRay.scan(code, language).term, 4}\n"
92
+ code.bold.cyan + "\n"
88
93
  end
89
94
 
90
95
  def block_quote(text)
@@ -102,7 +107,7 @@ public
102
107
  when 1 then title.bold.yellow
103
108
  when 2 then title.bold.cyan
104
109
  when 3 then title.bold.blue
105
- else title.purple
110
+ else title.magenta
106
111
  end
107
112
 
108
113
  "#{bar}\n #{title}\n#{bar}\n\n"
@@ -174,7 +179,8 @@ public
174
179
 
175
180
  end
176
181
 
182
+ ###########################################################################
177
183
 
178
184
  if __FILE__ == $0
179
- puts ANSIRenderer.render(open(ARGV.first || "test.html"))
185
+ puts HTMLRenderer::ANSI.render(open(ARGV.first || "test.html"))
180
186
  end
@@ -1,30 +1,25 @@
1
1
  ######################################################################################
2
2
  require 'oga'
3
3
  ######################################################################################
4
- #
5
- # TODOs:
6
- # - Streaming output (yield every paragraph/div/header)
7
- # - Embed into 'c' tool (for rendering raw HTML blocks)
8
- #
9
- ######################################################################################
10
-
11
- class String
12
4
 
13
- def tighten
14
- gsub(/\s+/, ' ').strip
15
- end
5
+ module HTMLRenderer
16
6
 
17
- def blank?; !!self[/[^\s]/]; end
7
+ module STDLIBRefinements
8
+ refine String do
9
+ def tighten
10
+ gsub(/\s+/, ' ').strip
11
+ end
18
12
 
19
- end
13
+ def blank?; !!self[/[^\s]/]; end
14
+ end
20
15
 
21
- class NilClass
22
- def blank?; true; end
23
- end
16
+ refine NilClass do
17
+ def blank?; true; end
18
+ end
19
+ end
24
20
 
25
- ######################################################################################
21
+ using STDLIBRefinements
26
22
 
27
- module HTMLRenderer
28
23
 
29
24
  class State
30
25
  attr_accessor :list_order
@@ -65,14 +60,16 @@ private
65
60
  content.blank? ? normal_text(content) : nil
66
61
 
67
62
  when Oga::XML::Element
68
- case node.name
63
+ case node.name.downcase
69
64
  when "a"
70
65
  url = node["href"]
71
66
  title = node["title"]
72
67
  name = node["name"]
73
68
  content = render_children(node, state)
74
69
 
75
- if name and not url
70
+ if title.blank? and url.blank? and name.blank?
71
+ content
72
+ elsif name and not url
76
73
  anchor(name, title, content)
77
74
  else
78
75
  link(url, title, content)
@@ -96,6 +93,8 @@ private
96
93
  emphasis(render_children(node, state))
97
94
  when "sup"
98
95
  superscript(render_children(node, state))
96
+ when "sub"
97
+ subscript(render_children(node, state))
99
98
  when "u"
100
99
  underline(render_children(node, state))
101
100
  when "br"
@@ -118,7 +117,7 @@ private
118
117
  list_item(render_children(node, state), state.list_order)
119
118
 
120
119
  when "code"
121
- block_code(render_children(node, state))
120
+ block_code(render_children(node, state), nil)
122
121
  when "blockquote"
123
122
  block_quote(render_children(node, state))
124
123
 
@@ -149,20 +148,22 @@ private
149
148
  table(header, rows)
150
149
 
151
150
  when "html", "body", "nav", "span", "form", "label", "input", "button", "section", "fieldset",
152
- "menu", "article", "header", "time", "aside", "footer", "nobr", "wbr",
153
- "table", "tr", "td", "th", "thead", "tbody", "noscript", "select",
154
- "address"
151
+ "pre", "menu", "article", "header", "time", "aside", "footer", "nobr", "wbr",
152
+ "table", "tr", "td", "th", "tt", "thead", "tbody", "noscript", "select",
153
+ "address", "center", "small"
155
154
  render_children(node, state)
156
155
 
157
156
  when "head", "script", "link", "style"
158
- # skip it
159
-
157
+ #
158
+ # don't render anything
159
+ #
160
160
  else
161
- raise "Unrecognized HTML tag: #{node.name} -> #{node.inspect}"
161
+ # raise "Unrecognized HTML tag: #{node.name} -> #{node.inspect}"
162
+ $stderr.puts "Unrecognized HTML tag: #{node.name} -> #{node.inspect}"
163
+ render_children(node, state)
162
164
  end
163
165
 
164
- when Oga::XML::Comment
165
- # skip it
166
+ when Oga::XML::Comment, Oga::XML::Cdata
166
167
 
167
168
  else
168
169
  raise "Unhandled Oga node type: #{node.class}"
@@ -0,0 +1,9 @@
1
+ require 'html-renderer'
2
+
3
+ class HTMLRenderer::DebugRenderer < HTMLRenderer::Base
4
+
5
+ def method_missing(meth, *args)
6
+ "#{meth}(#{args})"
7
+ end
8
+
9
+ end
@@ -0,0 +1,105 @@
1
+ require 'strscan'
2
+
3
+ class Stack < Array
4
+ alias_method :top, :last
5
+ alias_method :peek, :last
6
+ end
7
+
8
+ class String
9
+ def recursive_inspect(depth)
10
+ (" "*depth)+inspect
11
+ end
12
+ end
13
+
14
+ class HTMLParser
15
+
16
+ OPEN_TAG_RE = %r{<([^>]+)>}
17
+ CLOSE_TAG_RE = %r{</([^>]+)>}
18
+ TEXT_RE = %r{[^<]+}
19
+ ATTR_RE = %r{(\w+)=(?:"([^"]+)"|'([^']+)'|(\w+))}
20
+
21
+ class Tag
22
+
23
+ attr_accessor :name, :attrs, :children
24
+
25
+ def self.from_str(s)
26
+ name, rest = s.split(/\s+/, 2)
27
+
28
+ if rest
29
+ attrs = rest.scan(HTMLParser::ATTR_RE).flatten.compact.each_slice(2).to_h
30
+ else
31
+ attrs = {}
32
+ end
33
+ new(name, attrs)
34
+ end
35
+
36
+ def initialize(name, attrs={}, children=[])
37
+ @name = name
38
+ @attrs = attrs
39
+ @children = children
40
+ end
41
+
42
+ def recursive_inspect(depth=0)
43
+ curdent = " "*depth
44
+ indent = " "*(depth+1)
45
+ "#{curdent}<#{name} #{attrs}>\n#{indent}#{children.map{|c| c.recursive_inspect(depth+1)}}\n#{curdent}</#{name}>"
46
+ end
47
+
48
+ end
49
+
50
+ def initialize(html)
51
+ @s = StringScanner.new(html)
52
+ # @s = html
53
+ end
54
+
55
+ def each_tag
56
+ until @s.eos?
57
+ if @s.scan(CLOSE_TAG_RE)
58
+ yield [:close_tag, @s.captures.first]
59
+ elsif @s.scan(OPEN_TAG_RE)
60
+ tag = Tag.from_str(@s.captures.first)
61
+ yield [:open_tag, tag]
62
+ elsif @s.scan(TEXT_RE)
63
+ yield [:text, @s.matched]
64
+ end
65
+ end
66
+ end
67
+
68
+ def as_tree
69
+ tree.map { |e| e.recursive_inspect }
70
+ end
71
+
72
+ def tree
73
+ stack = Stack.new
74
+ stack.push Tag.new("root")
75
+
76
+ each_tag do |type, elem|
77
+ case type
78
+ when :text
79
+ text = elem.strip
80
+ stack.top.children << text unless text.empty?
81
+ when :open_tag
82
+ stack.top.children << elem
83
+ stack.push elem
84
+ when :close_tag
85
+ stack.pop
86
+ else
87
+ raise "wat"
88
+ end
89
+ end
90
+
91
+ stack
92
+ end
93
+
94
+ end
95
+
96
+ unless file = ARGV.first
97
+ file = "test.html"
98
+ end
99
+
100
+ html = File.read(file)
101
+
102
+ r = HTMLParser.new(html)
103
+ r.each_tag{|t| p t}
104
+
105
+ # puts r.as_tree
@@ -1,9 +1,10 @@
1
1
  require 'html-renderer'
2
2
 
3
3
  #
4
- # Everything-stripping renderer.
4
+ # Strips out everything but the plain text.
5
5
  #
6
- class PlainTextRenderer < HTMLRenderer::Base
6
+ class HTMLRenderer::Text < HTMLRenderer::Base
7
+
7
8
  # Methods where the first argument is the text content
8
9
  [
9
10
  # block-level calls
@@ -65,8 +66,3 @@ class PlainTextRenderer < HTMLRenderer::Base
65
66
  content + "\t"
66
67
  end
67
68
  end
68
-
69
-
70
- if __FILE__ == $0
71
- puts PlainTextRenderer.render(open("test.html"))
72
- end
File without changes
@@ -0,0 +1,15 @@
1
+ require 'html-renderer'
2
+ require 'html-renderer/debug'
3
+
4
+ [
5
+ HTMLRenderer::ANSI,
6
+ HTMLRenderer::Text,
7
+ HTMLRenderer::DebugRenderer,
8
+ ].each do |renderer|
9
+ puts
10
+ puts "=== #{renderer} =============================="
11
+ puts
12
+ puts renderer.render(open("test.html"))
13
+ puts
14
+ puts
15
+ end
metadata CHANGED
@@ -1,43 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: html-renderer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - epitron
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-22 00:00:00.000000000 Z
11
+ date: 2020-07-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oga
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: terminal-table
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
31
  - - "~>"
18
32
  - !ruby/object:Gem::Version
19
- version: '2'
33
+ version: '1.8'
20
34
  type: :runtime
21
35
  prerelease: false
22
36
  version_requirements: !ruby/object:Gem::Requirement
23
37
  requirements:
24
38
  - - "~>"
25
39
  - !ruby/object:Gem::Version
26
- version: '2'
40
+ version: '1.8'
27
41
  - !ruby/object:Gem::Dependency
28
- name: ansi
42
+ name: term-ansicolor
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
45
  - - "~>"
32
46
  - !ruby/object:Gem::Version
33
- version: '1'
34
- type: :development
47
+ version: '1.7'
48
+ type: :runtime
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
52
  - - "~>"
39
53
  - !ruby/object:Gem::Version
40
- version: '1'
54
+ version: '1.7'
41
55
  description: Easily implement an HTML renderer by creating a subclass and adding some
42
56
  methods, similar to RedCarpet. (Examples are included for rendering HTML to ANSI
43
57
  and plain text.)
@@ -49,21 +63,25 @@ extra_rdoc_files:
49
63
  - LICENSE
50
64
  files:
51
65
  - ".gemspec"
66
+ - ".gitignore"
52
67
  - LICENSE
53
68
  - README.md
54
69
  - Rakefile
70
+ - TODO.md
55
71
  - VERSION
56
- - examples/ansi_renderer.rb
57
- - examples/debug_renderer.rb
58
- - examples/plain_text_renderer.rb
59
- - examples/test.html
60
72
  - lib/html-renderer.rb
73
+ - lib/html-renderer/ansi.rb
61
74
  - lib/html-renderer/base.rb
75
+ - lib/html-renderer/debug.rb
76
+ - lib/html-renderer/html_parser.rb
77
+ - lib/html-renderer/text.rb
78
+ - test/test.html
79
+ - test/test.rb
62
80
  homepage: http://github.com/epitron/html-renderer/
63
81
  licenses:
64
82
  - WTFPL
65
83
  metadata: {}
66
- post_install_message:
84
+ post_install_message:
67
85
  rdoc_options: []
68
86
  require_paths:
69
87
  - lib
@@ -78,9 +96,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
78
96
  - !ruby/object:Gem::Version
79
97
  version: '0'
80
98
  requirements: []
81
- rubyforge_project:
82
- rubygems_version: 2.7.6
83
- signing_key:
99
+ rubygems_version: 3.1.3
100
+ signing_key:
84
101
  specification_version: 4
85
102
  summary: HTML Renderer
86
103
  test_files: []
@@ -1,14 +0,0 @@
1
- require 'html-renderer'
2
-
3
- class DebugRenderer < HTMLRenderer::Base
4
-
5
- def method_missing(meth, *args)
6
- "#{meth}(#{args})"
7
- end
8
-
9
- end
10
-
11
-
12
- if __FILE__ == $0
13
- puts DebugRenderer.render(open("test.html"))
14
- end