#!/usr/bin/env ruby # # = Synopsis # # Print help about puppet types on the console. Run with '-h' to get detailed # help. # # FIXME: (1) Formatting could be a lot prettier # (2) The command line options are kinda screwy; unclear how best to # present the various pieces of info to user require 'puppet' require 'optparse' class Formatter def initialize(width) @width = width end def wrap(txt, opts) return "" unless txt && !txt.empty? work = (opts[:scrub] ? scrub(txt) : txt) indent = (opts[:indent] ? opts[:indent] : 0) textLen = @width - indent patt = Regexp.new("^(.{0,#{textLen}})[ \n]") prefix = " " * indent res = [] while work.length > textLen if work =~ patt res << $1 work.slice!(0, $&.length) else res << work.slice!(0, textLen) end end res << work if work.length.nonzero? return prefix + res.join("\n" + prefix) end def header(txt, sep = "-") "\n#{txt}\n" + sep * txt.size end private def scrub(text) # For text with no carriage returns, there's nothing to do. if text !~ /\n/ return text end indent = nil # If we can match an indentation, then just remove that same level of # indent from every line. if text =~ /^(\s+)/ indent = $1 return text.gsub(/^#{indent}/,'') else return text end end end class TypeDoc def initialize @format = Formatter.new(76) @types = {} Puppet::Type.loadall Puppet::Type.eachtype { |type| next if type.name == :component @types[type.name] = type } end def list_types puts "These are the types known to puppet:\n" @types.keys.sort { |a, b| a.to_s <=> b.to_s }.each do |name| type = @types[name] s = type.doc.gsub(/\s+/, " ") n = s.index(".") if n.nil? s = ".. no documentation .." elsif n > 45 s = s[0, 45] + " ..." else s = s[0, n] end printf "%-15s - %s\n", name, s end end def format_type(name, opts) name = name.to_sym unless @types.has_key?(name) puts "Unknown type #{name}" return end type = @types[name] puts @format.header(name.to_s, "=") puts @format.wrap(type.doc, :indent => 0, :scrub => true) + "\n\n" puts @format.header("Parameters") if opts[:parameters] format_attrs(type, [:property, :param]) else list_attrs(type, [:property, :param]) end if opts[:metaparams] puts @format.header("Meta Parameters") if opts[:parameters] format_attrs(type, [:meta]) else list_attrs(type, [:meta]) end end if type.providers.size > 0 puts @format.header("Providers") if opts[:providers] format_providers(type) else list_providers(type) end end end # List details about attributes def format_attrs(type, attrs) docs = {} type.eachattr do |obj, kind| if attrs.include?(kind) && obj.name != :provider docs[obj.name] = obj.doc end end docs.sort { |a,b| a[0].to_s <=> b[0].to_s }.each { |name, doc| print "\n- **%s**" % name if type.namevar == name and name != :name puts " (*namevar*)" else puts "" end puts @format.wrap(doc, :indent => 4, :scrub => true) } end # List the names of attributes def list_attrs(type, attrs) params = [] type.eachattr do |obj, kind| if attrs.include?(kind) && obj.name != :provider params << obj.name.to_s end end puts @format.wrap(params.sort.join(", "), :indent => 4) end def format_providers(type) type.providers.sort { |a,b| a.to_s <=> b.to_s }.each { |prov| puts "\n- **%s**" % prov puts @format.wrap(type.provider(prov).doc, :indent => 4, :scrub => true) } end def list_providers(type) list = type.providers.sort { |a,b| a.to_s <=> b.to_s }.join(", ") puts @format.wrap(list, :indent => 4) end end def process_args result = { :list => false, :providers => false, :parameters => true, :metaparams => false } opts = OptionParser.new("#{$0} [options] [type]") opts.separator(" Print documentation for puppet types and their parameters") opts.on("-l", "--list", "List all types") do |val| result[:list] = true end opts.on("-p", "--providers", "Describe providers in detail") do |val| result[:providers] = true end opts.on("-s", "--short", "Only list parameters without detail") do |val| result[:parameters] = false end opts.on("-m", "--meta", "Include metaparams") do |val| result[:metaparams] = true end result[:types] = opts.order(ARGV) # Check for obviously bogus options unless result[:list] || result[:types].size > 0 $stderr.puts opts exit(1) end if result[:list] && result[:types].size > 0 $stderr.puts "Warning: ignoring types when listing all types" end return result end opts = process_args doc = TypeDoc.new if opts[:list] doc.list_types else opts[:types].each { |name| doc.format_type(name, opts) } end