class Ronn::Document

The Document class can be used to load and inspect a ronn document and to convert a ronn document into other formats, like roff or HTML.

Ronn files may optionally follow the naming convention: “<name>.<section>.ronn”. The <name> and <section> are used in generated documentation unless overridden by the information extracted from the document's name section.

Attributes

data[R]

The raw input data, read from path or stream and unmodified.

date[RW]

The date the document was published; center displayed in the document footer.

index[RW]

The index used to resolve man and file references.

manual[RW]

The manual this document belongs to; center displayed in the header.

name[RW]

The man pages name: usually a single word name of a program or filename; displayed along with the section in the left and right portions of the header as well as the bottom right section of the footer.

organization[RW]

The name of the group, organization, or individual responsible for this document; displayed in the left portion of the footer.

path[R]

Path to the Ronn document. This may be '-' or nil when the Ronn::Document object is created with a stream.

section[RW]

The man page's section: a string whose first character is numeric; displayed in parenthesis along with the name.

styles[RW]

Array of style modules to apply to the document.

tagline[RW]

Single sentence description of the thing being described by this man page; displayed in the NAME section.

Public Class Methods

new(path=nil, attributes={}, &block) click to toggle source

Create a Ronn::Document given a path or with the data returned by calling the block. The document is loaded and preprocessed before the intialize method returns. The attributes hash may contain values for any writeable attributes defined on this class.

   # File lib/ronn/document.rb
64 def initialize(path=nil, attributes={}, &block)
65   @path = path
66   @basename = path.to_s =~ /^-?$/ ? nil : File.basename(path)
67   @reader = block ||
68     lambda do |f|
69       if ['-', nil].include?(f)
70         STDIN.read
71       else
72         File.read(f)
73       end
74     end
75   @data = @reader.call(path)
76   @name, @section, @tagline = sniff
77 
78   @styles = %w[man]
79   @manual, @organization, @date = nil
80   @markdown, @input_html, @html = nil
81   @index = Ronn::Index[path || '.']
82   @index.add_manual(self) if path && name
83 
84   attributes.each { |attr_name,value| send("#{attr_name}=", value) }
85 end

Public Instance Methods

basename(type=nil) click to toggle source

Generate a file basename of the form “<name>.<section>.<type>” for the given file extension. Uses the name and section from the source file path but falls back on the name and section defined in the document.

   # File lib/ronn/document.rb
91 def basename(type=nil)
92   type = nil if ['', 'roff'].include?(type.to_s)
93   [path_name || @name, path_section || @section, type].
94   compact.join('.')
95 end
convert(format) click to toggle source

Convert the document to :roff, :html, or :html_fragment and return the result as a string.

    # File lib/ronn/document.rb
220 def convert(format)
221   send "to_#{format}"
222 end
html() click to toggle source

A Hpricot::Document for the manual content fragment.

    # File lib/ronn/document.rb
214 def html
215   @html ||= process_html!
216 end
markdown() click to toggle source

Preprocessed markdown input text.

    # File lib/ronn/document.rb
209 def markdown
210   @markdown ||= process_markdown!
211 end
name?() click to toggle source

Truthful when the name was extracted from the name section of the document.

    # File lib/ronn/document.rb
129 def name?
130   !@name.nil?
131 end
path_for(type=nil) click to toggle source

Construct a path for a file near the source file. Uses the Document#basename method to generate the basename part and appends it to the dirname of the source document.

    # File lib/ronn/document.rb
100 def path_for(type=nil)
101   if @basename
102     File.join(File.dirname(path), basename(type))
103   else
104     basename(type)
105   end
106 end
path_name() click to toggle source

Returns the <name> part of the path, or nil when no path is available. This is used as the manual page name when the file contents do not include a name section.

    # File lib/ronn/document.rb
111 def path_name
112   @basename[/^[^.]+/] if @basename
113 end
path_section() click to toggle source

Returns the <section> part of the path, or nil when no path is available.

    # File lib/ronn/document.rb
117 def path_section
118   $1 if @basename.to_s =~ /\.(\d\w*)\./
119 end
reference_name() click to toggle source

The name used to reference this manual.

    # File lib/ronn/document.rb
146 def reference_name
147   name + (section && "(#{section})").to_s
148 end
section?() click to toggle source

True when the section number was extracted from the name section of the document.

    # File lib/ronn/document.rb
141 def section?
142   !@section.nil?
143 end
section_heads()
Alias for: toc
sniff() click to toggle source

