class Madness::Document

Handle a single markdown document.

Attributes

base[R]
dir[R]
file[R]
path[R]
title[R]
type[R]

Public Class Methods

new(path) click to toggle source
# File lib/madness/document.rb, line 13
def initialize(path)
  @path = path
  @base = path.empty? ? docroot : "#{docroot}/#{path}"
  @base.chomp! '/'
  set_base_attributes
end

Public Instance Methods

content() click to toggle source

Return the HTML for that document

# File lib/madness/document.rb, line 21
def content
  @content ||= content!
end
content!() click to toggle source

Return the HTML for that document, force re-read.

# File lib/madness/document.rb, line 26
def content!
  [:empty, :missing].include?(type) ? "<h1>#{title}</h1>" : markdown_to_html
end

Private Instance Methods

add_anchor_ids() click to toggle source

Add anchors with IDs before all headers

# File lib/madness/document.rb, line 90
def add_anchor_ids
  doc.walk do |node|
    if node.type == :header
      anchor = CommonMarker::Node.new(:inline_html)

      next unless node.first_child.type == :text
      
      anchor_id = node.first_child.string_content.to_slug
      anchor.string_content = "<a id='#{anchor_id}'></a>"
      node.prepend_child anchor
    end
  end
end
doc() click to toggle source
# File lib/madness/document.rb, line 72
def doc
  @doc ||= CommonMarker.render_doc markdown, :DEFAULT, [:table]
end
document_toc() click to toggle source

Returns a UL object containing the document table of contents

# File lib/madness/document.rb, line 117
def document_toc
  toc = []
  doc.walk do |node|
    next unless node.type == :header
    level = node.header_level
    next unless level.between? 2, 3
    text = node.first_child.string_content
    spacer = "  " * (level - 1)
    toc << "#{spacer}- [#{text}](##{text.to_slug})"
  end

  toc = toc.join "\n"
  CommonMarker.render_doc(toc).first_child
end
markdown() click to toggle source
# File lib/madness/document.rb, line 68
def markdown
  @markdown ||= File.read file
end
markdown_to_html() click to toggle source

Convert markdown to HTML, with some additional processing:

  1. Add anchors to headers

  2. Syntax highilghting

  3. Prepend H1 if needed

# File lib/madness/document.rb, line 80
def markdown_to_html
  replace_toc_marker
  prepend_h1 if config.auto_h1
  add_anchor_ids
  html = doc.to_html :UNSAFE
  html = syntax_highlight(html) if config.highlighter
  html
end
md_file?() click to toggle source
# File lib/madness/document.rb, line 158
def md_file?
  File.exist?("#{base}.md") || (File.exist?(base) && File.extname(base) == '.md')
end
md_filename() click to toggle source
# File lib/madness/document.rb, line 162
def md_filename
  File.extname(base) == '.md' ? base : "#{base}.md"
end
prepend_h1() click to toggle source

If the document does not start with an H1 tag, add it.

# File lib/madness/document.rb, line 133
def prepend_h1
  return unless doc.first_child
  return if doc.first_child.type == :header and doc.first_child.header_level == 1
  h1 = CommonMarker.render_doc("# #{title}").first_child
  doc.first_child.insert_before h1
end
replace_toc_marker() click to toggle source

Replace <!– TOC –> with a Table of Contents for the page

# File lib/madness/document.rb, line 105
def replace_toc_marker
  toc_marker = doc.find do |node|
    node.type == :html and node.string_content.include? "<!-- TOC -->"
  end

  return unless toc_marker

  toc_marker.insert_after document_toc
  toc_marker.insert_after CommonMarker.render_doc("## Table of Contents").first_child
end
set_base_attributes() click to toggle source

Identify file, dir and type. :readme - in case the path is a directory, and it contains index.md

or README.md

:file - in case the path is a *.md file :empty - in case it is a folder without README.md or index.md :missing - in any other case, we don't know (will trigger 404)

# File lib/madness/document.rb, line 38
def set_base_attributes
  @dir  = docroot
  @type = :missing
  @file = ''
  @title = 'Index'

  if File.directory? base
    @title = File.basename(path).to_label unless path.empty?
    set_base_attributes_for_directory
  elsif md_file?
    @file = md_filename
    @title = File.basename(base).to_label
    @dir  = File.dirname file
    @type = :file
  end
end
set_base_attributes_for_directory() click to toggle source
# File lib/madness/document.rb, line 55
def set_base_attributes_for_directory
  @dir  = base
  @type = :readme

  if File.exist? "#{base}/index.md"
    @file = "#{base}/index.md"
  elsif File.exist? "#{base}/README.md"
    @file = "#{base}/README.md"
  else
    @type = :empty
  end
end
syntax_highlight(html) click to toggle source

Apply syntax highlighting with CodeRay. This will parse for any <code class='LANG'> sections in the HTML, pass it to CodeRay for highlighting. Since CodeRay adds another HTML escaping, on top of what RDiscount does, we unescape it before passing it to CodeRay.

Open StackOverflow question: stackoverflow.com/questions/37771279/prevent-double-escaping-with-coderay-and-rdiscount

# File lib/madness/document.rb, line 148
def syntax_highlight(html)
  line_numbers = config.line_numbers ? :table : nil
  opts = { css: :style, wrap: nil, line_numbers: line_numbers }
  html.gsub(/\<code class="language-(.+?)"\>(.+?)\<\/code\>/m) do
    lang, code = $1, $2
    code = CGI.unescapeHTML code
    CodeRay.scan(code, lang).html opts
  end
end