module Rake::ToolkitProgram

Tools to easily build CLI programs for UNIX-like systems

In addition to any commands defined, this module provides:

Use .command_tasks to define commands and .run to execute the CLI.

Constants

NAMESPACE
VERSION

Public Class Methods

args() click to toggle source
# File lib/rake/toolkit_program.rb, line 174
def self.args
  @args
end
available_commands(include: :all) click to toggle source
# File lib/rake/toolkit_program.rb, line 88
def self.available_commands(include: :all)
  Rake::Task.tasks.select {|t| is_task_name?(t.name)}.reject do |t|
    case include
    when :all then false
    when :listable then t.comment.to_s.empty?
    else raise ArgumentError, "#{include.inspect} not valid as include:"
    end
  end
end
command_tasks() { || ... } click to toggle source

Access the Rake namespace for CLI tasks/commands

Defining Rake tasks inside the block of this method (with task) defines the tasks such that they are recognized as invocable from the command line (via the first command line argument, or the first element of args passed to .run).

To make commands/tasks defined in this block visible in the help and shell completion, use desc to describe the task.

# File lib/rake/toolkit_program.rb, line 220
def self.command_tasks
  namespace(NAMESPACE) {yield}
end
default_parsed_args(&blk) click to toggle source

Specify a standard type for parsed argument accumulation

If this is called, the block is used to construct the argument accumulator if no accumulator object is explicitly specified when calling Rake::Task(Rake::ToolkitProgram::TaskExt)parse_args. This helps Rake::ToolkitProgram.args be more consistent throughout the client program.

# File lib/rake/toolkit_program.rb, line 187
def self.default_parsed_args(&blk)
  @default_parsed_args_creator = blk
end
each_completion_script_line(static_options: nil, static_flags: nil) { |" " + l| ... } click to toggle source
# File lib/rake/toolkit_program/completion.rb, line 23
    def self.each_completion_script_line(static_options: nil, static_flags: nil)
      options = static_options || %Q{$("$1" --commands)}
      flags = (static_flags.shellescape if static_flags) || \
        ('' if static_options) || \
        %Q{$("$1" --flag-completion "${COMP_WORDS[@]}")}
      
      <<-END_BASH.dedent.each_line {|l| yield("  " + l)}
        COMPREPLY=()
        MY_WORDNUM=1
        if [ "${COMP_CWORD}" = 2 ] && [ "${COMP_WORDS[1]}" = help ]; then
          MY_WORDNUM=2
        elif [ "${COMP_CWORD}" != "1" ]; then
          HELP_FLAG="--help"
          if [ -n "${COMP_WORDS[$COMP_CWORD]}" ] && [ "${HELP_FLAG\#${COMP_WORDS[$COMP_CWORD]}}" = "$HELP_FLAG" ]; then
            # Word being completed is NOT a prefix of --help: don't offer --help
            :
          elif ! { echo " ${COMP_WORDS[*]}" | egrep -q '\\s(--help|-h|--\\s)'; }; then
            COMPREPLY=("--help")
          fi
          DO_COMPGEN=true
          if ! { echo " ${COMP_WORDS[*]}" | egrep -q '\\s(--help|-h|--\\s)'; }; then
            FLAGS_CANDIDATE=#{flags}
            if [ "$(echo "$FLAGS_CANDIDATE" | head -n1)" == '!NOFSCOMP!' ]; then
              DO_COMPGEN=false
              COMPREPLY+=($(echo "$FLAGS_CANDIDATE" | tail -n+2))
            else
              COMPREPLY+=($FLAGS_CANDIDATE)
            fi
          fi
          if $DO_COMPGEN && [ "${COMP_WORDS[$COMP_CWORD]}" != "--" ] && ! { echo " ${COMP_WORDS[*]}" | egrep -q '\\s(--help|-h)'; }; then
            COMPREPLY+=($(compgen -f -d -- "${COMP_WORDS[$COMP_CWORD]}"))
          fi
          return
        fi
        COMPREPLY=($(compgen -W "#{options}" -- "${COMP_WORDS[$MY_WORDNUM]}"))
      END_BASH
    end
