diff --git a/lib/puppet/faces/help.rb b/lib/puppet/faces/help.rb index 17ce3ec6b..c5f4b0fe6 100644 --- a/lib/puppet/faces/help.rb +++ b/lib/puppet/faces/help.rb @@ -1,117 +1,85 @@ require 'puppet/faces' require 'puppet/util/command_line' +require 'pathname' +require 'erb' Puppet::Faces.define(:help, '0.0.1') do - HelpSummaryFormat = ' %-18s %s' - summary "Displays help about puppet subcommands" action(:help) do summary "Display help about faces and their actions." option "--version VERSION" do desc "Which version of the interface to show help for" end when_invoked do |*args| # Check our invocation, because we want varargs and can't do defaults # yet. REVISIT: when we do option defaults, and positional options, we # should rewrite this to use those. --daniel 2011-04-04 options = args.pop if options.nil? or args.length > 2 then raise ArgumentError, "help only takes two (optional) arguments, a face name, and an action" end version = :current if options.has_key? :version then if options[:version].to_s !~ /^current$/i then version = options[:version] else if args.length == 0 then raise ArgumentError, "version only makes sense when a face is given" end end end # Name those parameters... facename, actionname = args face = facename ? Puppet::Faces[facename.to_sym, version] : nil action = (face and actionname) ? face.get_action(actionname.to_sym) : nil - # Finally, build up the help text. Maybe ERB would have been nicer - # after all. Oh, well. --daniel 2011-04-11 - message = [] - if args.length == 0 then - message << "Use: puppet [options] [options]" - message << "" - message << "Available subcommands, from Puppet Faces:" - Puppet::Faces.faces.sort.each do |name| - face = Puppet::Faces[name, :current] - message << format(HelpSummaryFormat, face.name, face.summary) - end - - unless legacy_applications.empty? then # great victory when this is true! - message << "" - message << "Available applications, soon to be ported to Faces:" - legacy_applications.each do |appname| - summary = horribly_extract_summary_from appname - message << format(HelpSummaryFormat, appname, summary) - end - end - - message << "" - message << < ' for help on a specific subcommand action. -See 'puppet help ' for help on a specific subcommand. -See 'puppet man ' for the full man page. -Puppet v#{Puppet::PUPPETVERSION} -EOT - elsif args.length == 1 then - message << "Use: puppet #{face.name} [options] [options]" - message << "" + template = case args.length + when 0 then erb_template 'global.erb' + when 1 then erb_template 'face.erb' + when 2 then erb_template 'action.erb' + else + fail ArgumentError, "Too many arguments to help action" + end - message << "Available actions:" - face.actions.each do |actionname| - action = face.get_action(actionname) - message << format(HelpSummaryFormat, action.name, action.summary) - end - - elsif args.length == 2 - "REVISIT: gotta write this code." - else - raise ArgumentError, "help only takes two arguments, a face name and an action" - end - - message + return ERB.new(template, nil, '%').result(binding) end end + def erb_template(name) + (Pathname(__FILE__).dirname + "help" + name).read + end + def legacy_applications # The list of applications, less those that are duplicated as a face. Puppet::Util::CommandLine.available_subcommands.reject do |appname| Puppet::Faces.face? appname.to_sym, :current or # ...this is a nasty way to exclude non-applications. :( %w{faces_base indirection_base}.include? appname end.sort end def horribly_extract_summary_from(appname) begin require "puppet/application/#{appname}" help = Puppet::Application[appname].help.split("\n") # Now we find the line with our summary, extract it, and return it. This # depends on the implementation coincidence of how our pages are # formatted. If we can't match the pattern we expect we return the empty # string to ensure we don't blow up in the summary. --daniel 2011-04-11 while line = help.shift do if md = /^puppet-#{appname}\([^\)]+\) -- (.*)$/.match(line) then return md[1] end end rescue Exception # Damn, but I hate this: we just ignore errors here, no matter what # class they are. Meh. end return '' end end diff --git a/lib/puppet/faces/help/action.erb b/lib/puppet/faces/help/action.erb new file mode 100644 index 000000000..eaf131464 --- /dev/null +++ b/lib/puppet/faces/help/action.erb @@ -0,0 +1,3 @@ +Use: puppet <%= face.name %> [options] <%= action.name %> [options] + +Summary: <%= action.summary %> diff --git a/lib/puppet/faces/help/face.erb b/lib/puppet/faces/help/face.erb new file mode 100644 index 000000000..efe5fd809 --- /dev/null +++ b/lib/puppet/faces/help/face.erb @@ -0,0 +1,7 @@ +Use: puppet <%= face.name %> [options] [options] + +Available actions: +% face.actions.each do |actionname| +% action = face.get_action(actionname) + <%= action.name.to_s.ljust(16) %> <%= action.summary %> +% end diff --git a/lib/puppet/faces/help/global.erb b/lib/puppet/faces/help/global.erb new file mode 100644 index 000000000..e123367a2 --- /dev/null +++ b/lib/puppet/faces/help/global.erb @@ -0,0 +1,20 @@ +puppet [options] [options] + +Available subcommands, from Puppet Faces: +% Puppet::Faces.faces.sort.each do |name| +% face = Puppet::Faces[name, :current] + <%= face.name.to_s.ljust(16) %> <%= face.summary %> +% end + +% unless legacy_applications.empty? then # great victory when this is true! +Available applications, soon to be ported to Faces: +% legacy_applications.each do |appname| +% summary = horribly_extract_summary_from appname + <%= appname.to_s.ljust(16) %> <%= summary %> +% end +% end + +See 'puppet help ' for help on a specific subcommand action. +See 'puppet help ' for help on a specific subcommand. +See 'puppet man ' for the full man page. +Puppet v<%= Puppet::PUPPETVERSION %> diff --git a/spec/unit/faces/help_spec.rb b/spec/unit/faces/help_spec.rb index 61f1947f3..1399abfef 100644 --- a/spec/unit/faces/help_spec.rb +++ b/spec/unit/faces/help_spec.rb @@ -1,105 +1,103 @@ require 'spec_helper' require 'puppet/faces/help' describe Puppet::Faces[:help, '0.0.1'] do it "should have a help action" do subject.should be_action :help end it "should have a default action of help" do pending "REVISIT: we don't support default actions yet" end it "should accept a call with no arguments" do expect { subject.help() }.should_not raise_error end it "should accept a face name" do expect { subject.help(:help) }.should_not raise_error end it "should accept a face and action name" do expect { subject.help(:help, :help) }.should_not raise_error end it "should fail if more than a face and action are given" do expect { subject.help(:help, :help, :for_the_love_of_god) }. should raise_error ArgumentError end it "should treat :current and 'current' identically" do subject.help(:help, :current).should == subject.help(:help, 'current') end it "should complain when the request version of a face is missing" do expect { subject.help(:huzzah, :bar, :version => '17.0.0') }. should raise_error Puppet::Error end it "should find a face by version" do face = Puppet::Faces[:huzzah, :current] subject.help(:huzzah, face.version).should == subject.help(:huzzah, :current) end context "when listing subcommands" do subject { Puppet::Faces[:help, :current].help } # Check a precondition for the next block; if this fails you have # something odd in your set of faces, and we skip testing things that # matter. --daniel 2011-04-10 it "should have at least one face with a summary" do Puppet::Faces.faces.should be_any do |name| Puppet::Faces[name, :current].summary end end Puppet::Faces.faces.each do |name| face = Puppet::Faces[name, :current] summary = face.summary it { should have_matching_element %r{ #{name} } } it { should have_matching_element %r{ #{name} +#{summary}} } if summary end Puppet::Util::CommandLine.available_subcommands do |name| it { should have_matching_element %r{ #{name} } } end end context "when listing legacy applications" do let :help do Puppet::Faces[:help, :current] end # If we don't, these tests are ... less than useful, because they assume # it. When this breaks you should consider ditching the entire feature # and tests, but if not work out how to fake one. --daniel 2011-04-11 it "should have at least one legacy application" do help.legacy_applications.should have_at_least(1).item end # Meh. This is nasty, but we can't control the other list; the specific # bug that caused these to be listed is annoyingly subtle and has a nasty # fix, so better to have a "fail if you do something daft" trigger in # place here, I think. --daniel 2011-04-11 %w{faces_base indirection_base}.each do |name| it "should not list the #{name} application" do help.legacy_applications.should_not include name end end Puppet::Faces[:help, :current].legacy_applications.each do |appname| it "should list #{appname} in the help output" do help.help.should have_matching_element %r{ #{appname} } end summary = Puppet::Faces[:help, :current].horribly_extract_summary_from(appname) if summary then it "should display the summary of #{appname}" do help.help.should have_matching_element %r{ #{summary}\b} end end end end - - end