[Pkg-puppet-devel] [SCM] Puppet packaging for Debian branch, experimental, updated. debian/2.6.8-1-844-g7ec39d5

Daniel Pittman daniel at puppetlabs.com
Tue May 10 08:18:25 UTC 2011


The following commit has been merged in the experimental branch:
commit 092ab09d00474d69361ee757efde2b28c89b39eb
Author: Daniel Pittman <daniel at puppetlabs.com>
Date:   Tue Apr 26 17:18:09 2011 -0700

    (#6962) Extend documentation API for Faces.
    
    This adds the remaining documentation mechanisms to the Face instances,
    allowing them to build and report correct documentation, licensing and
    ownership for the help face to build on.

diff --git a/lib/puppet/face/help/action.erb b/lib/puppet/face/help/action.erb
index eaf1314..7a9b871 100644
--- a/lib/puppet/face/help/action.erb
+++ b/lib/puppet/face/help/action.erb
@@ -1,3 +1,47 @@
-Use: puppet <%= face.name %> [options] <%= action.name %> [options]
+puppet <%= face.name %><%= action.default? ? '' : " #{action.name}" %>(1) -- <%= action.summary || face.summary %>
+<%= '=' * (_erbout.length - 1) %>
 
-Summary: <%= action.summary %>
+% if action.synopsis
+SYNOPSIS
+--------
+
+<%= action.synopsis %>
+
+% end
+% if action.description
+DESCRIPTION
+-----------
+<%= action.description %>
+
+%end
+% unless action.options.empty?
+OPTIONS
+-------
+%   action.options.sort.each do |name|
+%     option = action.get_option name
+<%= "  " + option.optparse.join(" |" ) %>
+<%= option.desc and option.desc.gsub(/^/, '    ') %>
+
+%   end
+% end
+% if action.examples
+EXAMPLES
+--------
+<%= action.examples %>
+% end
+% if action.notes
+NOTES
+-----
+<%= action.notes %>
+
+% end
+% unless action.authors.empty?
+AUTHOR
+------
+<%= action.authors.map {|x| " * " + x } .join("\n") %>
+
+%end
+COPYRIGHT AND LICENSE
+---------------------
+<%= action.copyright %>
+<%= action.license   %>
diff --git a/lib/puppet/face/help/face.erb b/lib/puppet/face/help/face.erb
index efe5fd8..944f7a9 100644
--- a/lib/puppet/face/help/face.erb
+++ b/lib/puppet/face/help/face.erb
@@ -1,7 +1,47 @@
-Use: puppet <%= face.name %> [options] <action> [options]
+NAME
+  <%= face.name %> -- <%= face.summary || "unknown face..." %>
 
-Available actions:
+% if face.synopsis
+SYNOPSIS
+<%= face.synopsis.gsub(/^/, '  ') %>
+
+% end
+% if face.description
+DESCRIPTION
+<%= face.description.chomp.gsub(/^/, '  ') %>
+
+%end
+% unless face.options.empty?
+OPTIONS
+%   face.options.sort.each do |name|
+%     option = face.get_option name
+<%= "  " + option.optparse.join(" |" ) %>
+<%= option.desc and option.desc.gsub(/^/, '    ') %>
+
+%   end
+% end
+ACTIONS
+% padding = face.actions.map{|x| x.to_s.length}.max + 2
 % face.actions.each do |actionname|
 %   action = face.get_action(actionname)
-  <%= action.name.to_s.ljust(16) %>  <%= action.summary %>
+  <%= action.name.to_s.ljust(padding) %>  <%= action.summary %>
 % end
+
+% if face.examples
+EXAMPLES
+<%= face.examples %>
+% end
+% if face.notes
+NOTES
+<%= face.notes %>
+
+% end
+% unless face.authors.empty?
+AUTHOR
+<%= face.authors.join("\n").gsub(/^/, ' * ') %>
+
+%end
+COPYRIGHT AND LICENSE
+<%= face.copyright.gsub(/^/, '  ') %>
+<%= face.license.gsub(/^/, '  ')   %>
+
diff --git a/lib/puppet/interface.rb b/lib/puppet/interface.rb
index ced0086..adf6c99 100644
--- a/lib/puppet/interface.rb
+++ b/lib/puppet/interface.rb
@@ -1,5 +1,6 @@
 require 'puppet'
 require 'puppet/util/autoload'
+require 'prettyprint'
 
 class Puppet::Interface
   require 'puppet/interface/face_collection'
@@ -70,7 +71,7 @@ class Puppet::Interface
   # the same instance between build-time and the runtime instance.  When that
   # splits out this should merge into a module that both the action and face
   # include. --daniel 2011-04-17
-  attr_accessor :summary, :description
+  attr_accessor :summary
   def summary(value = nil)
     self.summary = value unless value.nil?
     @summary
@@ -83,11 +84,178 @@ class Puppet::Interface
     @summary = value
   end
 
+  attr_accessor :description
   def description(value = nil)
     self.description = value unless value.nil?
     @description
   end
 
+  attr_accessor :examples
+  def examples(value = nil)
+    self.examples = value unless value.nil?
+    @examples
+  end
+
+  attr_accessor :short_description
+  def short_description(value = nil)
+    self.short_description = value unless value.nil?
+    if @short_description.nil? then
+      fail "REVISIT: Extract this..."
+    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(value)
+    end
+    @authors.empty? ? nil : @authors.join("\n")
+  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)
+  end
+  def authors
+    @authors
+  end
+  def authors=(value)
+    if Array(value).any? {|x| x =~ /\n/ } then
+      raise ArgumentError, 'author should be a single line; use multiple statements'
+    end
+    @authors = Array(value)
+  end
+
+  attr_accessor :notes
+  def notes(value = nil)
+    @notes = value unless value.nil?
+    @notes
+  end
+
+  attr_accessor :license
+  def license(value = nil)
+    @license = value unless value.nil?
+    @license
+  end
+
+  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
+
+  def synopsis
+    output = PrettyPrint.format do |s|
+      s.text("puppet #{name} <action>")
+      s.breakable
+
+      options.each do |option|
+        option = get_option(option)
+        wrap = option.required? ? %w{ < > } : %w{ [ ] }
+
+        s.group(0, *wrap) do
+          option.optparse.each do |item|
+            unless s.current_group.first?
+              s.breakable
+              s.text '|'
+              s.breakable
+            end
+            s.text item
+          end
+        end
+      end
+    end
+  end
+
 
   ########################################################################
   attr_reader :name, :version
