gitlab-markup 1.5.0.pre

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 (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +5 -0
  3. data/.gitlab-ci.yml +27 -0
  4. data/.kick +26 -0
  5. data/.travis.yml +21 -0
  6. data/CONTRIBUTING.md +49 -0
  7. data/Gemfile +13 -0
  8. data/HISTORY.md +128 -0
  9. data/LICENSE +20 -0
  10. data/README.md +58 -0
  11. data/Rakefile +17 -0
  12. data/bin/github-markup +10 -0
  13. data/gitlab-markup.gemspec +25 -0
  14. data/lib/github-markup.rb +6 -0
  15. data/lib/github/commands/rest2html +200 -0
  16. data/lib/github/markup.rb +55 -0
  17. data/lib/github/markup/command_implementation.rb +71 -0
  18. data/lib/github/markup/gem_implementation.rb +30 -0
  19. data/lib/github/markup/implementation.rb +28 -0
  20. data/lib/github/markup/markdown.rb +60 -0
  21. data/lib/github/markup/rdoc.rb +26 -0
  22. data/lib/github/markups.rb +50 -0
  23. data/script/bootstrap +8 -0
  24. data/script/cibuild +20 -0
  25. data/test/fixtures/fail.sh +3 -0
  26. data/test/markup_test.rb +116 -0
  27. data/test/markups/README.asciidoc +23 -0
  28. data/test/markups/README.asciidoc.html +59 -0
  29. data/test/markups/README.creole +34 -0
  30. data/test/markups/README.creole.html +20 -0
  31. data/test/markups/README.litcoffee +59 -0
  32. data/test/markups/README.litcoffee.html +66 -0
  33. data/test/markups/README.markdown +2 -0
  34. data/test/markups/README.markdown.html +4 -0
  35. data/test/markups/README.mediawiki +30 -0
  36. data/test/markups/README.mediawiki.html +60 -0
  37. data/test/markups/README.noformat +2 -0
  38. data/test/markups/README.noformat.html +2 -0
  39. data/test/markups/README.org +131 -0
  40. data/test/markups/README.org.html +139 -0
  41. data/test/markups/README.pod +88 -0
  42. data/test/markups/README.pod.html +85 -0
  43. data/test/markups/README.rdoc +6 -0
  44. data/test/markups/README.rdoc.html +12 -0
  45. data/test/markups/README.rmd +3 -0
  46. data/test/markups/README.rmd.html +6 -0
  47. data/test/markups/README.rst +79 -0
  48. data/test/markups/README.rst.html +91 -0
  49. data/test/markups/README.rst.txt +21 -0
  50. data/test/markups/README.rst.txt.html +37 -0
  51. data/test/markups/README.textile +2 -0
  52. data/test/markups/README.textile.html +4 -0
  53. data/test/markups/README.toc.rst +30 -0
  54. data/test/markups/README.toc.rst.html +32 -0
  55. data/test/markups/README.txt +2 -0
  56. data/test/markups/README.txt.html +2 -0
  57. metadata +216 -0
@@ -0,0 +1,55 @@
1
+ require "github/markup/command_implementation"
2
+ require "github/markup/gem_implementation"
3
+
4
+ module GitHub
5
+ module Markup
6
+ extend self
7
+ @@markups = []
8
+
9
+ def markups
10
+ @@markups
11
+ end
12
+
13
+ def preload!
14
+ markups.each do |markup|
15
+ markup.load
16
+ end
17
+ end
18
+
19
+ def render(filename, content = nil)
20
+ content ||= File.read(filename)
21
+
22
+ if impl = renderer(filename)
23
+ impl.render(content)
24
+ else
25
+ content
26
+ end
27
+ end
28
+
29
+ def markup(file, pattern, opts = {}, &block)
30
+ markups << GemImplementation.new(pattern, file, &block)
31
+ end
32
+
33
+ def command(command, regexp, name, &block)
34
+ if File.exist?(file = File.dirname(__FILE__) + "/commands/#{command}")
35
+ command = file
36
+ end
37
+
38
+ markups << CommandImplementation.new(regexp, command, name, &block)
39
+ end
40
+
41
+ def can_render?(filename)
42
+ !!renderer(filename)
43
+ end
44
+
45
+ def renderer(filename)
46
+ markups.find { |impl|
47
+ impl.match?(filename)
48
+ }
49
+ end
50
+
51
+ # Define markups
52
+ markups_rb = File.dirname(__FILE__) + '/markups.rb'
53
+ instance_eval File.read(markups_rb), markups_rb
54
+ end
55
+ end
@@ -0,0 +1,71 @@
1
+ begin
2
+ require "posix-spawn"
3
+ rescue LoadError
4
+ require "open3"
5
+ end
6
+
7
+ require "github/markup/implementation"
8
+
9
+ module GitHub
10
+ module Markup
11
+ class CommandError < RuntimeError
12
+ end
13
+
14
+ class CommandImplementation < Implementation
15
+ attr_reader :command, :block, :name
16
+
17
+ def initialize(regexp, command, name, &block)
18
+ super regexp
19
+ @command = command.to_s
20
+ @block = block
21
+ @name = name
22
+ end
23
+
24
+ def render(content)
25
+ rendered = execute(command, content)
26
+ rendered = rendered.to_s.empty? ? content : rendered
27
+ call_block(rendered, content)
28
+ end
29
+
30
+ private
31
+ def call_block(rendered, content)
32
+ if block && block.arity == 2
33
+ block.call(rendered, content)
34
+ elsif block
35
+ block.call(rendered)
36
+ else
37
+ rendered
38
+ end
39
+ end
40
+
41
+ if defined?(POSIX::Spawn)
42
+ def execute(command, target)
43
+ spawn = POSIX::Spawn::Child.new(*command, :input => target)
44
+ if spawn.status.success?
45
+ sanitize(spawn.out, target.encoding)
46
+ else
47
+ raise CommandError.new(spawn.err.strip)
48
+ end
49
+ end
50
+ else
51
+ def execute(command, target)
52
+ output = Open3.popen3(*command) do |stdin, stdout, stderr, wait_thr|
53
+ stdin.puts target
54
+ stdin.close
55
+ if wait_thr.value.success?
56
+ stdout.readlines
57
+ else
58
+ raise CommandError.new(stderr.readlines.join('').strip)
59
+ end
60
+ end
61
+ sanitize(output.join(''), target.encoding)
62
+ end
63
+ end
64
+
65
+ def sanitize(input, encoding)
66
+ input.gsub("\r", '').force_encoding(encoding)
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,30 @@
1
+ require "github/markup/implementation"
2
+
3
+ module GitHub
4
+ module Markup
5
+ class GemImplementation < Implementation
6
+ attr_reader :gem_name, :renderer
7
+
8
+ def initialize(regexp, gem_name, &renderer)
9
+ super regexp
10
+ @gem_name = gem_name.to_s
11
+ @renderer = renderer
12
+ end
13
+
14
+ def load
15
+ return if @loaded
16
+ require gem_name
17
+ @loaded = true
18
+ end
19
+
20
+ def render(content)
21
+ load
22
+ renderer.call(content)
23
+ end
24
+
25
+ def name
26
+ gem_name
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,28 @@
1
+ module GitHub
2
+ module Markup
3
+ class Implementation
4
+ attr_reader :regexp
5
+
6
+ def initialize(regexp)
7
+ @regexp = regexp
8
+ end
9
+
10
+ def load
11
+ # no-op by default
12
+ end
13
+
14
+ def render(content)
15
+ raise NotImplementedError, "subclasses of GitHub::Markup::Implementation must define #render"
16
+ end
17
+
18
+ def match?(filename)
19
+ file_ext_regexp =~ filename
20
+ end
21
+
22
+ private
23
+ def file_ext_regexp
24
+ @file_ext_regexp ||= /\.(#{regexp})\z/
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,60 @@
1
+ require "github/markup/implementation"
2
+
3
+ module GitHub
4
+ module Markup
5
+ class Markdown < Implementation
6
+ MARKDOWN_GEMS = {
7
+ "github/markdown" => proc { |content|
8
+ GitHub::Markdown.render(content)
9
+ },
10
+ "redcarpet" => proc { |content|
11
+ Redcarpet::Markdown.new(Redcarpet::Render::HTML).render(content)
12
+ },
13
+ "rdiscount" => proc { |content|
14
+ RDiscount.new(content).to_html
15
+ },
16
+ "maruku" => proc { |content|
17
+ Maruku.new(content).to_html
18
+ },
19
+ "kramdown" => proc { |content|
20
+ Kramdown::Document.new(content).to_html
21
+ },
22
+ "bluecloth" => proc { |content|
23
+ BlueCloth.new(content).to_html
24
+ },
25
+ }
26
+
27
+ def initialize
28
+ super(/md|rmd|mkdn?|mdwn|mdown|markdown|litcoffee/i)
29
+ end
30
+
31
+ def load
32
+ return if @renderer
33
+ MARKDOWN_GEMS.each do |gem_name, renderer|
34
+ if try_require(gem_name)
35
+ @renderer = renderer
36
+ return
37
+ end
38
+ end
39
+ raise LoadError, "no suitable markdown gem found"
40
+ end
41
+
42
+ def render(content)
43
+ load
44
+ @renderer.call(content)
45
+ end
46
+
47
+ def name
48
+ "markdown"
49
+ end
50
+
51
+ private
52
+ def try_require(file)
53
+ require file
54
+ true
55
+ rescue LoadError
56
+ false
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,26 @@
1
+ require "github/markup/implementation"
2
+ require "rdoc"
3
+ require "rdoc/markup/to_html"
4
+
5
+ module GitHub
6
+ module Markup
7
+ class RDoc < Implementation
8
+ def initialize
9
+ super /rdoc/
10
+ end
11
+
12
+ def render(content)
13
+ if ::RDoc::VERSION.to_i >= 4
14
+ h = ::RDoc::Markup::ToHtml.new(::RDoc::Options.new)
15
+ else
16
+ h = ::RDoc::Markup::ToHtml.new
17
+ end
18
+ h.convert(content)
19
+ end
20
+
21
+ def name
22
+ "rdoc"
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,50 @@
1
+ require "github/markup/markdown"
2
+ require "github/markup/rdoc"
3
+ require "shellwords"
4
+
5
+ markups << GitHub::Markup::Markdown.new
6
+
7
+ markup(:redcloth, /textile/) do |content|
8
+ RedCloth.new(content).to_html
9
+ end
10
+
11
+ markups << GitHub::Markup::RDoc.new
12
+
13
+ markup('org-ruby', /org/) do |content|
14
+ Orgmode::Parser.new(content, {
15
+ :allow_include_files => false,
16
+ :skip_syntax_highlight => true
17
+ }).to_html
18
+ end
19
+
20
+ markup(:creole, /creole/) do |content|
21
+ Creole.creolize(content)
22
+ end
23
+
24
+ markup(:wikicloth, /mediawiki|wiki/) do |content|
25
+ wikicloth = WikiCloth::WikiCloth.new(:data => content)
26
+ WikiCloth::WikiBuffer::HTMLElement::ESCAPED_TAGS << 'tt'
27
+ wikicloth.to_html(:noedit => true)
28
+ end
29
+
30
+ markup(:asciidoctor, /adoc|asc(iidoc)?/) do |content|
31
+ Asciidoctor::Compliance.unique_id_start_index = 1
32
+ Asciidoctor.convert(content, :safe => :secure, :attributes => %w(showtitle=@ idprefix idseparator=- env=github env-github source-highlighter=html-pipeline))
33
+ end
34
+
35
+ command(
36
+ "python2 -S #{Shellwords.escape(File.dirname(__FILE__))}/commands/rest2html",
37
+ /re?st(\.txt)?/,
38
+ "restructuredtext"
39
+ )
40
+
41
+ # pod2html is nice enough to generate a full-on HTML document for us,
42
+ # so we return the favor by ripping out the good parts.
43
+ #
44
+ # Any block passed to `command` will be handed the command's STDOUT for
45
+ # post processing.
46
+ command('/usr/bin/env perl -MPod::Simple::HTML -e Pod::Simple::HTML::go', /pod/, "pod") do |rendered|
47
+ if rendered =~ /<!-- start doc -->\s*(.+)\s*<!-- end doc -->/mi
48
+ $1
49
+ end
50
+ end
@@ -0,0 +1,8 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ cd $(dirname "$0")/..
6
+
7
+ bundle install
8
+ easy_install docutils
@@ -0,0 +1,20 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ # GC customizations
6
+ export RUBY_GC_MALLOC_LIMIT=79000000
7
+ export RUBY_HEAP_MIN_SLOTS=800000
8
+ export RUBY_HEAP_FREE_MIN=100000
9
+ export RUBY_HEAP_SLOTS_INCREMENT=400000
10
+ export RUBY_HEAP_SLOTS_GROWTH_FACTOR=1
11
+
12
+ export PATH="/usr/share/rbenv/shims:$PATH"
13
+ export RBENV_VERSION="1.9.3"
14
+
15
+ # bootstrap gem environment changes
16
+ echo "Bootstrapping gem environment ..."
17
+
18
+ script/bootstrap --local
19
+
20
+ rake
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env bash
2
+
3
+ echo "failure message">&2 && false
@@ -0,0 +1,116 @@
1
+ # encoding: UTF-8
2
+
3
+ $LOAD_PATH.unshift File.dirname(__FILE__) + "/../lib"
4
+
5
+ require 'github/markup'
6
+ require 'minitest/autorun'
7
+ require 'html/pipeline'
8
+ require 'nokogiri'
9
+ require 'nokogiri/diff'
10
+
11
+ def normalize_html(text)
12
+ text.strip!
13
+ text.gsub!(/\s\s+/,' ')
14
+ text.gsub!(/\p{Pi}|\p{Pf}|&amp;quot;/u,'"')
15
+ text.gsub!("\u2026",'...')
16
+ text
17
+ end
18
+
19
+ def assert_html_equal(expected, actual, msg = nil)
20
+ assertion = Proc.new do
21
+ expected_doc = Nokogiri::HTML(expected) {|config| config.noblanks}
22
+ actual_doc = Nokogiri::HTML(actual) {|config| config.noblanks}
23
+
24
+ expected_doc.search('//text()').each {|node| node.content = normalize_html node.content}
25
+ actual_doc.search('//text()').each {|node| node.content = normalize_html node.content}
26
+
27
+ ignore_changes = {"+" => Regexp.union(/^\s*id=".*"\s*$/), "-" => nil}
28
+ expected_doc.diff(actual_doc) do |change, node|
29
+ if change != ' ' && !node.blank? then
30
+ break unless node.to_html =~ ignore_changes[change]
31
+ end
32
+ end
33
+ end
34
+ assert(assertion.call, msg)
35
+ end
36
+
37
+ class MarkupTest < Minitest::Test
38
+ class MarkupFilter < HTML::Pipeline::Filter
39
+ def call
40
+ filename = context[:filename]
41
+ GitHub::Markup.render(filename, File.read(filename)).strip.force_encoding("utf-8")
42
+ end
43
+ end
44
+
45
+ Pipeline = HTML::Pipeline.new [
46
+ MarkupFilter,
47
+ HTML::Pipeline::SanitizationFilter
48
+ ]
49
+
50
+ Dir['test/markups/README.*'].each do |readme|
51
+ next if readme =~ /html$/
52
+ markup = readme.split('/').last.gsub(/^README\./, '')
53
+
54
+ define_method "test_#{markup}" do
55
+ skip "Skipping MediaWiki test because wikicloth is currently not compatible with JRuby." if markup == "mediawiki" && RUBY_PLATFORM == "java"
56
+
57
+ source = File.read(readme)
58
+ expected_file = "#{readme}.html"
59
+ expected = File.read(expected_file).rstrip
60
+ actual = Pipeline.to_html(nil, :filename => readme)
61
+
62
+ if source != expected
63
+ assert(source != actual, "#{markup} did not render anything")
64
+ end
65
+
66
+ diff = IO.popen("diff -u - #{expected_file}", 'r+') do |f|
67
+ f.write actual
68
+ f.close_write
69
+ f.read
70
+ end
71
+
72
+ assert_html_equal expected, actual, <<message
73
+ #{File.basename expected_file}'s contents are not html equal to output:
74
+ #{diff}
75
+ message
76
+ end
77
+ end
78
+
79
+ def test_knows_what_it_can_and_cannot_render
80
+ assert_equal false, GitHub::Markup.can_render?('README.html')
81
+ assert_equal true, GitHub::Markup.can_render?('README.markdown')
82
+ assert_equal true, GitHub::Markup.can_render?('README.rmd')
83
+ assert_equal true, GitHub::Markup.can_render?('README.Rmd')
84
+ assert_equal false, GitHub::Markup.can_render?('README.cmd')
85
+ assert_equal true, GitHub::Markup.can_render?('README.litcoffee')
86
+ end
87
+
88
+ def test_each_render_has_a_name
89
+ assert_equal "markdown", GitHub::Markup.renderer('README.md').name
90
+ assert_equal "redcloth", GitHub::Markup.renderer('README.textile').name
91
+ assert_equal "rdoc", GitHub::Markup.renderer('README.rdoc').name
92
+ assert_equal "org-ruby", GitHub::Markup.renderer('README.org').name
93
+ assert_equal "creole", GitHub::Markup.renderer('README.creole').name
94
+ assert_equal "wikicloth", GitHub::Markup.renderer('README.wiki').name
95
+ assert_equal "asciidoctor", GitHub::Markup.renderer('README.adoc').name
96
+ assert_equal "restructuredtext", GitHub::Markup.renderer('README.rst').name
97
+ assert_equal "pod", GitHub::Markup.renderer('README.pod').name
98
+ end
99
+
100
+ def test_raises_error_if_command_exits_non_zero
101
+ GitHub::Markup.command('test/fixtures/fail.sh', /fail/, 'fail')
102
+ assert GitHub::Markup.can_render?('README.fail')
103
+ begin
104
+ GitHub::Markup.render('README.fail', "stop swallowing errors")
105
+ rescue GitHub::Markup::CommandError => e
106
+ assert_equal "failure message", e.message
107
+ else
108
+ fail "an exception was expected but was not raised"
109
+ end
110
+ end
111
+
112
+ def test_preserve_markup
113
+ content = "Noël"
114
+ assert_equal content.encoding.name, GitHub::Markup.render('Foo.rst', content).encoding.name
115
+ end
116
+ end