minimal_markdown 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 7f513dd2b70e6b64938c16b2c1ab15c2ba24e57dcdad5a8ac1ecacefc0cbdc8e
4
+ data.tar.gz: a313ef705ff4a74d5cb516505d2c472e4149746cc4f9cb2c51864d75a39c5369
5
+ SHA512:
6
+ metadata.gz: 8e15301d1ccf20fec7ff5efa8634e41b41280b0157ee1d7ee070a718c226c04c3f1a0aaf2999f1d94589aac75f55519c4d8e94dd7afcfa1e91c9b5e152207e23
7
+ data.tar.gz: 6e849f0eb968d5fb994ec33d792554eb4d081ff7b74b97360449138cae18c99b001a537c152ec703c9cc67ce5b867531dc9df530433cc9396d4f908f1041cfa1
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,32 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ minimal_markdown (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.3)
10
+ rspec (3.8.0)
11
+ rspec-core (~> 3.8.0)
12
+ rspec-expectations (~> 3.8.0)
13
+ rspec-mocks (~> 3.8.0)
14
+ rspec-core (3.8.2)
15
+ rspec-support (~> 3.8.0)
16
+ rspec-expectations (3.8.4)
17
+ diff-lcs (>= 1.2.0, < 2.0)
18
+ rspec-support (~> 3.8.0)
19
+ rspec-mocks (3.8.1)
20
+ diff-lcs (>= 1.2.0, < 2.0)
21
+ rspec-support (~> 3.8.0)
22
+ rspec-support (3.8.2)
23
+
24
+ PLATFORMS
25
+ ruby
26
+
27
+ DEPENDENCIES
28
+ minimal_markdown!
29
+ rspec (~> 3.8.0)
30
+
31
+ BUNDLED WITH
32
+ 2.0.1
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright 2019 Roger Nesbitt
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # MinimalMarkdown
2
+
3
+ A lightweight and minimal Ruby implementation of a Markdown renderer,
4
+ designed with security in mind.
5
+
6
+ Tiny codebase, no dependencies, and HTML is rendered from a tree so no
7
+ possibility of user-supplied HTML sneaking into the output.
8
+
9
+ ## Supported Markdown elements
10
+
11
+ * Unordered lists
12
+ * Bold
13
+ * Italic
14
+ * Paragraphs
15
+
16
+ ## Usage
17
+
18
+ Add to your Gemfile:
19
+
20
+ ```
21
+ gem 'minimal_markdown'
22
+ ```
23
+
24
+ Add to your app:
25
+
26
+ ```
27
+ MinimalMarkdown.render('Here is my *markdown*')
28
+ => "<div>Here is my <b>markdown</b></div>"
29
+ ```
@@ -0,0 +1,15 @@
1
+ module MinimalMarkdown
2
+ class HtmlRenderer
3
+ attr_reader :tree
4
+
5
+ def initialize(tree)
6
+ @tree = tree
7
+ end
8
+
9
+ def render
10
+ output = ''
11
+ tree.each { |node| node.render(output) }
12
+ output
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,88 @@
1
+ module MinimalMarkdown
2
+ class ImplementorBase
3
+ def multiline?
4
+ false
5
+ end
6
+
7
+ def update_tree(tree)
8
+ tree.each { |node| update_node(node) }
9
+ end
10
+
11
+ private
12
+
13
+ def update_node(node)
14
+ if node.is_a?(Text)
15
+ if node.text.is_a?(Array)
16
+ update_tree(node.text)
17
+ else
18
+ out = []
19
+ input = node.text
20
+ while result = input.match(@regex)
21
+ out << Text.new(result.pre_match + result[1]) unless result.pre_match.empty?
22
+ out << @node_class.new(Text.new(result[2]))
23
+ input = result[3] + result.post_match
24
+ end
25
+
26
+ if !out.empty?
27
+ out << Text.new(input) unless input.empty?
28
+ node.text = out
29
+ end
30
+ end
31
+ elsif node.respond_to?(:children)
32
+ update_tree(node.children)
33
+ else
34
+ update_node(node.child)
35
+ end
36
+ end
37
+ end
38
+
39
+ class BoldImplementor < ImplementorBase
40
+ def initialize
41
+ @regex = /(^|\W)\*(\S[^*]*|[^*]*\S)\*(\W|$)/
42
+ @node_class = Bold
43
+ end
44
+ end
45
+
46
+ class ItalicImplementor < ImplementorBase
47
+ def initialize
48
+ @regex = /(^|\W)_(\S[^_]*|[^_]*\S)_(\W|$)/
49
+ @node_class = Italic
50
+ end
51
+ end
52
+
53
+ class UnorderedListImplementor
54
+ def multiline?
55
+ true
56
+ end
57
+
58
+ def update_tree(tree)
59
+ tree.flat_map do |node|
60
+ if node.is_a?(String)
61
+ update_text(node)
62
+ else
63
+ node
64
+ end
65
+ end
66
+ end
67
+
68
+ private
69
+
70
+ def update_text(input)
71
+ out = []
72
+
73
+ while result = input.match(/(?:^[ ]*\*[ ]+[^\n]+(?:\n|$))+/m)
74
+ out << result.pre_match unless result.pre_match.empty?
75
+
76
+ children = result[0].split("\n").map do |line|
77
+ Text.new(line.gsub(/^\s*\*\s+/, ''))
78
+ end
79
+ out << UnorderedList.new(children)
80
+
81
+ input = result.post_match
82
+ end
83
+
84
+ out << input unless input.empty?
85
+ out
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,58 @@
1
+ require 'cgi'
2
+
3
+ module MinimalMarkdown
4
+ UnorderedList = Struct.new(:children) do
5
+ def render(output)
6
+ output << '<ul>'
7
+ children.each do |child|
8
+ output << '<li>'
9
+ child.render(output)
10
+ output << '</li>'
11
+ end
12
+ output << '</ul>'
13
+ end
14
+ end
15
+
16
+ Line = Struct.new(:child, :spacing) do
17
+ def render(output)
18
+ if spacing < 2
19
+ output << '<div>'
20
+ child.render(output)
21
+ output << '</div>'
22
+ else
23
+ output << '<p>'
24
+ child.render(output)
25
+ output << '</p>'
26
+ if spacing > 2
27
+ output << '<br>' * (spacing - 2)
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ Text = Struct.new(:text) do
34
+ def render(output)
35
+ if text.is_a?(Array)
36
+ text.each { |child| child.render(output) }
37
+ else
38
+ output << CGI.escapeHTML(text)
39
+ end
40
+ end
41
+ end
42
+
43
+ Bold = Struct.new(:child) do
44
+ def render(output)
45
+ output << '<b>'
46
+ child.render(output)
47
+ output << '</b>'
48
+ end
49
+ end
50
+
51
+ Italic = Struct.new(:child) do
52
+ def render(output)
53
+ output << '<i>'
54
+ child.render(output)
55
+ output << '</i>'
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,56 @@
1
+ require 'cgi'
2
+
3
+ module MinimalMarkdown
4
+ class Parser
5
+ DEFAULT_IMPLEMENTORS = [
6
+ UnorderedListImplementor,
7
+ BoldImplementor,
8
+ ItalicImplementor
9
+ ]
10
+
11
+ attr_reader :text, :implementors
12
+
13
+ def initialize(text, implementors: DEFAULT_IMPLEMENTORS)
14
+ @text = text
15
+ @implementors = implementors
16
+ end
17
+
18
+ def parse
19
+ prepared_text = text.strip.gsub("\r", "")
20
+
21
+ preline_implementors, postline_implementors = implementors.map(&:new).partition(&:multiline?)
22
+
23
+ preline_tree = run_implementors(preline_implementors, [prepared_text])
24
+ postline_tree = convert_to_lines(preline_tree)
25
+ run_implementors(postline_implementors, postline_tree)
26
+ end
27
+
28
+ private
29
+
30
+ def run_implementors(implementors, input_tree)
31
+ implementors.inject(input_tree) do |tree, implementor|
32
+ implementor.update_tree(tree)
33
+ end
34
+ end
35
+
36
+ def convert_to_lines(tree)
37
+ tree.flat_map do |node|
38
+ if node.is_a?(String)
39
+ parse_lines(node)
40
+ else
41
+ node
42
+ end
43
+ end
44
+ end
45
+
46
+ def parse_lines(text)
47
+ text.scan(/(.*)(\s*\n)*/).flat_map do |line, spaces|
48
+ if line.empty?
49
+ []
50
+ else
51
+ Line.new(Text.new(line), spaces&.count("\n") || 0)
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,3 @@
1
+ module MinimalMarkdown
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,12 @@
1
+ require_relative 'minimal_markdown/version'
2
+ require_relative 'minimal_markdown/nodes'
3
+ require_relative 'minimal_markdown/implementors'
4
+ require_relative 'minimal_markdown/parser'
5
+ require_relative 'minimal_markdown/html_renderer'
6
+
7
+ module MinimalMarkdown
8
+ def self.render(text)
9
+ tree = MinimalMarkdown::Parser.new(text).parse
10
+ MinimalMarkdown::HtmlRenderer.new(tree).render
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ require_relative 'lib/minimal_markdown/version'
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = 'minimal_markdown'
5
+ s.version = MinimalMarkdown::VERSION
6
+ s.summary = "Minimal markdown renderer"
7
+ s.description = "A lightweight and minimal Ruby implementation of a Markdown renderer, designed with security in mind."
8
+ s.authors = ["Roger Nesbitt"]
9
+ s.email = 'roger@seriousorange.com'
10
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
11
+ s.homepage = 'https://github.com/mogest/minimal_markdown'
12
+ s.license = 'MIT'
13
+
14
+ s.add_development_dependency 'rspec', '~> 3.8.0'
15
+ end
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: minimal_markdown
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Roger Nesbitt
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-08-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 3.8.0
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 3.8.0
27
+ description: A lightweight and minimal Ruby implementation of a Markdown renderer,
28
+ designed with security in mind.
29
+ email: roger@seriousorange.com
30
+ executables: []
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - Gemfile
35
+ - Gemfile.lock
36
+ - LICENSE
37
+ - README.md
38
+ - lib/minimal_markdown.rb
39
+ - lib/minimal_markdown/html_renderer.rb
40
+ - lib/minimal_markdown/implementors.rb
41
+ - lib/minimal_markdown/nodes.rb
42
+ - lib/minimal_markdown/parser.rb
43
+ - lib/minimal_markdown/version.rb
44
+ - minimal_markdown.gemspec
45
+ homepage: https://github.com/mogest/minimal_markdown
46
+ licenses:
47
+ - MIT
48
+ metadata: {}
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ requirements: []
64
+ rubyforge_project:
65
+ rubygems_version: 2.7.6
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: Minimal markdown renderer
69
+ test_files: []