@@ -97,9 +265,15 @@ class Puppet::Interface
       raise ArgumentError, "Cannot create face #{name.inspect} with invalid version number '#{version}'!"
     end
 
-    @name = Puppet::Interface::FaceCollection.underscorize(name)
+    @name    = Puppet::Interface::FaceCollection.underscorize(name)
     @version = version
 
+    # The few bits of documentation we actually demand.  The default license
+    # is a favour to our end users; if you happen to get that in a core face
+    # report it as a bug, please. --daniel 2011-04-26
+    @authors  = []
+    @license  = 'All Rights Reserved'
+
     instance_eval(&block) if block_given?
   end
 
diff --git a/spec/shared_behaviours/documentation_on_faces.rb b/spec/shared_behaviours/documentation_on_faces.rb
index 41b4015..ef26456 100644
--- a/spec/shared_behaviours/documentation_on_faces.rb
+++ b/spec/shared_behaviours/documentation_on_faces.rb
@@ -1,34 +1,177 @@
 # encoding: UTF-8
 shared_examples_for "documentation on faces" do
-  context "description" do
-    describe "#summary" do
-      it "should accept a summary" do
-        text = "this is my summary"
-        expect { subject.summary = text }.to_not raise_error
-        subject.summary.should == text
+  defined?(Attrs) or
+    Attrs = [:summary, :description, :examples, :short_description, :notes, :author]
+
+  defined?(SingleLineAttrs) or
+    SingleLineAttrs = [:summary, :author]
+
+  # Simple, procedural tests that apply to a bunch of methods.
+  Attrs.each do |attr|
+    it "should accept a #{attr}" do
+      expect { subject.send("#{attr}=", "hello") }.not_to raise_error
+      subject.send(attr).should == "hello"
+    end
+
+    it "should accept a long (single line) value for #{attr}" do
+      text = "I never know when to stop with the word banana" + ("na" * 1000)
+      expect { subject.send("#{attr}=", text) }.to_not raise_error
+      subject.send(attr).should == text
+    end
+  end
+
+  # Should they accept multiple lines?
+  Attrs.each do |attr|
+    text = "with\nnewlines"
+
+    if SingleLineAttrs.include? attr then
+      it "should not accept multiline values for #{attr}" do
+        expect { subject.send("#{attr}=", text) }.
+          to raise_error ArgumentError, /#{attr} should be a single line/
+        subject.send(attr).should be_nil
+      end
+    else
+      it "should accept multiline values for #{attr}" do
+        expect { subject.send("#{attr}=", text) }.not_to raise_error
+        subject.send(attr).should == text
+      end
+    end
+  end
+
+  describe "multiple authors" do
+    authors = %w{John Paul George Ringo}
+
+    context "in the DSL" do
+      it "should support multiple authors" do
+
+        authors.each {|name| subject.author name }
+        subject.authors.should =~ authors
+
+        subject.author.should == authors.join("\n")
+      end
+
+      it "should reject author as an array" do
+        expect { subject.author ["Foo", "Bar"] }.
+          to raise_error ArgumentError, /author must be a string/
+      end
+    end
+
+    context "#author=" do
+      it "should accept a single name" do
+        subject.author = "Fred"
+        subject.author.should == "Fred"
+      end
+
+      it "should accept an array of names" do
+        subject.author = authors
+        subject.authors.should =~ authors
+        subject.author.should == authors.join("\n")
+      end
+
+      it "should not append when set multiple times" do
+        subject.author = "Fred"
+        subject.author = "John"
+        subject.author.should == "John"
+      end
+
+      it "should reject arrays with embedded newlines" do
+        expect { subject.author = ["Fred\nJohn"] }.
+          to raise_error ArgumentError, /author should be a single line/
       end
