class Ronn::Document
extracted from the document’s name section.
generated documentation unless overridden by the information
“<name>.<section>.ronn”. The <name> and <section> are used in
Ronn files may optionally follow the naming convention:
HTML.
and to convert a ronn document into other formats, like roff or
The Document class can be used to load and inspect a ronn document
def basename(type=nil)
the source file path but falls back on the name and section
for the given file extension. Uses the name and section from
Generate a file basename of the form "
def basename(type=nil) type = nil if ['', 'roff'].include?(type.to_s) [path_name || @name, path_section || @section, type]. compact.join('.') end
def convert(format)
Convert the document to :roff, :html, or :html_fragment and
def convert(format) send "to_#{format}" end
def date
this is the file's modified time or, if no file is given,
The date the man page was published. If not set explicitly,
def date return @date if @date return File.mtime(path) if File.exist?(path) Time.now end
def html
def html @html ||= process_html! end
def html_filter_angle_quotes
def html_filter_angle_quotes # convert all angle quote vars nested in code blocks # back to the original text @html.search('code').search('text()').each do |node| next unless node.to_html.include?('var>') new = node.to_html. gsub('<var>', '<'). gsub("</var>", '>') node.swap(new) end end
def html_filter_annotate_bare_links
Add a 'data-bare-link' attribute to hyperlinks
def html_filter_annotate_bare_links @html.search('a[@href]').each do |node| href = node.attributes['href'] text = node.inner_text if href == text || href[0] == ?# || CGI.unescapeHTML(href) == "mailto:#{CGI.unescapeHTML(text)}" then node.set_attribute('data-bare-link', 'true') end end end
def html_filter_definition_lists
def html_filter_definition_lists # process all unordered lists depth-first @html.search('ul').to_a.reverse.each do |ul| items = ul.search('li') next if items.any? { |item| item.inner_text.split("\n", 2).first !~ /:$/ } ul.name = 'dl' items.each do |item| if child = item.at('p') wrap = '<p></p>' container = child else wrap = '<dd></dd>' container = item end term, definition = container.inner_html.split(":\n", 2) dt = item.before("<dt>#{term}</dt>").first dt.attributes['class'] = 'flush' if dt.inner_text.length <= 7 item.name = 'dd' container.swap(wrap.sub(/></, ">#{definition}<")) end end end
def html_filter_heading_anchors
def html_filter_heading_anchors @html.search('h2|h3|h4|h5|h6').not('[@id]').each do |heading| heading.set_attribute('id', heading.inner_text.gsub(/\W+/, '-')) end end
def html_filter_inject_name_section
def html_filter_inject_name_section markup = if title? "<h1>#{title}</h1>" elsif name "<h2>NAME</h2>\n" + "<p class='man-name'>\n <code>#{name}</code>" + (tagline ? " - <span class='man-whatis'>#{tagline}</span>\n" : "\n") + "</p>\n" end if markup if @html.children @html.at("*").before(markup) else @html = Hpricot(markup) end end end
def html_filter_manual_reference_links
Convert text of the form "name(section)" to a hyperlink. The URL is
def html_filter_manual_reference_links return if index.nil? @html.search('text()').each do |node| next if !node.content.include?(')') next if %w[pre code h1 h2 h3].include?(node.parent.name) next if child_of?(node, 'a') node.swap( node.content.gsub(/([0-9A-Za-z_:.+=@~-]+)(\(\d+\w*\))/) { name, sect = $1, $2 if ref = index["#{name}#{sect}"] "<a class='man-ref' href='#{ref.url}'>#{name}<span class='s'>#{sect}</span></a>" else # warn "warn: manual reference not defined: '#{name}#{sect}'" "<span class='man-ref'>#{name}<span class='s'>#{sect}</span></span>" end } ) end end
def initialize(path=nil, attributes={}, &block)
the intialize method returns. The attributes hash may contain values
calling the block. The document is loaded and preprocessed before
Create a Ronn::Document given a path or with the data returned by
def initialize(path=nil, attributes={}, &block) @path = path @basename = path.to_s =~ /^-?$/ ? nil : File.basename(path) @reader = block || lambda do |f| if ['-', nil].include?(f) STDIN.read else File.read(f) end end @data = @reader.call(path) @name, @section, @tagline = sniff @styles = %w[man] @manual, @organization, @date = nil @markdown, @input_html, @html = nil @index = Ronn::Index[path || '.'] @index.add_manual(self) if path && name attributes.each { |attr_name,value| send("#{attr_name}=", value) } end
def input_html
def input_html @input_html ||= strip_heading(Markdown.new(markdown).to_html) end
def markdown
def markdown @markdown ||= process_markdown! end
def markdown_filter_angle_quotes(markdown)
def markdown_filter_angle_quotes(markdown) markdown.gsub(/\<([^:.\/]+?)\>/) do |match| contents = $1 tag, attrs = contents.split(' ', 2) if attrs =~ /\/=/ || html_element?(tag.sub(/^\//, '')) || data.include?("</#{tag}>") match.to_s else "<var>#{contents}</var>" end end end
def markdown_filter_heading_anchors(markdown)
Add [id]: #ANCHOR elements to the markdown source text for all sections.
def markdown_filter_heading_anchors(markdown) first = true markdown.split("\n").grep(/^[#]{2,5} +[\w '-]+[# ]*$/).each do |line| markdown << "\n\n" if first first = false title = line.gsub(/[^\w -]/, '').strip anchor = title.gsub(/\W+/, '-').gsub(/(^-+|-+$)/, '') markdown << "[#{title}]: ##{anchor} \"#{title}\"\n" end markdown end
def markdown_filter_link_index(markdown)
Appends all index links to the end of the document as Markdown reference
def markdown_filter_link_index(markdown) return markdown if index.nil? || index.empty? markdown << "\n\n" index.each { |ref| markdown << "[#{ref.name}]: #{ref.url}\n" } end
def name
Returns the manual page name based first on the document's
def name @name || path_name end
def name?
Truthful when the name was extracted from the name section
def name? !@name.nil? end
def path_for(type=nil)
Document#basename method to generate the basename part and
Construct a path for a file near the source file. Uses the
def path_for(type=nil) if @basename File.join(File.dirname(path), basename(type)) else basename(type) end end
def path_name
available. This is used as the manual page name when the
Returns the
def path_name @basename[/^[^.]+/] if @basename end
def path_section
Returns the
def path_section $1 if @basename.to_s =~ /\.(\d\w*)\./ end
def preprocess!
Parse the document and extract the name, section, and tagline from its
def preprocess! input_html nil end
def process_html!
def process_html! @html = Hpricot(input_html) html_filter_angle_quotes html_filter_definition_lists html_filter_inject_name_section html_filter_heading_anchors html_filter_annotate_bare_links html_filter_manual_reference_links @html end
def process_markdown!
def process_markdown! markdown = markdown_filter_heading_anchors(self.data) markdown_filter_link_index(markdown) markdown_filter_angle_quotes(markdown) end
def reference_name
def reference_name name + (section && "(#{section})").to_s end
def section
Returns the manual page section based first on the document's
def section @section || path_section end
def section?
True when the section number was extracted from the name
def section? !@section.nil? end
def sniff
tuple of the form: [name, section, description], where missing information
Sniff the document header and extract basic document metadata. Return a
def sniff html = Markdown.new(data[0, 512]).to_html heading, html = html.split("</h1>\n", 2) return [nil, nil, nil] if html.nil? case heading when /([\w_.\[\]~+=@:-]+)\s*\((\d\w*)\)\s*-+\s*(.*)/ # name(section) -- description [$1, $2, $3] when /([\w_.\[\]~+=@:-]+)\s+-+\s+(.*)/ # name -- description [$1, nil, $2] else # description [nil, nil, heading.sub('<h1>', '')] end end
def strip_heading(html)
def strip_heading(html) heading, html = html.split("</h1>\n", 2) html || heading end
def styles=(styles)
Styles to insert in the generated HTML output. This is a simple Array of
def styles=(styles) @styles = (%w[man] + styles).uniq end
def title
The document's title when no name section was defined. When a name section
def title @tagline if !name? end
def title?
the "
Truthful when the document started with an h1 but did not follow
def title? !name? && tagline end
def to_h
def to_h %w[name section tagline manual organization date styles toc]. inject({}) { |hash, name| hash[name] = send(name); hash } end
def to_html
def to_html if layout = ENV['RONN_LAYOUT'] if !File.exist?(layout_path = File.expand_path(layout)) warn "warn: can't find #{layout}, using default layout." layout_path = nil end end template = Ronn::Template.new(self) template.context.push :html => to_html_fragment(wrap_class=nil) template.render(layout_path || 'default') end
def to_html_fragment(wrap_class='mp')
def to_json
def to_json require 'json' to_h.merge('date' => date.iso8601).to_json end
def to_markdown
def to_markdown markdown end
def to_roff
def to_roff RoffFilter.new( to_html_fragment(wrap_class=nil), name, section, tagline, manual, organization, date ).to_s end
def to_yaml
def to_yaml require 'yaml' to_h.to_yaml end
def toc
as an array of +[id, text]+ tuples, where +id+ is the element's generated
Retrieve a list of top-level section headings in the document and return
def toc @toc ||= html.search('h2[@id]').map { |h2| [h2.attributes['id'], h2.inner_text] } end