class Toys::StandardMiddleware::ShowHelp

A middleware that shows help text for the tool when a flag (typically `–help`) is provided. It can also be configured to show help by default if the tool is a namespace that is not runnable.

If a tool is not runnable, this middleware can also add a `–[no-]recursive` flag, which, when set to `true` (the default), shows all subtools recursively rather than only immediate subtools. This middleware can also search for keywords in its subtools.

Constants

DEFAULT_HELP_FLAGS

Default help flags @return [Array<String>]

DEFAULT_LIST_FLAGS

Default list subtools flags @return [Array<String>]

DEFAULT_RECURSIVE_FLAGS

Default recursive flags @return [Array<String>]

DEFAULT_SEARCH_FLAGS

Default search flags @return [Array<String>]

DEFAULT_SHOW_ALL_SUBTOOLS_FLAGS

Default show-all-subtools flags @return [Array<String>]

DEFAULT_USAGE_FLAGS

Default usage flags @return [Array<String>]

RECURSIVE_SUBTOOLS_KEY

Key for the recursive setting @return [Object]

SEARCH_STRING_KEY

Key for the search string @return [Object]

SHOW_ALL_SUBTOOLS_KEY

Key for the show-all-subtools setting @return [Object]

SHOW_HELP_KEY

Key set when the show help flag is present @return [Object]

SHOW_LIST_KEY

Key set when the show subtool list flag is present @return [Object]

SHOW_USAGE_KEY

Key set when the show usage flag is present @return [Object]

TOOL_NAME_KEY

Key for the tool name @return [Object]

Public Class Methods

new(help_flags: false, usage_flags: false, list_flags: false, recursive_flags: false, search_flags: false, show_all_subtools_flags: false, default_recursive: false, default_show_all_subtools: false, fallback_execution: false, allow_root_args: false, show_source_path: false, separate_sources: false, use_less: false, stream: $stdout, styled_output: nil) click to toggle source

Create a ShowHelp middleware.

@param help_flags [Boolean,Array<String>,Proc] Specify flags to

display help. The value may be any of the following:

*  An array of flags.
*  The `true` value to use {DEFAULT_HELP_FLAGS}.
*  The `false` value for no flags. (Default)
*  A proc that takes a tool and returns any of the above.

@param usage_flags [Boolean,Array<String>,Proc] Specify flags to

display usage. The value may be any of the following:

*  An array of flags.
*  The `true` value to use {DEFAULT_USAGE_FLAGS}.
*  The `false` value for no flags. (Default)
*  A proc that takes a tool and returns any of the above.

@param list_flags [Boolean,Array<String>,Proc] Specify flags to

display subtool list. The value may be any of the following:

*  An array of flags.
*  The `true` value to use {DEFAULT_LIST_FLAGS}.
*  The `false` value for no flags. (Default)
*  A proc that takes a tool and returns any of the above.

@param recursive_flags [Boolean,Array<String>,Proc] Specify flags

to control recursive subtool search. The value may be any of the
following:

*  An array of flags.
*  The `true` value to use {DEFAULT_RECURSIVE_FLAGS}.
*  The `false` value for no flags. (Default)
*  A proc that takes a tool and returns any of the above.

@param search_flags [Boolean,Array<String>,Proc] Specify flags

to search subtools for a search term. The value may be any of
the following:

*  An array of flags.
*  The `true` value to use {DEFAULT_SEARCH_FLAGS}.
*  The `false` value for no flags. (Default)
*  A proc that takes a tool and returns any of the above.

@param show_all_subtools_flags [Boolean,Array<String>,Proc] Specify

flags to show all subtools, including hidden tools and non-runnable
namespaces. The value may be any of the following:

*  An array of flags.
*  The `true` value to use {DEFAULT_SHOW_ALL_SUBTOOLS_FLAGS}.
*  The `false` value for no flags. (Default)
*  A proc that takes a tool and returns any of the above.

@param default_recursive [Boolean] Whether to search recursively for

subtools by default. Default is `false`.

@param default_show_all_subtools [Boolean] Whether to show all subtools

by default. Default is `false`.

@param fallback_execution [Boolean] Cause the tool to display its own

help text if it is not otherwise runnable. This is mostly useful
for namespaces, which have children are not runnable. Default is
`false`.

@param allow_root_args [Boolean] If the root tool includes flags for

help or usage, and doesn't otherwise use positional arguments,
then a tool name can be passed as arguments to display help for
that tool.

@param show_source_path [Boolean] Show the source path section. Default

is `false`.

@param separate_sources [Boolean] Split up tool list by source root.

Defaults to false.

@param use_less [Boolean] If the `less` tool is available, and the

output stream is a tty, then use `less` to display help text.

@param stream [IO] Output stream to write to. Default is stdout. @param styled_output [Boolean,nil] Cause the tool to display help text

