From 092ab09d00474d69361ee757efde2b28c89b39eb Mon Sep 17 00:00:00 2001 From: Daniel Pittman Date: Tue, 26 Apr 2011 17:18:09 -0700 Subject: (#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. --- lib/puppet/face/help/action.erb | 48 +++++- lib/puppet/face/help/face.erb | 46 +++++- lib/puppet/interface.rb | 178 +++++++++++++++++++++- spec/shared_behaviours/documentation_on_faces.rb | 183 ++++++++++++++++++++--- spec/unit/interface_spec.rb | 6 +- 5 files changed, 433 insertions(+), 28 deletions(-) diff --git a/lib/puppet/face/help/action.erb b/lib/puppet/face/help/action.erb index eaf131464..7a9b87117 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 efe5fd809..944f7a96b 100644 --- a/lib/puppet/face/help/face.erb +++ b/lib/puppet/face/help/face.erb @@ -1,7 +1,47 @@ -Use: puppet <%= face.name %> [options] [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 ced00863d..adf6c991c 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} ") + 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 41b4015c9..ef2645620 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 a1d70cf64..27da39766 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 -- cgit From 59e7ef15507de48f6504ef21a8e0e775104961c6 Mon Sep 17 00:00:00 2001 From: Daniel Pittman Date: Tue, 26 Apr 2011 23:30:08 -0700 Subject: (#6962) Move documentation support into a module. Given that we have identical documentation behaviour in the face and action code, it should properly be written once. So, move it into a module, extend the other classes with it, and have done. --- lib/puppet/interface.rb | 166 +--------------------- lib/puppet/interface/action.rb | 52 ++++--- lib/puppet/interface/action_manager.rb | 4 +- lib/puppet/interface/documentation.rb | 168 +++++++++++++++++++++++ spec/shared_behaviours/documentation_on_faces.rb | 49 ++++++- 5 files changed, 255 insertions(+), 184 deletions(-) create mode 100644 lib/puppet/interface/documentation.rb diff --git a/lib/puppet/interface.rb b/lib/puppet/interface.rb index adf6c991c..23d760b30 100644 --- a/lib/puppet/interface.rb +++ b/lib/puppet/interface.rb @@ -1,8 +1,11 @@ require 'puppet' require 'puppet/util/autoload' +require 'puppet/interface/documentation' require 'prettyprint' class Puppet::Interface + include DocSupport + require 'puppet/interface/face_collection' require 'puppet/interface/action_manager' @@ -66,173 +69,12 @@ class Puppet::Interface Puppet.warning("set_default_format is deprecated (and ineffective); use render_as on your actions instead.") end + ######################################################################## # Documentation. We currently have to rewrite both getters because we share # 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 - def summary(value = nil) - self.summary = value unless value.nil? - @summary - end - def summary=(value) - value = value.to_s - value =~ /\n/ and - raise ArgumentError, "Face summary should be a single line; put the long text in 'description' instead." - - @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} ") diff --git a/lib/puppet/interface/action.rb b/lib/puppet/interface/action.rb index 08bc0a345..18c7ab057 100644 --- a/lib/puppet/interface/action.rb +++ b/lib/puppet/interface/action.rb @@ -1,12 +1,21 @@ -# -*- coding: utf-8 -*- require 'puppet/interface' -require 'puppet/interface/option' +require 'puppet/interface/documentation' +require 'prettyprint' class Puppet::Interface::Action + include Puppet::Interface::DocSupport + def initialize(face, name, attrs = {}) raise "#{name.inspect} is an invalid action name" unless name.to_s =~ /^[a-z]\w*$/ @face = face @name = name.to_sym + + # 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' + attrs.each do |k, v| send("#{k}=", v) end @options = {} @@ -30,8 +39,31 @@ class Puppet::Interface::Action !!@default end - attr_accessor :summary - + ######################################################################## + # Documentation... + def synopsis + output = PrettyPrint.format do |s| + s.text("puppet #{@face.name}") + s.text(" #{name}") unless default? + 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 ######################################################################## # Support for rendering formats and all. @@ -82,18 +114,6 @@ class Puppet::Interface::Action end - ######################################################################## - # Documentation stuff, whee! - attr_accessor :summary, :description - def summary=(value) - value = value.to_s - value =~ /\n/ and - raise ArgumentError, "Face summary should be a single line; put the long text in 'description' instead." - - @summary = value - end - - ######################################################################## # Initially, this was defined to allow the @action.invoke pattern, which is # a very natural way to invoke behaviour given our introspection diff --git a/lib/puppet/interface/action_manager.rb b/lib/puppet/interface/action_manager.rb index 440be2d1b..c5eb8e08a 100644 --- a/lib/puppet/interface/action_manager.rb +++ b/lib/puppet/interface/action_manager.rb @@ -1,9 +1,11 @@ -require 'puppet/interface/action_builder' +require 'puppet/interface/action' module Puppet::Interface::ActionManager # Declare that this app can take a specific action, and provide # the code to do so. def action(name, &block) + require 'puppet/interface/action_builder' + @actions ||= {} @default_action ||= nil raise "Action #{name} already defined for #{self}" if action?(name) diff --git a/lib/puppet/interface/documentation.rb b/lib/puppet/interface/documentation.rb new file mode 100644 index 000000000..f3bf33da5 --- /dev/null +++ b/lib/puppet/interface/documentation.rb @@ -0,0 +1,168 @@ +class Puppet::Interface + module DocSupport + attr_accessor :summary + def summary(value = nil) + self.summary = value unless value.nil? + @summary + end + def summary=(value) + value = value.to_s + value =~ /\n/ and + raise ArgumentError, "Face summary should be a single line; put the long text in 'description' instead." + + @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 + 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(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 + end +end diff --git a/spec/shared_behaviours/documentation_on_faces.rb b/spec/shared_behaviours/documentation_on_faces.rb index ef2645620..dd2bd3110 100644 --- a/spec/shared_behaviours/documentation_on_faces.rb +++ b/spec/shared_behaviours/documentation_on_faces.rb @@ -38,6 +38,46 @@ shared_examples_for "documentation on faces" do end end + describe "#short_description" do + it "should return the set value if set after description" do + subject.description = "hello\ngoodbye" + subject.short_description = "whatever" + subject.short_description.should == "whatever" + end + + it "should return the set value if set before description" do + subject.short_description = "whatever" + subject.description = "hello\ngoodbye" + subject.short_description.should == "whatever" + end + + it "should return nothing if not set and no description" do + subject.short_description.should be_nil + end + + it "should return the first paragraph of description if not set (where it is one line long)" do + subject.description = "hello" + subject.short_description.should == subject.description + end + + it "should return the first paragraph of description if not set (where there is no paragraph break)" do + subject.description = "hello\ngoodbye" + subject.short_description.should == subject.description + end + + it "should return the first paragraph of description if not set (where there is a paragraph break)" do + subject.description = "hello\ngoodbye\n\nmore\ntext\nhere\n\nfinal\nparagraph" + subject.short_description.should == "hello\ngoodbye" + end + + it "should trim a very, very long first paragraph" do + line = "this is a very, very, very long long line full of text\n" + subject.description = line * 20 + "\n\nwhatever, dude." + + subject.short_description.should == (line * 5).chomp + end + end + describe "multiple authors" do authors = %w{John Paul George Ringo} @@ -86,11 +126,6 @@ shared_examples_for "documentation on faces" 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" @@ -173,6 +208,10 @@ shared_examples_for "documentation on faces" do it "should have a #{attr}" do subject.send(attr).should_not be_nil end + + it "'s #{attr} should not be empty..." do + subject.send(attr).should_not == '' + end end end end -- cgit From b8525c9f032cfa9ac621509ba8e332803a99a120 Mon Sep 17 00:00:00 2001 From: Daniel Pittman Date: Tue, 26 Apr 2011 17:26:39 -0700 Subject: (#6962) Fill out documentation on Faces and Actions This uses the documentation we had written, wiring it into the existing faces and actions. This helps fill out the need to document these things before they ship. --- lib/puppet/face/catalog.rb | 18 ++++++++++++------ lib/puppet/face/facts.rb | 3 ++- lib/puppet/face/key.rb | 7 +++++-- lib/puppet/face/node.rb | 5 +++-- lib/puppet/face/secret_agent.rb | 2 ++ 5 files changed, 24 insertions(+), 11 deletions(-) diff --git a/lib/puppet/face/catalog.rb b/lib/puppet/face/catalog.rb index 59356d43f..da03af0fd 100644 --- a/lib/puppet/face/catalog.rb +++ b/lib/puppet/face/catalog.rb @@ -3,14 +3,18 @@ require 'puppet/face/indirector' Puppet::Face::Indirector.define(:catalog, '0.0.1') do summary "Compile, save, view, and convert catalogs." - @longdocs = "This face primarily interacts with the compiling subsystem. - By default, it compiles a catalog using the default manifest and the - hostname from 'certname', but you can choose to retrieve a catalog from - the server by specifying '--from rest'. You can also choose to print any - catalog in 'dot' format (for easy graph viewing with OmniGraffle or Graphviz) - with '--format dot'." + description <<-EOT +This face primarily interacts with the compiling subsystem. +By default, it compiles a catalog using the default manifest and the +hostname from 'certname', but you can choose to retrieve a catalog from +the server by specifying '--from rest'. You can also choose to print any +catalog in 'dot' format (for easy graph viewing with OmniGraffle or Graphviz) +with '--format dot'. + EOT action(:apply) do + summary "apply a Puppet::Resource::Catalog object" + when_invoked do |catalog, options| report = Puppet::Transaction::Report.new("apply") report.configuration_version = catalog.version @@ -32,6 +36,8 @@ Puppet::Face::Indirector.define(:catalog, '0.0.1') do end action(:download) do + summary "download the catalog given the certname and facts" + when_invoked do |certname, facts, options| Puppet::Resource::Catalog.indirection.terminus_class = :rest facts_to_upload = {:facts_format => :b64_zlib_yaml, :facts => CGI.escape(facts.render(:b64_zlib_yaml))} diff --git a/lib/puppet/face/facts.rb b/lib/puppet/face/facts.rb index a5a279ddf..abcb3369a 100644 --- a/lib/puppet/face/facts.rb +++ b/lib/puppet/face/facts.rb @@ -4,8 +4,9 @@ require 'puppet/node/facts' Puppet::Face::Indirector.define(:facts, '0.0.1') do summary "Retrieve, store, and view facts." - # Upload our facts to the server action(:upload) do + summary "upload our facts to the server." + render_as :yaml when_invoked do |options| diff --git a/lib/puppet/face/key.rb b/lib/puppet/face/key.rb index c85345167..0988ebea2 100644 --- a/lib/puppet/face/key.rb +++ b/lib/puppet/face/key.rb @@ -3,6 +3,9 @@ require 'puppet/face/indirector' Puppet::Face::Indirector.define(:key, '0.0.1') do summary "Create, save, and remove certificate keys." - @longdocs = "Keys are created for you automatically when certificate - requests are generated with 'puppet certificate generate'." + description <<-EOT +Keys are created for you automatically when certificate +requests are generated with 'puppet certificate generate'. + EOT + end diff --git a/lib/puppet/face/node.rb b/lib/puppet/face/node.rb index 7032debc3..7cb6ae469 100644 --- a/lib/puppet/face/node.rb +++ b/lib/puppet/face/node.rb @@ -2,6 +2,7 @@ require 'puppet/face/indirector' Puppet::Face::Indirector.define(:node, '0.0.1') do summary "View and manage nodes" - @longdocs = "It defaults to using whatever your node - terminus is set as." + description <<-EOT +It defaults to using whatever your node terminus is set as. + EOT end diff --git a/lib/puppet/face/secret_agent.rb b/lib/puppet/face/secret_agent.rb index af7ffb7b7..90841b9e0 100644 --- a/lib/puppet/face/secret_agent.rb +++ b/lib/puppet/face/secret_agent.rb @@ -4,6 +4,8 @@ Puppet::Face.define(:secret_agent, '0.0.1') do summary "Provides agent-like behavior, with no plugin downloading or reporting." action(:synchronize) do + summary "run the secret agent, which makes the catalog and system match..." + when_invoked do |certname, options| Puppet::Face[:plugin, '0.0.1'].download -- cgit From 6e152ad1d0bb34f6d24fd818b2b020d5a8dd385b Mon Sep 17 00:00:00 2001 From: Daniel Pittman Date: Tue, 26 Apr 2011 17:30:41 -0700 Subject: (#6962) Give copyright and license for all faces. Now we have the capability, mark all our faces copyright the company and under the Apache 2 license, which indeed they are. --- lib/puppet/face/catalog.rb | 3 +++ lib/puppet/face/certificate.rb | 3 +++ lib/puppet/face/certificate_request.rb | 3 +++ lib/puppet/face/certificate_revocation_list.rb | 3 +++ lib/puppet/face/config.rb | 3 +++ lib/puppet/face/facts.rb | 3 +++ lib/puppet/face/file.rb | 3 +++ lib/puppet/face/help.rb | 3 +++ lib/puppet/face/key.rb | 3 +++ lib/puppet/face/node.rb | 3 +++ lib/puppet/face/parser.rb | 3 +++ lib/puppet/face/plugin.rb | 3 +++ lib/puppet/face/report.rb | 3 +++ lib/puppet/face/resource.rb | 3 +++ lib/puppet/face/resource_type.rb | 3 +++ lib/puppet/face/secret_agent.rb | 3 +++ lib/puppet/face/status.rb | 3 +++ 17 files changed, 51 insertions(+) diff --git a/lib/puppet/face/catalog.rb b/lib/puppet/face/catalog.rb index da03af0fd..98f550413 100644 --- a/lib/puppet/face/catalog.rb +++ b/lib/puppet/face/catalog.rb @@ -1,6 +1,9 @@ require 'puppet/face/indirector' Puppet::Face::Indirector.define(:catalog, '0.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" + summary "Compile, save, view, and convert catalogs." description <<-EOT diff --git a/lib/puppet/face/certificate.rb b/lib/puppet/face/certificate.rb index 9ee83d0a2..7f2998da2 100644 --- a/lib/puppet/face/certificate.rb +++ b/lib/puppet/face/certificate.rb @@ -2,6 +2,9 @@ require 'puppet/face/indirector' require 'puppet/ssl/host' Puppet::Face::Indirector.define(:certificate, '0.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" + summary "provide access to the CA for certificate management" option "--ca-location LOCATION" do diff --git a/lib/puppet/face/certificate_request.rb b/lib/puppet/face/certificate_request.rb index 4e711b25b..0f7f72205 100644 --- a/lib/puppet/face/certificate_request.rb +++ b/lib/puppet/face/certificate_request.rb @@ -1,5 +1,8 @@ require 'puppet/face/indirector' Puppet::Face::Indirector.define(:certificate_request, '0.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" + summary "Manage certificate requests." end diff --git a/lib/puppet/face/certificate_revocation_list.rb b/lib/puppet/face/certificate_revocation_list.rb index f111586af..9a8fe303d 100644 --- a/lib/puppet/face/certificate_revocation_list.rb +++ b/lib/puppet/face/certificate_revocation_list.rb @@ -1,5 +1,8 @@ require 'puppet/face/indirector' Puppet::Face::Indirector.define(:certificate_revocation_list, '0.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" + summary "Manage the list of revoked certificates." end diff --git a/lib/puppet/face/config.rb b/lib/puppet/face/config.rb index d1f6d5a9e..fc685202c 100644 --- a/lib/puppet/face/config.rb +++ b/lib/puppet/face/config.rb @@ -1,6 +1,9 @@ require 'puppet/face' Puppet::Face.define(:config, '0.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" + summary "Interact with Puppet configuration options." action(:print) do diff --git a/lib/puppet/face/facts.rb b/lib/puppet/face/facts.rb index abcb3369a..88e3c7ba9 100644 --- a/lib/puppet/face/facts.rb +++ b/lib/puppet/face/facts.rb @@ -2,6 +2,9 @@ require 'puppet/face/indirector' require 'puppet/node/facts' Puppet::Face::Indirector.define(:facts, '0.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" + summary "Retrieve, store, and view facts." action(:upload) do diff --git a/lib/puppet/face/file.rb b/lib/puppet/face/file.rb index 547df3e4f..1b2e62b6d 100644 --- a/lib/puppet/face/file.rb +++ b/lib/puppet/face/file.rb @@ -1,6 +1,9 @@ require 'puppet/face/indirector' Puppet::Face::Indirector.define(:file, '0.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" + summary "Retrieve and store files in a filebucket" set_indirection_name :file_bucket_file diff --git a/lib/puppet/face/help.rb b/lib/puppet/face/help.rb index a762fb02e..ba64d2b54 100644 --- a/lib/puppet/face/help.rb +++ b/lib/puppet/face/help.rb @@ -4,6 +4,9 @@ require 'pathname' require 'erb' Puppet::Face.define(:help, '0.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" + summary "Displays help about puppet subcommands" action(:help) do diff --git a/lib/puppet/face/key.rb b/lib/puppet/face/key.rb index 0988ebea2..5d1a9ab26 100644 --- a/lib/puppet/face/key.rb +++ b/lib/puppet/face/key.rb @@ -1,6 +1,9 @@ require 'puppet/face/indirector' Puppet::Face::Indirector.define(:key, '0.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" + summary "Create, save, and remove certificate keys." description <<-EOT diff --git a/lib/puppet/face/node.rb b/lib/puppet/face/node.rb index 7cb6ae469..3591dd92b 100644 --- a/lib/puppet/face/node.rb +++ b/lib/puppet/face/node.rb @@ -1,5 +1,8 @@ require 'puppet/face/indirector' Puppet::Face::Indirector.define(:node, '0.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" + summary "View and manage nodes" description <<-EOT diff --git a/lib/puppet/face/parser.rb b/lib/puppet/face/parser.rb index bea146f81..c4c3fb55e 100644 --- a/lib/puppet/face/parser.rb +++ b/lib/puppet/face/parser.rb @@ -2,6 +2,9 @@ require 'puppet/face' require 'puppet/parser' Puppet::Face.define(:parser, '0.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" + summary "Interact directly with the parser" action :validate do diff --git a/lib/puppet/face/plugin.rb b/lib/puppet/face/plugin.rb index 4d95bd93b..19060942a 100644 --- a/lib/puppet/face/plugin.rb +++ b/lib/puppet/face/plugin.rb @@ -1,5 +1,8 @@ require 'puppet/face' Puppet::Face.define(:plugin, '0.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" + summary "Interact with the Puppet plugin system" action :download do diff --git a/lib/puppet/face/report.rb b/lib/puppet/face/report.rb index 4de25ef92..9855f3d2d 100644 --- a/lib/puppet/face/report.rb +++ b/lib/puppet/face/report.rb @@ -1,6 +1,9 @@ require 'puppet/face/indirector' Puppet::Face::Indirector.define(:report, '0.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" + summary "Create, display, and submit reports" action(:submit) do diff --git a/lib/puppet/face/resource.rb b/lib/puppet/face/resource.rb index 908b2e462..55a14f280 100644 --- a/lib/puppet/face/resource.rb +++ b/lib/puppet/face/resource.rb @@ -1,5 +1,8 @@ require 'puppet/face/indirector' Puppet::Face::Indirector.define(:resource, '0.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" + summary "Interact directly with resources via the RAL, like ralsh" end diff --git a/lib/puppet/face/resource_type.rb b/lib/puppet/face/resource_type.rb index fe86eb873..8776dc105 100644 --- a/lib/puppet/face/resource_type.rb +++ b/lib/puppet/face/resource_type.rb @@ -1,5 +1,8 @@ require 'puppet/face/indirector' Puppet::Face::Indirector.define(:resource_type, '0.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" + summary "View resource types, classes, and nodes from all manifests" end diff --git a/lib/puppet/face/secret_agent.rb b/lib/puppet/face/secret_agent.rb index 90841b9e0..50018cb13 100644 --- a/lib/puppet/face/secret_agent.rb +++ b/lib/puppet/face/secret_agent.rb @@ -1,6 +1,9 @@ require 'puppet/face' Puppet::Face.define(:secret_agent, '0.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" + summary "Provides agent-like behavior, with no plugin downloading or reporting." action(:synchronize) do diff --git a/lib/puppet/face/status.rb b/lib/puppet/face/status.rb index 2a0956ee3..d35d7e1b3 100644 --- a/lib/puppet/face/status.rb +++ b/lib/puppet/face/status.rb @@ -1,5 +1,8 @@ require 'puppet/face/indirector' Puppet::Face::Indirector.define(:status, '0.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" + summary "View status information" end -- cgit From e8eb290a1681baa19ef0b035af7cf17daadc6069 Mon Sep 17 00:00:00 2001 From: Daniel Pittman Date: Wed, 27 Apr 2011 10:05:48 -0700 Subject: (#6962) Finish documentation API on Face options. This extends the last of the documentation support, down into options, so they can be described as expected. In the process we split out the modular docs API into a full and short version options only want short docs, but the behaviours are identical to the full version. --- lib/puppet/face/help.rb | 2 +- lib/puppet/face/help/action.erb | 3 ++- lib/puppet/face/help/face.erb | 3 ++- lib/puppet/face/indirector.rb | 6 ++++-- lib/puppet/interface.rb | 2 +- lib/puppet/interface/action.rb | 2 +- lib/puppet/interface/documentation.rb | 6 +++++- lib/puppet/interface/option.rb | 6 +++++- spec/shared_behaviours/things_that_declare_options.rb | 2 ++ spec/unit/interface/option_builder_spec.rb | 14 ++++++++------ 10 files changed, 31 insertions(+), 15 deletions(-) diff --git a/lib/puppet/face/help.rb b/lib/puppet/face/help.rb index ba64d2b54..07c3ed9b1 100644 --- a/lib/puppet/face/help.rb +++ b/lib/puppet/face/help.rb @@ -13,7 +13,7 @@ Puppet::Face.define(:help, '0.0.1') do summary "Display help about faces and their actions." option "--version VERSION" do - desc "Which version of the interface to show help for" + summary "which version of the interface to show help for" end default diff --git a/lib/puppet/face/help/action.erb b/lib/puppet/face/help/action.erb index 7a9b87117..c26958a35 100644 --- a/lib/puppet/face/help/action.erb +++ b/lib/puppet/face/help/action.erb @@ -20,7 +20,8 @@ OPTIONS % action.options.sort.each do |name| % option = action.get_option name <%= " " + option.optparse.join(" |" ) %> -<%= option.desc and option.desc.gsub(/^/, ' ') %> +<%= option.summary %> +<%= option.description %> % end % end diff --git a/lib/puppet/face/help/face.erb b/lib/puppet/face/help/face.erb index 944f7a96b..b249981de 100644 --- a/lib/puppet/face/help/face.erb +++ b/lib/puppet/face/help/face.erb @@ -16,7 +16,8 @@ OPTIONS % face.options.sort.each do |name| % option = face.get_option name <%= " " + option.optparse.join(" |" ) %> -<%= option.desc and option.desc.gsub(/^/, ' ') %> +<%= option.summary %> +<%= option.description %> % end % end diff --git a/lib/puppet/face/indirector.rb b/lib/puppet/face/indirector.rb index 6c7708b51..a7ff7e1f0 100644 --- a/lib/puppet/face/indirector.rb +++ b/lib/puppet/face/indirector.rb @@ -3,8 +3,10 @@ require 'puppet/face' class Puppet::Face::Indirector < Puppet::Face option "--terminus TERMINUS" do - desc "REVISIT: You can select a terminus, which has some bigger effect -that we should describe in this file somehow." + description %q{ +REVISIT: You can select a terminus, which has some bigger effect +that we should describe in this file somehow. +}.strip before_action do |action, args, options| set_terminus(options[:terminus]) diff --git a/lib/puppet/interface.rb b/lib/puppet/interface.rb index 23d760b30..4a735069f 100644 --- a/lib/puppet/interface.rb +++ b/lib/puppet/interface.rb @@ -4,7 +4,7 @@ require 'puppet/interface/documentation' require 'prettyprint' class Puppet::Interface - include DocSupport + include FullDocs require 'puppet/interface/face_collection' diff --git a/lib/puppet/interface/action.rb b/lib/puppet/interface/action.rb index 18c7ab057..177df81f2 100644 --- a/lib/puppet/interface/action.rb +++ b/lib/puppet/interface/action.rb @@ -3,7 +3,7 @@ require 'puppet/interface/documentation' require 'prettyprint' class Puppet::Interface::Action - include Puppet::Interface::DocSupport + include Puppet::Interface::FullDocs def initialize(face, name, attrs = {}) raise "#{name.inspect} is an invalid action name" unless name.to_s =~ /^[a-z]\w*$/ diff --git a/lib/puppet/interface/documentation.rb b/lib/puppet/interface/documentation.rb index f3bf33da5..d0bfbb261 100644 --- a/lib/puppet/interface/documentation.rb +++ b/lib/puppet/interface/documentation.rb @@ -1,5 +1,5 @@ class Puppet::Interface - module DocSupport + module TinyDocs attr_accessor :summary def summary(value = nil) self.summary = value unless value.nil? @@ -18,6 +18,10 @@ class Puppet::Interface self.description = value unless value.nil? @description end + end + + module FullDocs + include TinyDocs attr_accessor :examples def examples(value = nil) diff --git a/lib/puppet/interface/option.rb b/lib/puppet/interface/option.rb index 3d3840ff6..493b5c3bd 100644 --- a/lib/puppet/interface/option.rb +++ b/lib/puppet/interface/option.rb @@ -1,6 +1,10 @@ require 'puppet/interface' class Puppet::Interface::Option + include Puppet::Interface::FullDocs + # For compatibility, deprecated, and should go fairly soon... + ['', '='].each { |x| alias :"desc#{x}" :"description#{x}" } + def initialize(parent, *declaration, &block) @parent = parent @optparse = [] @@ -80,7 +84,7 @@ class Puppet::Interface::Option end attr_reader :parent, :name, :aliases, :optparse - attr_accessor :required, :desc + attr_accessor :required attr_accessor :before_action def before_action=(proc) diff --git a/spec/shared_behaviours/things_that_declare_options.rb b/spec/shared_behaviours/things_that_declare_options.rb index 5300a159a..3d33bab7f 100755 --- a/spec/shared_behaviours/things_that_declare_options.rb +++ b/spec/shared_behaviours/things_that_declare_options.rb @@ -28,6 +28,8 @@ shared_examples_for "things that declare options" do thing = add_options_to do option "--foo" do desc text + description text + summary text end end diff --git a/spec/unit/interface/option_builder_spec.rb b/spec/unit/interface/option_builder_spec.rb index e9346852c..3e91c683b 100755 --- a/spec/unit/interface/option_builder_spec.rb +++ b/spec/unit/interface/option_builder_spec.rb @@ -16,13 +16,15 @@ describe Puppet::Interface::OptionBuilder do option.should be_an_instance_of Puppet::Interface::Option end - it "should support documentation declarations" do - text = "this is the description" - option = Puppet::Interface::OptionBuilder.build(face, "--foo") do - desc text + [:description, :summary].each do |doc| + it "should support #{doc} declarations" do + text = "this is the #{doc}" + option = Puppet::Interface::OptionBuilder.build(face, "--foo") do + self.send doc, text + end + option.should be_an_instance_of Puppet::Interface::Option + option.send(doc).should == text end - option.should be_an_instance_of Puppet::Interface::Option - option.desc.should == text end context "before_action hook" do -- cgit From 0256d67e1a51a37f2c87ec197bdff6ef3a6b269f Mon Sep 17 00:00:00 2001 From: Daniel Pittman Date: Wed, 27 Apr 2011 10:38:41 -0700 Subject: (#6962) Add integration tests on Face documentation. We now run all the faces, and their actions, as well as global help through the wringer in this test: this way we can be confident that we have, at least, the ability to generate the help without a user-visible failure. We also check that we have set copyright and license terms in our own faces. Theoretically this might fail if the end user has extra faces on LOAD_PATH, but my hope is that we won't hit that... --- lib/puppet/interface/option.rb | 2 +- spec/integration/faces/documentation_spec.rb | 55 ++++++++++++++++++++++++++++ spec/lib/puppet/face/basetest.rb | 2 + spec/lib/puppet/face/huzzah.rb | 2 + spec/lib/puppet/face/version_matching.rb | 2 + 5 files changed, 62 insertions(+), 1 deletion(-) create mode 100755 spec/integration/faces/documentation_spec.rb diff --git a/lib/puppet/interface/option.rb b/lib/puppet/interface/option.rb index 493b5c3bd..b68bdeb12 100644 --- a/lib/puppet/interface/option.rb +++ b/lib/puppet/interface/option.rb @@ -1,7 +1,7 @@ require 'puppet/interface' class Puppet::Interface::Option - include Puppet::Interface::FullDocs + include Puppet::Interface::TinyDocs # For compatibility, deprecated, and should go fairly soon... ['', '='].each { |x| alias :"desc#{x}" :"description#{x}" } diff --git a/spec/integration/faces/documentation_spec.rb b/spec/integration/faces/documentation_spec.rb new file mode 100755 index 000000000..9ddf2f1b3 --- /dev/null +++ b/spec/integration/faces/documentation_spec.rb @@ -0,0 +1,55 @@ +#!/usr/bin/env rspec +require 'spec_helper' +require 'puppet/face' + +describe "documentation of faces" do + it "should generate global help" do + help = nil + expect { help = Puppet::Face[:help, :current].help }.not_to raise_error + help.should be_an_instance_of String + help.length.should be > 200 + end + + ######################################################################## + # Can we actually generate documentation for the face, and the actions it + # has? This avoids situations where the ERB template turns out to have a + # bug in it, triggered in something the user might do. + Puppet::Face.faces.sort.each do |face_name| + # REVISIT: We should walk all versions of the face here... + let :help do Puppet::Face[:help, :current] end + + context "generating help" do + it "for #{face_name}" do + expect { + text = help.help(face_name) + text.should be_an_instance_of String + text.length.should be > 100 + }.not_to raise_error + end + + Puppet::Face[face_name, :current].actions.sort.each do |action_name| + it "for #{face_name}.#{action_name}" do + expect { + text = help.help(face_name, action_name) + text.should be_an_instance_of String + text.length.should be > 100 + }.not_to raise_error + end + end + end + + ######################################################################## + # Ensure that we have authorship and copyright information in *our* faces; + # if you apply this to third party faces you might well be disappointed. + context "licensing of Puppet Labs face '#{face_name}'" do + subject { Puppet::Face[face_name, :current] } + its :license do should =~ /Apache\s*2/ end + its :copyright do should =~ /Puppet Labs/ end + + # REVISIT: This is less that ideal, I think, but right now I am more + # comfortable watching us ship with some copyright than without any; we + # can redress that when it becomes appropriate. --daniel 2011-04-27 + its :copyright do should =~ /2011/ end + end + end +end diff --git a/spec/lib/puppet/face/basetest.rb b/spec/lib/puppet/face/basetest.rb index a98bc382f..41a4ef3f9 100755 --- a/spec/lib/puppet/face/basetest.rb +++ b/spec/lib/puppet/face/basetest.rb @@ -1,6 +1,8 @@ require 'puppet/face' Puppet::Face.define(:basetest, '0.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" summary "This is just so tests don't fail" option "--[no-]boolean" diff --git a/spec/lib/puppet/face/huzzah.rb b/spec/lib/puppet/face/huzzah.rb index 3428c6816..2c2b7aa8d 100755 --- a/spec/lib/puppet/face/huzzah.rb +++ b/spec/lib/puppet/face/huzzah.rb @@ -1,5 +1,7 @@ require 'puppet/face' Puppet::Face.define(:huzzah, '2.0.1') do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" summary "life is a thing for celebration" action :bar do "is where beer comes from" end end diff --git a/spec/lib/puppet/face/version_matching.rb b/spec/lib/puppet/face/version_matching.rb index bfd0013f7..52bc71dbd 100644 --- a/spec/lib/puppet/face/version_matching.rb +++ b/spec/lib/puppet/face/version_matching.rb @@ -4,6 +4,8 @@ require 'puppet/face' # change this you need to ensure that is still correct. --daniel 2011-04-21 ['1.0.0', '1.0.1', '1.1.0', '1.1.1', '2.0.0'].each do |version| Puppet::Face.define(:version_matching, version) do + copyright "Puppet Labs", 2011 + license "Apache 2 license; see COPYING" summary "version matching face #{version}" script :version do version end end -- cgit