summaryrefslogtreecommitdiffstats
path: root/lib/puppet/interface/documentation.rb
blob: 48e9a8b1a5dea14b0e025f2b76d75f7f78b8037b (plain)
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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# This isn't usable outside Puppet::Interface; don't load it alone.
class Puppet::Interface
  module DocGen
    def self.strip_whitespace(text)
      text.gsub!(/[ \t\f]+$/, '')

      # We need to identify an indent: the minimum number of whitespace
      # characters at the start of any line in the text.
      #
      # Using split rather than each_line is because the later only takes a
      # block on Ruby 1.8.5 / Centos, and we support that. --daniel 2011-05-03
      indent = text.split(/\n/).map {|x| x.index(/[^\s]/) }.compact.min

      if indent > 0 then
        text.gsub!(/^[ \t\f]{0,#{indent}}/, '')
      end

      return text
    end

    # The documentation attributes all have some common behaviours; previously
    # we open-coded them across the set of six things, but that seemed
    # wasteful - especially given that they were literally the same, and had
    # the same bug hidden in them.
    #
    # This feels a bit like overkill, but at least the common code is common
    # now. --daniel 2011-04-29
    def attr_doc(name, &validate)
      # Now, which form of the setter do we want, validated or not?
      get_arg = "value.to_s"
      if validate
        define_method(:"_validate_#{name}", validate)
        get_arg = "_validate_#{name}(#{get_arg})"
      end

      # We use module_eval, which I don't like much, because we can't have an
      # argument to a block with a default value in Ruby 1.8, and I don't like
      # the side-effects (eg: no argument count validation) of using blocks
      # without as metheds.  When we are 1.9 only (hah!) you can totally
      # replace this with some up-and-up define_method. --daniel 2011-04-29
      module_eval(<<-EOT, __FILE__, __LINE__ + 1)
        def #{name}(value = nil)
          self.#{name} = value unless value.nil?
          @#{name}
        end

        def #{name}=(value)
          @#{name} = Puppet::Interface::DocGen.strip_whitespace(#{get_arg})
        end
      EOT
    end
  end

  module TinyDocs
    extend Puppet::Interface::DocGen

    attr_doc :summary do |value|
      value =~ /\n/ and
        raise ArgumentError, "Face summary should be a single line; put the long text in 'description' instead."
      value
    end

    attr_doc :description
  end

  module FullDocs
    extend Puppet::Interface::DocGen
    include TinyDocs

    attr_doc :examples
    attr_doc :notes
    attr_doc :license

    attr_doc :short_description
    def short_description(value = nil)
      self.short_description = value unless value.nil?
      if @short_description.nil? then
        return nil if @description.nil?
        lines = @description.split("\n")
        grab  = [5, lines.index('') || 5].min
        @short_description = lines[0, grab].join("\n")
      end
      @short_description
    end

    def author(value = nil)
      unless value.nil? then
        unless value.is_a? String
          raise ArgumentError, 'author must be a string; use multiple statements for multiple authors'
        end

        if value =~ /\n/ then
          raise ArgumentError, 'author should be a single line; use multiple statements for multiple authors'
        end
        @authors.push(Puppet::Interface::DocGen.strip_whitespace(value))
      end
      @authors.empty? ? nil : @authors.join("\n")
    end
    def authors
      @authors
    end
    def author=(value)
      if Array(value).any? {|x| x =~ /\n/ } then
        raise ArgumentError, 'author should be a single line; use multiple statements'
      end
      @authors = Array(value).map{|x| Puppet::Interface::DocGen.strip_whitespace(x) }
    end
    alias :authors= :author=

    def copyright(owner = nil, years = nil)
      if years.nil? and not owner.nil? then
        raise ArgumentError, 'copyright takes the owners names, then the years covered'
      end
      self.copyright_owner = owner unless owner.nil?
      self.copyright_years = years unless years.nil?

      if self.copyright_years or self.copyright_owner then
        "Copyright #{self.copyright_years} by #{self.copyright_owner}"
      else
        "Unknown copyright owner and years."
      end
    end

    attr_accessor :copyright_owner
    def copyright_owner=(value)
      case value
      when String then @copyright_owner = value
      when Array  then @copyright_owner = value.join(", ")
      else
        raise ArgumentError, "copyright owner must be a string or an array of strings"
      end
      @copyright_owner
    end

    attr_accessor :copyright_years
    def copyright_years=(value)
      years = munge_copyright_year value
      years = (years.is_a?(Array) ? years : [years]).
        sort_by do |x| x.is_a?(Range) ? x.first : x end

      @copyright_years = years.map do |year|
        if year.is_a? Range then
          "#{year.first}-#{year.last}"
        else
          year
        end
      end.join(", ")
    end

    def munge_copyright_year(input)
      case input
      when Range then input
      when Integer then
        if input < 1970 then
          fault = "before 1970"
        elsif input > (future = Time.now.year + 2) then
          fault = "after #{future}"
        end
        if fault then
          raise ArgumentError, "copyright with a year #{fault} is very strange; did you accidentally add or subtract two years?"
        end

        input

      when String then
        input.strip.split(/,/).map do |part|
          part = part.strip
          if part =~ /^\d+$/ then
            part.to_i
          elsif found = part.split(/-/) then
            unless found.length == 2 and found.all? {|x| x.strip =~ /^\d+$/ }
              raise ArgumentError, "#{part.inspect} is not a good copyright year or range"
            end
            Range.new(found[0].to_i, found[1].to_i)
          else
            raise ArgumentError, "#{part.inspect} is not a good copyright year or range"
          end
        end

      when Array then
        result = []
        input.each do |item|
          item = munge_copyright_year item
          if item.is_a? Array
            result.concat item
          else
            result << item
          end
        end
        result

      else
        raise ArgumentError, "#{input.inspect} is not a good copyright year, set, or range"
      end
    end
  end
end