with ansi styles. If `nil`, display styles if the output stream is
a tty. Default is `nil`.
# File lib/toys/standard_middleware/show_help.rb, line 171
def initialize(help_flags: false,
               usage_flags: false,
               list_flags: false,
               recursive_flags: false,
               search_flags: false,
               show_all_subtools_flags: false,
               default_recursive: false,
               default_show_all_subtools: false,
               fallback_execution: false,
               allow_root_args: false,
               show_source_path: false,
               separate_sources: false,
               use_less: false,
               stream: $stdout,
               styled_output: nil)
  @help_flags = help_flags
  @usage_flags = usage_flags
  @list_flags = list_flags
  @recursive_flags = recursive_flags
  @search_flags = search_flags
  @show_all_subtools_flags = show_all_subtools_flags
  @default_recursive = default_recursive ? true : false
  @default_show_all_subtools = default_show_all_subtools ? true : false
  @fallback_execution = fallback_execution
  @allow_root_args = allow_root_args
  @show_source_path = show_source_path
  @separate_sources = separate_sources
  @stream = stream
  @styled_output = styled_output
  @use_less = use_less && !Compat.jruby?
end

Public Instance Methods

config(tool, loader) { || ... } click to toggle source

Configure flags and default data. @private

# File lib/toys/standard_middleware/show_help.rb, line 207
def config(tool, loader)
  unless tool.argument_parsing_disabled?
    StandardMiddleware.append_common_flag_group(tool)
    has_subtools = loader.has_subtools?(tool.full_name)
    help_flags = add_help_flags(tool)
    usage_flags = add_usage_flags(tool)
    list_flags = has_subtools ? add_list_flags(tool) : []
    can_display_help = !help_flags.empty? || !list_flags.empty? ||
                       !usage_flags.empty? || @fallback_execution
    if can_display_help && has_subtools
      add_recursive_flags(tool)
      add_search_flags(tool)
      add_show_all_subtools_flags(tool)
    end
  end
  yield
end
run(context) { || ... } click to toggle source

Display help text if requested. @private

# File lib/toys/standard_middleware/show_help.rb, line 229
def run(context)
  if context[SHOW_USAGE_KEY]
    show_usage(context)
  elsif context[SHOW_LIST_KEY]
    show_list(context)
  elsif context[SHOW_HELP_KEY]
    show_help(context, true)
  else
    begin
      yield
    rescue NotRunnableError => e
      raise e unless @fallback_execution
      show_help(context, false)
    end
  end
end

Private Instance Methods

add_help_flags(tool) click to toggle source
# File lib/toys/standard_middleware/show_help.rb, line 336
def add_help_flags(tool)
  flags = resolve_flags_spec(@help_flags, tool, DEFAULT_HELP_FLAGS)
  unless flags.empty?
    tool.add_flag(
      SHOW_HELP_KEY, flags,
      report_collisions: false,
      desc: "Display help for this tool",
      group: StandardMiddleware::COMMON_FLAG_GROUP
    )
  end
  flags
end
add_list_flags(tool) click to toggle source
# File lib/toys/standard_middleware/show_help.rb, line 362
def add_list_flags(tool)
  flags = resolve_flags_spec(@list_flags, tool, DEFAULT_LIST_FLAGS)
  unless flags.empty?
    tool.add_flag(
      SHOW_LIST_KEY, flags,
      report_collisions: false,
      desc: "List the subtools under this tool",
      group: StandardMiddleware::COMMON_FLAG_GROUP
    )
  end
  flags
end
add_recursive_flags(tool) click to toggle source
# File lib/toys/standard_middleware/show_help.rb, line 375
def add_recursive_flags(tool)
  flags = resolve_flags_spec(@recursive_flags, tool, DEFAULT_RECURSIVE_FLAGS)
  if flags.empty?
    tool.default_data[RECURSIVE_SUBTOOLS_KEY] = @default_recursive
  else
    tool.add_flag(
      RECURSIVE_SUBTOOLS_KEY, flags,
      report_collisions: false, default: @default_recursive,
      desc: "List all subtools recursively when displaying help" \
            " (default is #{@default_recursive})",
      group: StandardMiddleware::COMMON_FLAG_GROUP
    )
  end
  flags
end
add_search_flags(tool) click to toggle source
# File lib/toys/standard_middleware/show_help.rb, line 391
def add_search_flags(tool)
  flags = resolve_flags_spec(@search_flags, tool, DEFAULT_SEARCH_FLAGS)
  unless flags.empty?
    tool.add_flag(
      SEARCH_STRING_KEY, flags,
      report_collisions: false,
      desc: "Search subtools for the given regular expression when displaying help",
      group: StandardMiddleware::COMMON_FLAG_GROUP
    )
  end
  flags
end
add_show_all_subtools_flags(tool) click to toggle source
# File lib/toys/standard_middleware/show_help.rb, line 404
def add_show_all_subtools_flags(tool)
  flags = resolve_flags_spec(@show_all_subtools_flags, tool, DEFAULT_SHOW_ALL_SUBTOOLS_FLAGS)
  if flags.empty?
    tool.default_data[SHOW_ALL_SUBTOOLS_KEY] = @default_show_all_subtools
  else
    tool.add_flag(
      SHOW_ALL_SUBTOOLS_KEY, flags,
      report_collisions: false, default: @default_show_all_subtools,
      desc: "List all subtools including hidden subtools and namespaces" \
            " (default is #{@default_show_all_subtools})",
      group: StandardMiddleware::COMMON_FLAG_GROUP
    )
  end
  flags
