File: docs.rb

package info (click to toggle)
puppet-agent 8.10.0-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 27,404 kB
  • sloc: ruby: 286,820; sh: 492; xml: 116; makefile: 88; cs: 68
file content (132 lines) | stat: -rw-r--r-- 4,090 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# frozen_string_literal: true

# Some simple methods for helping manage automatic documentation generation.
module Puppet::Util::Docs
  # Specify the actual doc string.
  def desc(str)
    @doc = str
  end

  # 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.
  def dochook(name, &block)
    method = "dochook_#{name}"

    meta_def method, &block
  end

  attr_writer :doc

  # Generate the full doc string.
  def doc
    extra = methods.find_all { |m| m.to_s =~ /^dochook_.+/ }.sort.filter_map { |m|
      send(m)
    }.collect { |r| "* #{r}" }.join("\n")

    if @doc
      scrub(@doc) + (extra.empty? ? '' : "\n\n#{extra}")
    else
      extra
    end
  end

  # Build a table
  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

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

  def nodoc?
    nodoc
  end

  # Pad a field with spaces
  def pad(value, length)
    value.to_s + (" " * (length - value.to_s.length))
  end

  HEADER_LEVELS = [nil, "#", "##", "###", "####", "#####"]

  def markdown_header(name, level)
    "#{HEADER_LEVELS[level]} #{name}\n\n"
  end

  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

  # 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.
  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(&:rstrip).join("\n").rstrip
  end

  module_function :scrub
end