Sniff the document header and extract basic document metadata. Return a tuple of the form: [name, section, description], where missing information is represented by nil and any element may be missing.

    # File lib/ronn/document.rb
190 def sniff
191   html = Markdown.new(data[0, 512]).to_html
192   heading, html = html.split("</h1>\n", 2)
193   return [nil, nil, nil] if html.nil?
194 
195   case heading
196   when /([\w_.\[\]~+=@:-]+)\s*\((\d\w*)\)\s*-+\s*(.*)/
197     # name(section) -- description
198     [$1, $2, $3]
199   when /([\w_.\[\]~+=@:-]+)\s+-+\s+(.*)/
200     # name -- description
201     [$1, nil, $2]
202   else
203     # description
204     [nil, nil, heading.sub('<h1>', '')]
205   end
206 end
styles=(styles) click to toggle source

Styles to insert in the generated HTML output. This is a simple Array of string module names or file paths.

    # File lib/ronn/document.rb
183 def styles=(styles)
184   @styles = (%w[man] + styles).uniq
185 end
title() click to toggle source

The document's title when no name section was defined. When a name section exists, this value is nil.

    # File lib/ronn/document.rb
159 def title
160   @tagline if !name?
161 end
title?() click to toggle source

Truthful when the document started with an h1 but did not follow the “<name>(<sect>) – <tagline>” convention. We assume this is some kind of custom title.

    # File lib/ronn/document.rb
153 def title?
154   !name? && tagline
155 end
to_h() click to toggle source
    # File lib/ronn/document.rb
263 def to_h
264   %w[name section tagline manual organization date styles toc].
265   inject({}) { |hash, name| hash[name] = send(name); hash }
266 end
to_html() click to toggle source

Convert the document to HTML and return the result as a string.

    # File lib/ronn/document.rb
234 def to_html
235   if layout = ENV['RONN_LAYOUT']
236     if !File.exist?(layout_path = File.expand_path(layout))
237       warn "warn: can't find #{layout}, using default layout."
238       layout_path = nil
239     end
240   end
241 
242   template = Ronn::Template.new(self)
243   template.context.push :html => to_html_fragment(wrap_class=nil)
244   template.render(layout_path || 'default')
245 end
to_html_fragment(wrap_class='mp') click to toggle source

Convert the document to HTML and return the result as a string. The HTML does not include <html>, <head>, or <style> tags.

    # File lib/ronn/document.rb
250 def to_html_fragment(wrap_class='mp')
251   return html.to_s if wrap_class.nil?
252   [
253     "<div class='#{wrap_class}'>",
254     html.to_s,
255     "</div>"
256   ].join("\n")
257 end
to_json() click to toggle source
    # File lib/ronn/document.rb
273 def to_json
274   require 'json'
275   to_h.merge('date' => date.iso8601).to_json
276 end
to_markdown() click to toggle source
    # File lib/ronn/document.rb
259 def to_markdown
260   markdown
261 end
to_roff() click to toggle source

Convert the document to roff and return the result as a string.

    # File lib/ronn/document.rb
225 def to_roff
226   RoffFilter.new(
227     to_html_fragment(wrap_class=nil),
228     name, section, tagline,
229     manual, organization, date
230   ).to_s
231 end
to_yaml() click to toggle source
    # File lib/ronn/document.rb
268 def to_yaml
269   require 'yaml'
270   to_h.to_yaml
271 end
toc() click to toggle source

Retrieve a list of top-level section headings in the document and return as an array of +[id, text]+ tuples, where id is the element's generated id and text is the inner text of the heading element.

    # File lib/ronn/document.rb
175 def toc
176   @toc ||=
177     html.search('h2[@id]').map { |h2| [h2.attributes['id'], h2.inner_text] }
178 end
Also aliased as: section_heads

Protected Instance Methods

html_filter_angle_quotes() click to toggle source

Perform angle quote (<THESE>) post filtering.

    # File lib/ronn/document.rb
355 def html_filter_angle_quotes
356   # convert all angle quote vars nested in code blocks
357   # back to the original text
358   @html.search('code').search('text()').each do |node|
359     next unless node.to_html.include?('var&gt;')
360     new =
361       node.to_html.
362         gsub('&lt;var&gt;', '&lt;').
363         gsub("&lt;/var&gt;", '>')
364     node.swap(new)
365   end
366 end
html_filter_definition_lists() click to toggle source

Convert special format unordered lists to definition lists.

    # File lib/ronn/document.rb
