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

cli_class[R]

The Executable subclass to which this help applies.

Public Class Methods

new(cli_class) click to toggle source

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
section(name, &default) click to toggle source
# 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

inspect(format=nil)
Alias for: to_s
manpage(dir=nil) click to toggle source

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
markdown() click to toggle source

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
Also aliased as: md
md()
Alias for: markdown
option(name, description) click to toggle source

Set description of an option.

# File lib/executable/help.rb, line 108
def option(name, description)
  @options[name.to_s] = description
end
option_list() click to toggle source
# 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(hint=nil)
Alias for: show_help
show_help(hint=nil) click to toggle source

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
Also aliased as: show
show_manpage(file) click to toggle source
# File lib/executable/help.rb, line 136
def show_manpage(file)
  #exec "man #{file}"
  system "man #{file}"
end
subcommand(name, description) click to toggle source

Set desciption of a subcommand.

# File lib/executable/help.rb, line 115
def subcommand(name, description)
  @subcmds[name.to_s] = description
end
text() click to toggle source

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
Also aliased as: txt
text_description() click to toggle source

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
text_options() click to toggle source

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
text_subcommands() click to toggle source

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
to_s(format=nil) click to toggle source

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
Also aliased as: inspect
txt()
Alias for: text

Private Instance Methods

call_method() click to toggle source
# File lib/executable/help.rb, line 338
def call_method
  @call_method ||= method_list.find{ |m| m.name == :call }
end
lookup(glob, dir) click to toggle source
# 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
method_list() click to toggle source

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