+    end
+  end
+
+  describe "#license" do
+    it "should default to reserving rights" do
+      subject.license.should =~ /All Rights Reserved/
+    end
+
+    it "should accept an arbitrary license string in the DSL" do
+      subject.license("foo")
+      subject.license.should == "foo"
+    end
+
+    it "should accept an arbitrary license string on the object" do
+      subject.license = "foo"
+      subject.license.should == "foo"
+    end
+
+    it "should accept symbols to specify existing licenses..."
+  end
 
-      it "should accept a long, long, long summary" do
-        text = "I never know when to stop with the word banana" + ("na" * 1000)
-        expect { subject.summary = text }.to_not raise_error
-        subject.summary.should == text
+  describe "#copyright" do
+    it "should fail with just a name" do
+      expect { subject.copyright("invalid") }.
+        to raise_error ArgumentError, /copyright takes the owners names, then the years covered/
+    end
+
+    [1997, "1997"].each do |year|
+      it "should accept an entity name and a #{year.class.name} year" do
+        subject.copyright("me", year)
+        subject.copyright.should =~ /\bme\b/
+        subject.copyright.should =~ /#{year}/
       end
 
-      it "should reject a summary with a newline" do
-        expect { subject.summary = "with\nnewlines" }.
-          to raise_error ArgumentError, /summary should be a single line/
+      it "should accept multiple entity names and a #{year.class.name} year" do
+        subject.copyright ["me", "you"], year
+        subject.copyright.should =~ /\bme\b/
+        subject.copyright.should =~ /\byou\b/
+        subject.copyright.should =~ /#{year}/
       end
     end
 
-    describe "#description" do
-      it "should accept a description" do
-        subject.description = "hello"
-        subject.description.should == "hello"
+    ["1997-2003", "1997 - 2003", 1997..2003].each do |range|
+      it "should accept a #{range.class.name} range of years" do
+        subject.copyright("me", range)
+        subject.copyright.should =~ /\bme\b/
+        subject.copyright.should =~ /1997-2003/
+      end
+
+      it "should accept a #{range.class.name} range of years" do
+        subject.copyright ["me", "you"], range
+        subject.copyright.should =~ /\bme\b/
+        subject.copyright.should =~ /\byou\b/
+        subject.copyright.should =~ /1997-2003/
+      end
+    end
+
+    [[1997, 2003], ["1997", 2003], ["1997", "2003"]].each do |input|
+      it "should accept the set of years #{input.inspect} in an array" do
+        subject.copyright "me", input
+        subject.copyright.should =~ /\bme\b/
+        subject.copyright.should =~ /1997, 2003/
+      end
+
+      it "should accept the set of years #{input.inspect} in an array" do
+        subject.copyright ["me", "you"], input
+        subject.copyright.should =~ /\bme\b/
+        subject.copyright.should =~ /\byou\b/
+        subject.copyright.should =~ /1997, 2003/
+      end
+    end
+
+    it "should warn if someone does math accidentally on the range of years" do
+      expect { subject.copyright "me", 1997-2003 }.
+        to raise_error ArgumentError, /copyright with a year before 1970 is very strange; did you accidentally add or subtract two years\?/
+    end
+
+    it "should accept complex copyright years" do
+      years = [1997, 1999, 2000..2002, 2005].reverse
+      subject.copyright "me", years
+      subject.copyright.should =~ /\bme\b/
+      subject.copyright.should =~ /1997, 1999, 2000-2002, 2005/
+    end
+  end
+
+  # Things that are automatically generated.
+  [:name, :options, :synopsis].each do |attr|
+    describe "##{attr}" do
+      it "should not allow you to set #{attr}" do
+        subject.should_not respond_to :"#{attr}="
       end
 
-      it "should accept a description with a newline" do
-        subject.description = "hello \n my \n fine \n friend"
-        subject.description.should == "hello \n my \n fine \n friend"
+      it "should have a #{attr}" do
+        subject.send(attr).should_not be_nil
       end
     end
   end
diff --git a/spec/unit/interface_spec.rb b/spec/unit/interface_spec.rb
index a1d70cf..27da397 100755
--- a/spec/unit/interface_spec.rb
+++ b/spec/unit/interface_spec.rb
@@ -76,7 +76,11 @@ describe Puppet::Interface do
 
     # Required documentation methods...
     { :summary     => "summary",
-      :description => "This is the description of the stuff\n\nWhee"
+      :description => "This is the description of the stuff\n\nWhee",
+      :examples    => "This is my example",
+      :short_description => "This is my custom short description",
+      :notes       => "These are my notes...",
+      :author      => "This is my authorship data",
     }.each do |attr, value|
       it "should support #{attr} in the builder" do
         face = subject.new(:builder, '1.0.0') do

-- 
Puppet packaging for Debian



More information about the Pkg-puppet-devel mailing list