class Executable::Help
Encpsulates command help for defining and displaying well formated help output in plain text, markdown or via manpages if found.
TODO: Currently doesn’t hande aliases/shortcuts well and simply lists them as separate entries.
Creating text help in the fly is fine for personal projects, but for production app, ideally you want to have man pages. You can use the markdown
method to generate ‘.ronn` files and use the ronn tool to build manpages for your project. There is also the binman and md2man projects which can be used similarly.
Attributes
The Executable
subclass to which this help applies.
Public Class Methods
Setup new help object.
# File lib/executable/help.rb, line 34 def initialize(cli_class) @cli_class = cli_class @name = nil @usage = nil @decription = nil @copying = nil @see_also = nil @options = {} @subcmds = {} end
# File lib/executable/help.rb, line 20 def self.section(name, &default) define_method("default_#{name}", &default) class_eval %{ def #{name}(text=nil) @#{name} = text.to_s unless text.nil? @#{name} ||= default_#{name} end def #{name}=(text) @#{name} = text.to_s end } end
Public Instance Methods
Get man-page if there is one.
# File lib/executable/help.rb, line 144 def manpage(dir=nil) @manpage ||= ( man = [] dir = nil if dir raise unless File.directory?(dir) end if !dir && call_method file, line = call_method.source_location dir = File.dirname(file) end man_name = name.gsub(/\s+/, '-') + '.1' if dir glob = "man/{man1/,}#{man_name}" lookup(glob, dir) else nil end ) end
Generate a RONN-style Markdown.
# File lib/executable/help.rb, line 218 def markdown commands = text_subcommands options = text_options dashname = name.sub(/\s+/, '-') s = [] h = "#{dashname}(1) - #{text_description}" s << h + "\n" + ("=" * h.size) s << "## SYNOPSIS" s << "`" + name + "` [options...] [subcommand]" s << "## DESCRIPTION" s << text_description if !commands.empty? s << "## COMMANDS" s << commands.map{ |cmd, desc| " * `%s:`\n %s" % [cmd, desc] }.join("\n") end if !options.empty? s << "## OPTIONS" s << options.map{ |max, opt| " * `#{opt.mark}%s`:\n %s" % [opt.usage, opt.description] }.join("\n\n") end if copyright && !copyright.empty? s << "## COPYRIGHT" s << copyright end if see_also && !see_also.empty? s << "## SEE ALSO" s << see_also end s.compact.join("\n\n") end
Set description of an option.
# File lib/executable/help.rb, line 108 def option(name, description) @options[name.to_s] = description end
# File lib/executable/help.rb, line 324 def option_list @option_list ||= ( method_list.map do |meth| case meth.name when /^(.*?)[\!\=]$/ Option.new(meth) end end.compact.sort ) end
Show help.
@todo man-pages will probably fail on Windows.
# File lib/executable/help.rb, line 124 def show_help(hint=nil) if file = manpage(hint) show_manpage(file) else puts self end end
# File lib/executable/help.rb, line 136 def show_manpage(file) #exec "man #{file}" system "man #{file}" end
Set desciption of a subcommand.
# File lib/executable/help.rb, line 115 def subcommand(name, description) @subcmds[name.to_s] = description end
Generate plain text output.
# File lib/executable/help.rb, line 185 def text commands = text_subcommands options = text_options s = [] s << usage s << text_description if !commands.empty? s << "COMMANDS\n" + commands.map{ |cmd, desc| " %-17s %s" % [cmd, desc] }.join("\n") end if !options.empty? s << "OPTIONS\n" + options.map{ |max, opt| " %2s%-#{max}s %s" % [opt.mark, opt.usage, opt.description] }.join("\n") end s << copyright s << see_also s.compact.join("\n\n") end
Description of command in printable form. But will return nil
if there is no description.
@return [String,NilClass] command description
# File lib/executable/help.rb, line 270 def text_description return description if description #return Source.get_above_comment(@file, @line) if @file call_method ? call_method.comment : nil end
List of options coverted to a printable string. But will return nil
if there are no options.
@return [Array<Fixnum, Options>] option list for output
# File lib/executable/help.rb, line 297 def text_options option_list.each do |opt| if @options.key?(opt.name) opt.description = @options[opt.name] end end max = option_list.map{ |opt| opt.usage.size }.max.to_i + 2 option_list.map do |opt| [max, opt] end end
List of subcommands converted to a printable string. But will return nil
if there are no subcommands.
@return [String,NilClass] subcommand list text
# File lib/executable/help.rb, line 283 def text_subcommands commands = @cli_class.subcommands commands.map do |cmd, klass| desc = klass.help.text_description.to_s.split("\n").first [cmd, desc] end end
Render help text to a given format
. If no format it given then renders to plain text.
# File lib/executable/help.rb, line 173 def to_s(format=nil) case format when :markdown, :md markdown else text end end
Private Instance Methods
# File lib/executable/help.rb, line 338 def call_method @call_method ||= method_list.find{ |m| m.name == :call } end
# File lib/executable/help.rb, line 363 def lookup(glob, dir) dir = File.expand_path(dir) root = '/' home = File.expand_path('~') list = [] while dir != home && dir != root list.concat(Dir.glob(File.join(dir, glob))) break unless list.empty? dir = File.dirname(dir) end list.first end
Produce a list relavent methods.
# File lib/executable/help.rb, line 345 def method_list list = [] methods = [] stop_at = cli_class.ancestors.index(Executable::Command) || cli_class.ancestors.index(Executable) || -1 ancestors = cli_class.ancestors[0...stop_at] ancestors.reverse_each do |a| a.instance_methods(false).each do |m| list << cli_class.instance_method(m) end end list end