find(name, raise_if_missing: false) click to toggle source
# File lib/rake/toolkit_program.rb, line 98
def self.find(name, raise_if_missing: false)
  case 
  when known_command?(name)
    Rake::Task[task_name(name)]
  when raise_if_missing
    raise UnknownName.new(name)
  end
end
help_styling() { |help_styling| ... } click to toggle source

Access the HelpStyling object used for styling generated help

# File lib/rake/toolkit_program.rb, line 69
def self.help_styling
  if block_given?
    yield @help_styling
  end
  @help_styling
end
if_help_request() { || ... } click to toggle source
# File lib/rake/toolkit_program.rb, line 158
def self.if_help_request
  if args[0] == 'help'
    yield
  else
    args.each do |a|
      case a
      when '--'
        break
      when '-h', '--help'
        yield
        break
      end
    end
  end
end
is_task_name?(name) click to toggle source
# File lib/rake/toolkit_program.rb, line 80
def self.is_task_name?(name)
  name.to_s.start_with?(NAMESPACE + ':')
end
known_command?(name) click to toggle source
# File lib/rake/toolkit_program.rb, line 84
def self.known_command?(name)
  !name.nil? && Rake::Task.task_defined?(task_name(name))
end
new_default_parsed_args(&blk) click to toggle source

Construct a parsed argument accumulator

The constructor can be defined via the block of .default_parsed_args

# File lib/rake/toolkit_program.rb, line 196
def self.new_default_parsed_args(&blk)
  (@default_parsed_args_creator || blk).call
end
run(argv=ARGV, on_error: nil) click to toggle source

Run a CLI

Idiomatically, this is usually invoked as:

if __FILE__ == $0
  Rake::ToolkitProgram.run(on_error: :exit_program!)
end

The first element of args (which defaults to ARGV) names the command to execute. Additional arguments are available via .args.

on_error may be anything supporting to_proc (including a Proc or lambda) and accepts one parameter, which is an error object that is guaranteed to have an exit_program! method. Since Symbol#to_proc creates a Proc that sends the target Symbol to the single argument of the created Proc, passing :exit_program! (as in the idiomatic invocation) results in program exit according to the error being handled.

If the error to be handled by on_error does not respond_to? :exit_program!, it will be extended with ProgramExitFromError, giving it default exit_program! behavior of printing the error message on STDERR and exiting with code 1.

When on_error is nil, any errors are allowed to propagate out of run.

# File lib/rake/toolkit_program.rb, line 133
def self.run(argv=ARGV, on_error: nil)
  name, *@args = argv
  raise NoCommand if name.nil?
  if_help_request {name, args[0] = 'help', name}
  specified_task = find(name, raise_if_missing: true)
  if specified_task.kind_of?(ArgParsingTask)
    new_args = specified_task.parsed_arguments
    specified_task.argument_parser.parse(
      *case new_args
      when Hash then [args, {into: new_args}]
      else [args]
      end
    )
    @args = new_args
  end
  specified_task.invoke
rescue StandardError => error
  error.extend(ProgramExitFromError) unless error.respond_to?(:exit_program!)
  
  case
  when on_error then on_error.to_proc
  else method(:raise)
  end.call(error)
end
script_name(placeholder_ok: true) click to toggle source
# File lib/rake/toolkit_program.rb, line 200
def self.script_name(placeholder_ok: true)
  case 
  when $0 != '-' then $0
  when ENV['THIS_SCRIPT'] then ENV['THIS_SCRIPT']
  when placeholder_ok then '<script-name>'
  else raise "Script name unknown"
  end
end
task_name(name) click to toggle source
# File lib/rake/toolkit_program.rb, line 76
def self.task_name(name)
  "#{NAMESPACE}:#{name}"
end
title() click to toggle source
# File lib/rake/toolkit_program.rb, line 61
def self.title
  @title || "#{script_name.capitalize} Toolkit Program"
end
title=(s) click to toggle source
# File lib/rake/toolkit_program.rb, line 57
def self.title=(s)
  @title = s
end