module Puppet::Util::Docs

Some simple methods for helping manage automatic documentation generation.

Constants

HEADER_LEVELS

Attributes

doc[W]
nodoc[R]

There is nothing that would ever set this. It gets read in reference/type.rb, but will never have any value but nil.

Public Instance Methods

desc(str) click to toggle source

Specify the actual doc string.

# File lib/puppet/util/docs.rb, line 4
def desc(str)
  @doc = str
end
doc() click to toggle source

Generate the full doc string.

# File lib/puppet/util/docs.rb, line 20
def doc
  extra = methods.find_all { |m| m.to_s =~ /^dochook_.+/ }.sort.collect { |m|
    self.send(m)
  }.delete_if {|r| r.nil? }.collect {|r| "* #{r}"}.join("\n")

  if @doc
    scrub(@doc) + (extra.empty? ? '' : "\n\n#{extra}")
  else
    extra
  end
end
dochook(name, &block) click to toggle source

Add a new autodoc block. We have to define these as class methods, rather than just sticking them in a hash, because otherwise they’re too difficult to do inheritance with.

# File lib/puppet/util/docs.rb, line 11
def dochook(name, &block)
  method = "dochook_#{name}"

  meta_def method, &block
end
doctable(headers, data) click to toggle source

Build a table

# File lib/puppet/util/docs.rb, line 33
def doctable(headers, data)
  str = "\n\n"

  lengths = []
  # Figure out the longest field for all columns
  data.each do |name, values|
    [name, values].flatten.each_with_index do |value, i|
      lengths[i] ||= 0
      lengths[i] = value.to_s.length if value.to_s.length > lengths[i]
    end
  end

  # The headers could also be longest
  headers.each_with_index do |value, i|
    lengths[i] = value.to_s.length if value.to_s.length > lengths[i]
  end

  # Add the header names
  str += headers.zip(lengths).collect { |value, num| pad(value, num) }.join(" | ") + " |" + "\n"

  # And the header row
  str += lengths.collect { |num| "-" * num }.join(" | ") + " |" + "\n"

  # Now each data row
  data.sort { |a, b| a[0].to_s <=> b[0].to_s }.each do |name, rows|
    str += [name, rows].flatten.zip(lengths).collect do |value, length|
      pad(value, length)
    end.join(" | ") + " |" + "\n"
  end

  str + "\n"
end
markdown_definitionlist(term, definition) click to toggle source
# File lib/puppet/util/docs.rb, line 83
def markdown_definitionlist(term, definition)
  lines = scrub(definition).split("\n")
  str = "#{term}\n: #{lines.shift}\n"
  lines.each do |line|
    str << "  " if line =~ /\S/
    str << "#{line}\n"
  end
  str << "\n"
end
markdown_header(name, level) click to toggle source
# File lib/puppet/util/docs.rb, line 79
def markdown_header(name, level)
  "#{HEADER_LEVELS[level]} #{name}\n\n"
end
nodoc?() click to toggle source
# File lib/puppet/util/docs.rb, line 68
def nodoc?
  nodoc
end
pad(value, length) click to toggle source

Pad a field with spaces

# File lib/puppet/util/docs.rb, line 73
def pad(value, length)
  value.to_s + (" " * (length - value.to_s.length))
end

Public Class Methods

scrub(text) click to toggle source

Strip indentation and trailing whitespace from embedded doc fragments.

Multi-line doc fragments are sometimes indented in order to preserve the formatting of the code they’re embedded in. Since indents are syntactic elements in Markdown, we need to make sure we remove any indent that was added solely to preserve surrounding code formatting, but LEAVE any indent that delineates a Markdown element (code blocks, multi-line bulleted list items). We can do this by removing the *least common indent* from each line.

Least common indent is defined as follows:

  • Find the smallest amount of leading space on any line…

  • …excluding the first line (which may have zero indent without affecting the common indent)…

  • …and excluding lines that consist solely of whitespace.

  • The least common indent may be a zero-length string, if the fragment is not indented to match code.

  • If there are hard tabs for some dumb reason, we assume they’re at least consistent within this doc fragment.

See tests in spec/unit/util/docs_spec.rb for examples.

# File lib/puppet/util/docs.rb, line 114
def scrub(text)
  # One-liners are easy! (One-liners may be buffered with extra newlines.)
  return text.strip if text.strip !~ /\n/
  excluding_first_line = text.partition("\n").last
  indent = excluding_first_line.scan(/^[ \t]*(?=\S)/).min || '' # prevent nil
  # Clean hanging indent, if any
  if indent.length > 0
    text = text.gsub(/^#{indent}/, '')
  end
  # Clean trailing space
  text.lines.map{|line|line.rstrip}.join("\n").rstrip
end