module Rake::ToolkitProgram
Tools to easily build CLI programs for UNIX-like systems
In addition to any commands defined, this module provides:
-
a
--install-completions
command -
a
help
command -
-h
and--help
flags (until--
is encountered)
Use .command_tasks to define commands and .run to execute the CLI.
Constants
- NAMESPACE
- VERSION
Public Class Methods
# File lib/rake/toolkit_program.rb, line 174 def self.args @args end
# 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
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
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
# 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
# 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
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
# 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
# File lib/rake/toolkit_program.rb, line 80 def self.is_task_name?(name) name.to_s.start_with?(NAMESPACE + ':') end
# File lib/rake/toolkit_program.rb, line 84 def self.known_command?(name) !name.nil? && Rake::Task.task_defined?(task_name(name)) end
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 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
# 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
# File lib/rake/toolkit_program.rb, line 76 def self.task_name(name) "#{NAMESPACE}:#{name}" end
# File lib/rake/toolkit_program.rb, line 61 def self.title @title || "#{script_name.capitalize} Toolkit Program" end
# File lib/rake/toolkit_program.rb, line 57 def self.title=(s) @title = s end