end
add_usage_flags(tool) click to toggle source
# File lib/toys/standard_middleware/show_help.rb, line 349
def add_usage_flags(tool)
  flags = resolve_flags_spec(@usage_flags, tool, DEFAULT_USAGE_FLAGS)
  unless flags.empty?
    tool.add_flag(
      SHOW_USAGE_KEY, flags,
      report_collisions: false,
      desc: "Display a brief usage string for this tool",
      group: StandardMiddleware::COMMON_FLAG_GROUP
    )
  end
  flags
end
get_help_text(context, use_extra_args) click to toggle source
# File lib/toys/standard_middleware/show_help.rb, line 305
def get_help_text(context, use_extra_args)
  require "toys/utils/help_text"
  if use_extra_args && @allow_root_args && context[Context::Key::TOOL].root?
    tool_name = Array(context[Context::Key::UNMATCHED_POSITIONAL])
    unless tool_name.empty?
      cli = context[Context::Key::CLI]
      loader = cli.loader
      tool, rest = loader.lookup(tool_name)
      help_text = Utils::HelpText.new(tool, loader, cli.executable_name)
      report_usage_error(help_text, loader, tool.full_name, rest.first) unless rest.empty?
      return help_text
    end
  end
  Utils::HelpText.from_context(context)
end
less_path() click to toggle source
# File lib/toys/standard_middleware/show_help.rb, line 294
def less_path
  unless defined? @less_path
    @less_path =
      if @use_less && @stream.tty?
        path = `which less`.strip
        path.empty? ? nil : path
      end
  end
  @less_path
end
report_usage_error(help_text, loader, tool_name, next_word) click to toggle source
# File lib/toys/standard_middleware/show_help.rb, line 321
def report_usage_error(help_text, loader, tool_name, next_word)
  dict = loader.list_subtools(tool_name).map(&:simple_name)
  suggestions = Compat.suggestions(next_word, dict)
  tool_name = (tool_name + [next_word]).join(" ")
  message = "Tool not found: \"#{tool_name}\""
  unless suggestions.empty?
    suggestions_str = suggestions.join("\n                 ")
    message = "#{message}\nDid you mean...  #{suggestions_str}"
  end
  terminal.puts(message, :bright_red, :bold)
  terminal.puts
  terminal.puts help_text.usage_string(wrap_width: terminal.width)
  Context.exit(1)
end
resolve_flags_spec(flags, tool, defaults) click to toggle source
# File lib/toys/standard_middleware/show_help.rb, line 420
def resolve_flags_spec(flags, tool, defaults)
  flags = flags.call(tool) if flags.respond_to?(:call)
  case flags
  when true, :default
    Array(defaults)
  when ::String
    [flags]
  when ::Array
    flags
  else
    []
  end
end
show_help(context, use_extra_args) click to toggle source
# File lib/toys/standard_middleware/show_help.rb, line 276
def show_help(context, use_extra_args)
  help_text = get_help_text(context, use_extra_args)
  str = help_text.help_string(
    recursive: context[RECURSIVE_SUBTOOLS_KEY],
    search: context[SEARCH_STRING_KEY],
    include_hidden: context[SHOW_ALL_SUBTOOLS_KEY],
    show_source_path: @show_source_path,
    separate_sources: @separate_sources,
    wrap_width: terminal.width
  )
  if less_path
    require "toys/utils/exec"
    Utils::Exec.new.exec([less_path, "-R"], in: [:string, str])
  else
    terminal.puts(str)
  end
end
show_list(context) click to toggle source
# File lib/toys/standard_middleware/show_help.rb, line 264
def show_list(context)
  help_text = get_help_text(context, true)
  str = help_text.list_string(
    recursive: context[RECURSIVE_SUBTOOLS_KEY],
    search: context[SEARCH_STRING_KEY],
    include_hidden: context[SHOW_ALL_SUBTOOLS_KEY],
    separate_sources: @separate_sources,
    wrap_width: terminal.width
  )
  terminal.puts(str)
end
show_usage(context) click to toggle source
# File lib/toys/standard_middleware/show_help.rb, line 253
def show_usage(context)
  help_text = get_help_text(context, true)
  str = help_text.usage_string(
    recursive: context[RECURSIVE_SUBTOOLS_KEY],
    include_hidden: context[SHOW_ALL_SUBTOOLS_KEY],
    separate_sources: @separate_sources,
    wrap_width: terminal.width
  )
  terminal.puts(str)
end
terminal() click to toggle source
# File lib/toys/standard_middleware/show_help.rb, line 248
def terminal
  require "toys/utils/terminal"
  @terminal ||= Utils::Terminal.new(output: @stream, styled: @styled_output)
end