369 def html_filter_definition_lists
370   # process all unordered lists depth-first
371   @html.search('ul').to_a.reverse.each do |ul|
372     items = ul.search('li')
373     next if items.any? { |item| item.inner_text.split("\n", 2).first !~ /:$/ }
374 
375     ul.name = 'dl'
376     items.each do |item|
377       if child = item.at('p')
378         wrap = '<p></p>'
379         container = child
380       else
381         wrap = '<dd></dd>'
382         container = item
383       end
384       term, definition = container.inner_html.split(":\n", 2)
385 
386       dt = item.before("<dt>#{term}</dt>").first
387       dt.attributes['class'] = 'flush' if dt.inner_text.length <= 7
388 
389       item.name = 'dd'
390       container.swap(wrap.sub(/></, ">#{definition}<"))
391     end
392   end
393 end
html_filter_heading_anchors() click to toggle source

Add URL anchors to all HTML heading elements.

    # File lib/ronn/document.rb
415 def html_filter_heading_anchors
416   @html.search('h2|h3|h4|h5|h6').not('[@id]').each do |heading|
417     heading.set_attribute('id', heading.inner_text.gsub(/\W+/, '-'))
418   end
419 end
html_filter_inject_name_section() click to toggle source
    # File lib/ronn/document.rb
395 def html_filter_inject_name_section
396   markup =
397     if title?
398       "<h1>#{title}</h1>"
399     elsif name
400       "<h2>NAME</h2>\n" +
401       "<p class='man-name'>\n  <code>#{name}</code>" +
402       (tagline ? " - <span class='man-whatis'>#{tagline}</span>\n" : "\n") +
403       "</p>\n"
404     end
405   if markup
406     if @html.children
407       @html.at("*").before(markup)
408     else
409       @html = Hpricot(markup)
410     end
411   end
412 end
input_html() click to toggle source
    # File lib/ronn/document.rb
289 def input_html
290   @input_html ||= strip_heading(Markdown.new(markdown).to_html)
291 end
markdown_filter_angle_quotes(markdown) click to toggle source

Convert <WORD> to <var>WORD</var> but only if WORD isn't an HTML tag.

    # File lib/ronn/document.rb
341 def markdown_filter_angle_quotes(markdown)
342   markdown.gsub(/\<([^:.\/]+?)\>/) do |match|
343     contents = $1
344     tag, attrs = contents.split(' ', 2)
345     if attrs =~ /\/=/ || html_element?(tag.sub(/^\//, '')) ||
346        data.include?("</#{tag}>")
347       match.to_s
348     else
349       "<var>#{contents}</var>"
350     end
351   end
352 end
markdown_filter_heading_anchors(markdown) click to toggle source

Add [id]: #ANCHOR elements to the markdown source text for all sections. This lets us use the [SECTION-REF][] syntax

    # File lib/ronn/document.rb
328 def markdown_filter_heading_anchors(markdown)
329   first = true
330   markdown.split("\n").grep(/^[#]{2,5} +[\w '-]+[# ]*$/).each do |line|
331     markdown << "\n\n" if first
332     first = false
333     title = line.gsub(/[^\w -]/, '').strip
334     anchor = title.gsub(/\W+/, '-').gsub(/(^-+|-+$)/, '')
335     markdown << "[#{title}]: ##{anchor} \"#{title}\"\n"
336   end
337   markdown
338 end
preprocess!() click to toggle source

Parse the document and extract the name, section, and tagline from its contents. This is called while the object is being initialized.

    # File lib/ronn/document.rb
284 def preprocess!
285   input_html
286   nil
287 end
process_html!() click to toggle source
    # File lib/ronn/document.rb
304 def process_html!
305   @html = Hpricot(input_html)
306   html_filter_angle_quotes
307   html_filter_definition_lists
308   html_filter_inject_name_section
309   html_filter_heading_anchors
310   html_filter_annotate_bare_links
311   html_filter_manual_reference_links
312   @html
313 end
process_markdown!() click to toggle source
    # File lib/ronn/document.rb
298 def process_markdown!
299   markdown = markdown_filter_heading_anchors(self.data)
300   markdown_filter_link_index(markdown)
301   markdown_filter_angle_quotes(markdown)
302 end
strip_heading(html) click to toggle source
    # File lib/ronn/document.rb
293 def strip_heading(html)
294   heading, html = html.split("</h1>\n", 2)
295   html || heading
296 end