diff --git a/lib/puppet/reference/configuration.rb b/lib/puppet/reference/configuration.rb index 18efb6fe7..c1e225367 100644 --- a/lib/puppet/reference/configuration.rb +++ b/lib/puppet/reference/configuration.rb @@ -1,69 +1,69 @@ config = Puppet::Util::Reference.newreference(:configuration, :depth => 1, :doc => "A reference for all configuration parameters") do docs = {} Puppet.settings.each do |name, object| docs[name] = object end str = "" docs.sort { |a, b| a[0].to_s <=> b[0].to_s }.each do |name, object| # Make each name an anchor header = name.to_s - str += h(header, 3) + str << markdown_header(header, 3) # Print the doc string itself begin - str += object.desc.gsub(/\n/, " ") + str << object.desc.gsub(/\n/, " ") rescue => detail puts detail.backtrace puts detail end - str += "\n\n" + str << "\n\n" # Now print the data about the item. - str += "" + str << "" val = object.default if name.to_s == "vardir" val = "/var/lib/puppet" elsif name.to_s == "confdir" val = "/etc/puppet" end # Leave out the section information; it was apparently confusing people. - #str += "- **Section**: #{object.section}\n" + #str << "- **Section**: #{object.section}\n" unless val == "" - str += "- *Default*: #{val}\n" + str << "- *Default*: #{val}\n" end - str += "\n" + str << "\n" end return str end config.header = < "Indirection types and their terminus classes" do text = "" Puppet::Indirector::Indirection.instances.sort { |a,b| a.to_s <=> b.to_s }.each do |indirection| ind = Puppet::Indirector::Indirection.instance(indirection) name = indirection.to_s.capitalize - text += "## " + indirection.to_s + "\n\n" + text << "## " + indirection.to_s + "\n\n" - text += ind.doc + "\n\n" + text << ind.doc + "\n\n" Puppet::Indirector::Terminus.terminus_classes(ind.name).sort { |a,b| a.to_s <=> b.to_s }.each do |terminus| - text += "### " + terminus.to_s + "\n\n" - + terminus_name = terminus.to_s term_class = Puppet::Indirector::Terminus.terminus_class(ind.name, terminus) - - text += Puppet::Util::Docs.scrub(term_class.doc) + "\n\n" + terminus_doc = Puppet::Util::Docs.scrub(term_class.doc) + text << markdown_definitionlist(terminus_name, terminus_doc) end end text end reference.header = "This is the list of all indirections, their associated terminus classes, and how you select between them. In general, the appropriate terminus class is selected by the application for you (e.g., `puppet agent` would always use the `rest` terminus for most of its indirected classes), but some classes are tunable via normal settings. These will have `terminus setting` documentation listed with them. " diff --git a/lib/puppet/reference/metaparameter.rb b/lib/puppet/reference/metaparameter.rb index 3c4c08701..9235d3d26 100644 --- a/lib/puppet/reference/metaparameter.rb +++ b/lib/puppet/reference/metaparameter.rb @@ -1,41 +1,43 @@ metaparameter = Puppet::Util::Reference.newreference :metaparameter, :doc => "All Puppet metaparameters and all their details" do types = {} Puppet::Type.loadall Puppet::Type.eachtype { |type| next if type.name == :puppet next if type.name == :component types[type.name] = type } str = %{ # Metaparameters Metaparameters are parameters that work with any resource type; they are part of the Puppet framework itself rather than being part of the implementation of any given instance. Thus, any defined metaparameter can be used with any instance in your manifest, including defined components. ## Available Metaparameters } begin params = [] Puppet::Type.eachmetaparam { |param| params << param } params.sort { |a,b| a.to_s <=> b.to_s }.each { |param| - str += paramwrap(param.to_s, scrub(Puppet::Type.metaparamdoc(param)), :level => 3) + str << markdown_header(param.to_s, 3) + str << scrub(Puppet::Type.metaparamdoc(param)) + str << "\n\n" } rescue => detail puts detail.backtrace puts "incorrect metaparams: #{detail}" exit(1) end str end diff --git a/lib/puppet/reference/network.rb b/lib/puppet/reference/network.rb index fda7931fb..ee8fea07e 100644 --- a/lib/puppet/reference/network.rb +++ b/lib/puppet/reference/network.rb @@ -1,39 +1,39 @@ require 'puppet/network/handler' network = Puppet::Util::Reference.newreference :network, :depth => 2, :doc => "Available network handlers and clients" do ret = "" Puppet::Network::Handler.subclasses.sort { |a,b| a.to_s <=> b.to_s }.each do |name| handler = Puppet::Network::Handler.handler(name) next if ! handler.doc or handler.doc == "" interface = handler.interface - ret += h(name, 2) - - ret += scrub(handler.doc) - ret += "\n\n" - ret += option(:prefix, interface.prefix) - ret += option(:side, handler.side.to_s.capitalize) - ret += option(:methods, interface.methods.collect { |ary| ary[0] }.join(", ") ) - ret += "\n\n" + ret << markdown_header(name, 2) + + ret << scrub(handler.doc) + ret << "\n\n" + ret << option(:prefix, interface.prefix) + ret << option(:side, handler.side.to_s.capitalize) + ret << option(:methods, interface.methods.collect { |ary| ary[0] }.join(", ") ) + ret << "\n\n" end ret end network.header = " This is a list of all Puppet network interfaces. Each interface is implemented in the form of a client and a handler; the handler is loaded on the server, and the client knows how to call the handler's methods appropriately. Most handlers are meant to be started on the server, usually within `puppet master`, and the clients are mostly started on the client, usually within `puppet agent`. You can find the server-side handler for each interface at `puppet/network/handler/.rb` and the client class at `puppet/network/client/.rb`. " diff --git a/lib/puppet/reference/providers.rb b/lib/puppet/reference/providers.rb index c85ad23ab..576b00bcb 100644 --- a/lib/puppet/reference/providers.rb +++ b/lib/puppet/reference/providers.rb @@ -1,123 +1,123 @@ # This doesn't get stored in trac, since it changes every time. providers = Puppet::Util::Reference.newreference :providers, :title => "Provider Suitability Report", :depth => 1, :dynamic => true, :doc => "Which providers are valid for this machine" do types = [] Puppet::Type.loadall Puppet::Type.eachtype do |klass| next unless klass.providers.length > 0 types << klass end types.sort! { |a,b| a.name.to_s <=> b.name.to_s } command_line = Puppet::Util::CommandLine.new types.reject! { |type| ! command_line.args.include?(type.name.to_s) } unless command_line.args.empty? ret = "Details about this host:\n\n" # Throw some facts in there, so we know where the report is from. ["Ruby Version", "Puppet Version", "Operating System", "Operating System Release"].each do |label| name = label.gsub(/\s+/, '') value = Facter.value(name) - ret += option(label, value) + ret << option(label, value) end - ret += "\n" + ret << "\n" count = 1 # Produce output for each type. types.each do |type| features = type.features - ret += "\n" # add a trailing newline + ret << "\n" # add a trailing newline # Now build up a table of provider suitability. headers = %w{Provider Suitable?} + features.collect { |f| f.to_s }.sort table_data = {} functional = false notes = [] begin default = type.defaultprovider.name rescue Puppet::DevError default = "none" end type.providers.sort { |a,b| a.to_s <=> b.to_s }.each do |pname| data = [] table_data[pname] = data provider = type.provider(pname) # Add the suitability note if missing = provider.suitable?(false) and missing.empty? data << "*X*" suit = true functional = true else data << "[#{count}]_" # A pointer to the appropriate footnote suit = false end # Add a footnote with the details about why this provider is unsuitable, if that's the case unless suit details = ".. [#{count}]\n" missing.each do |test, values| case test when :exists - details += " - Missing files #{values.join(", ")}\n" + details << " - Missing files #{values.join(", ")}\n" when :variable values.each do |name, facts| if Puppet.settings.valid?(name) - details += " - Setting #{name} (currently #{Puppet.settings.value(name).inspect}) not in list #{facts.join(", ")}\n" + details << " - Setting #{name} (currently #{Puppet.settings.value(name).inspect}) not in list #{facts.join(", ")}\n" else - details += " - Fact #{name} (currently #{Facter.value(name).inspect}) not in list #{facts.join(", ")}\n" + details << " - Fact #{name} (currently #{Facter.value(name).inspect}) not in list #{facts.join(", ")}\n" end end when :true - details += " - Got #{values} true tests that should have been false\n" + details << " - Got #{values} true tests that should have been false\n" when :false - details += " - Got #{values} false tests that should have been true\n" + details << " - Got #{values} false tests that should have been true\n" when :feature - details += " - Missing features #{values.collect { |f| f.to_s }.join(",")}\n" + details << " - Missing features #{values.collect { |f| f.to_s }.join(",")}\n" end end notes << details count += 1 end # Add a note for every feature features.each do |feature| if provider.features.include?(feature) data << "*X*" else data << "" end end end - ret += h(type.name.to_s + "_", 2) + ret << markdown_header(type.name.to_s + "_", 2) - ret += "[#{type.name}](#{"http://docs.puppetlabs.com/references/stable/type.html##{type.name}"})\n\n" - ret += option("Default provider", default) - ret += doctable(headers, table_data) + ret << "[#{type.name}](#{"http://docs.puppetlabs.com/references/stable/type.html##{type.name}"})\n\n" + ret << option("Default provider", default) + ret << doctable(headers, table_data) notes.each do |note| - ret += note + "\n" + ret << note + "\n" end - ret += "\n" + ret << "\n" end - ret += "\n" + ret << "\n" ret end providers.header = " Puppet resource types are usually backed by multiple implementations called `providers`, which handle variance between platforms and tools. Different providers are suitable or unsuitable on different platforms based on things like the presence of a given tool. Here are all of the provider-backed types and their different providers. Any unmentioned types do not use providers yet. " diff --git a/lib/puppet/reference/type.rb b/lib/puppet/reference/type.rb index 65dd2538c..87aed435c 100644 --- a/lib/puppet/reference/type.rb +++ b/lib/puppet/reference/type.rb @@ -1,113 +1,116 @@ type = Puppet::Util::Reference.newreference :type, :doc => "All Puppet resource types and all their details" do types = {} Puppet::Type.loadall Puppet::Type.eachtype { |type| next if type.name == :puppet next if type.name == :component next if type.name == :whit types[type.name] = type } str = %{ ## Resource Types - The *namevar* is the parameter used to uniquely identify a type instance. This is the parameter that gets assigned when a string is provided before the colon in a type declaration. In general, only developers will need to worry about which parameter is the `namevar`. In the following code: file { "/etc/passwd": owner => root, group => root, mode => 644 } `/etc/passwd` is considered the title of the file object (used for things like dependency handling), and because `path` is the namevar for `file`, that string is assigned to the `path` parameter. - *Parameters* determine the specific configuration of the instance. They either directly modify the system (internally, these are called properties) or they affect how the instance behaves (e.g., adding a search path for `exec` instances or determining recursion on `file` instances). - *Providers* provide low-level functionality for a given resource type. This is usually in the form of calling out to external commands. When required binaries are specified for providers, fully qualifed paths indicate that the binary must exist at that specific path and unqualified binaries indicate that Puppet will search for the binary using the shell path. - *Features* are abilities that some providers might not support. You can use the list of supported features to determine how a given provider can be used. Resource types define features they can use, and providers can be tested to see which features they provide. } types.sort { |a,b| a.to_s <=> b.to_s }.each { |name,type| - str += " + str << " ---------------- " - str += h(name, 3) - str += scrub(type.doc) + "\n\n" + str << markdown_header(name, 3) + str << scrub(type.doc) + "\n\n" # Handle the feature docs. if featuredocs = type.featuredocs - str += h("Features", 4) - str += featuredocs + str << markdown_header("Features", 4) + str << featuredocs end docs = {} type.validproperties.sort { |a,b| a.to_s <=> b.to_s }.reject { |sname| property = type.propertybyname(sname) property.nodoc }.each { |sname| property = type.propertybyname(sname) raise "Could not retrieve property #{sname} on type #{type.name}" unless property doc = nil unless doc = property.doc $stderr.puts "No docs for #{type}[#{sname}]" next end doc = doc.dup tmp = doc tmp = scrub(tmp) docs[sname] = tmp } - str += h("Parameters", 4) + "\n" + str << markdown_header("Parameters", 4) + "\n" type.parameters.sort { |a,b| a.to_s <=> b.to_s }.each { |name,param| #docs[name] = indent(scrub(type.paramdoc(name)), $tab) docs[name] = scrub(type.paramdoc(name)) } additional_key_attributes = type.key_attributes - [:name] docs.sort { |a, b| a[0].to_s <=> b[0].to_s }.each { |name, doc| - str += paramwrap(name, doc, :namevar => additional_key_attributes.include?(name)) + if additional_key_attributes.include?(name) + doc = "(**Namevar:** If omitted, this parameter's value defaults to the resource's title.)\n\n" + doc + end + str << markdown_definitionlist(name, doc) } - str += "\n" + str << "\n" } str end diff --git a/lib/puppet/util/reference.rb b/lib/puppet/util/reference.rb index a4921ed2a..ae5f2d44c 100644 --- a/lib/puppet/util/reference.rb +++ b/lib/puppet/util/reference.rb @@ -1,140 +1,139 @@ require 'puppet/util/instance_loader' require 'fileutils' # Manage Reference Documentation. class Puppet::Util::Reference include Puppet::Util include Puppet::Util::Docs extend Puppet::Util::InstanceLoader instance_load(:reference, 'puppet/reference') def self.footer "\n\n----------------\n\n*This page autogenerated on #{Time.now}*\n" end def self.modes %w{pdf text} end def self.newreference(name, options = {}, &block) ref = self.new(name, options, &block) instance_hash(:reference)[symbolize(name)] = ref ref end def self.page(*sections) depth = 4 # Use the minimum depth sections.each do |name| section = reference(name) or raise "Could not find section #{name}" depth = section.depth if section.depth < depth end end def self.pdf(text) puts "creating pdf" Puppet::Util.secure_open("/tmp/puppetdoc.txt", "w") do |f| f.puts text end rst2latex = which('rst2latex') || which('rst2latex.py') || raise("Could not find rst2latex") cmd = %{#{rst2latex} /tmp/puppetdoc.txt > /tmp/puppetdoc.tex} Puppet::Util.secure_open("/tmp/puppetdoc.tex","w") do |f| # If we get here without an error, /tmp/puppetdoc.tex isn't a tricky cracker's symlink end output = %x{#{cmd}} unless $CHILD_STATUS == 0 $stderr.puts "rst2latex failed" $stderr.puts output exit(1) end $stderr.puts output # Now convert to pdf Dir.chdir("/tmp") do %x{texi2pdf puppetdoc.tex >/dev/null 2>/dev/null} end end def self.references instance_loader(:reference).loadall loaded_instances(:reference).sort { |a,b| a.to_s <=> b.to_s } end HEADER_LEVELS = [nil, "#", "##", "###", "####", "#####"] attr_accessor :page, :depth, :header, :title, :dynamic attr_writer :doc def doc if defined?(@doc) return "#{@name} - #{@doc}" else return @title end end def dynamic? self.dynamic end - def h(name, level) + def markdown_header(name, level) "#{HEADER_LEVELS[level]} #{name}\n\n" end + def markdown_definitionlist(term, definition) + lines = definition.split("\n") + str = "#{term}\n: #{lines.shift}\n" + lines.each do |line| + str << " " if line =~ /\S/ + str << "#{line}\n" + end + str << "\n" + end + def initialize(name, options = {}, &block) @name = name options.each do |option, value| send(option.to_s + "=", value) end meta_def(:generate, &block) # Now handle the defaults @title ||= "#{@name.to_s.capitalize} Reference" @page ||= @title.gsub(/\s+/, '') @depth ||= 2 @header ||= "" end # Indent every line in the chunk except those which begin with '..'. def indent(text, tab) text.gsub(/(^|\A)/, tab).gsub(/^ +\.\./, "..") end def option(name, value) ":#{name.to_s.capitalize}: #{value}\n" end - def paramwrap(name, text, options = {}) - options[:level] ||= 5 - #str = "#{name} : " - str = h(name, options[:level]) - str += "- **namevar**\n\n" if options[:namevar] - str += text - #str += text.gsub(/\n/, "\n ") - - str += "\n\n" - end - def text puts output end def to_markdown(withcontents = true) # First the header - text = h(@title, 1) - text += "\n\n**This page is autogenerated; any changes will get overwritten** *(last generated on #{Time.now.to_s})*\n\n" + text = markdown_header(@title, 1) + text << "\n\n**This page is autogenerated; any changes will get overwritten** *(last generated on #{Time.now.to_s})*\n\n" - text += @header + text << @header - text += generate + text << generate - text += self.class.footer if withcontents + text << self.class.footer if withcontents text end end diff --git a/spec/unit/util/reference_spec.rb b/spec/unit/util/reference_spec.rb new file mode 100644 index 000000000..219a673ef --- /dev/null +++ b/spec/unit/util/reference_spec.rb @@ -0,0 +1,29 @@ +#!/usr/bin/env rspec +require 'spec_helper' +require 'puppet/util/reference' + +describe Puppet::Util::Reference do + it "should create valid Markdown extension definition lists" do + my_fragment = nil + Puppet::Util::Reference.newreference :testreference, :doc => "A peer of the type and configuration references, but with no useful information" do + my_term = "A term" + my_definition = <<-EOT +The definition of this term. +We should be able to handle multi-line definitions. + +We should be able to handle multi-paragraph definitions. + EOT + my_fragment = markdown_definitionlist(my_term, my_definition) + end + Puppet::Util::Reference.reference(:testreference).send(:to_markdown, true) + my_fragment.should == <<-EOT +A term +: The definition of this term. + We should be able to handle multi-line definitions. + + We should be able to handle multi-paragraph definitions. + + EOT + end + +end \ No newline at end of file