philiprehberger-cli_kit

Tests Gem Version Last updated

All-in-one CLI toolkit with argument parsing, prompts, and spinners

Requirements

  • Ruby >= 3.1

Installation

Add to your Gemfile:

gem "philiprehberger-cli_kit"

Or install directly:

gem install philiprehberger-cli_kit

Usage

require "philiprehberger/cli_kit"

result = Philiprehberger::CliKit.parse(ARGV) do
  flag :verbose, short: :v
  option :output, short: :o, default: 'out.txt'
end

result.flags[:verbose]    # => true/false
result.options[:output]   # => 'out.txt' or user-provided value
result.arguments          # => remaining positional args

Subcommands

result = Philiprehberger::CliKit.parse(ARGV) do
  command(:deploy) do
    flag :force, short: :f
    option :env, short: :e
  end
  command(:test) do
    flag :coverage
  end
end

result.command            # => :deploy or :test or nil
result.flags[:force]      # => true/false (within matched command)
result.options[:env]      # => user-provided value

Auto-generated Help

result = Philiprehberger::CliKit.parse(ARGV) do
  flag :verbose, short: :v, desc: 'Enable verbose output'
  option :output, short: :o, desc: 'Output file path'
end

# Passing --help or -h prints formatted usage and exits:
#   Usage: command [options]
#
#   Options:
#     -v, --verbose           Enable verbose output
#     -o, --output VALUE      Output file path

result.help_text          # => formatted help string without printing

Prompts

name = Philiprehberger::CliKit.prompt('What is your name?')
# What is your name? _

confirmed = Philiprehberger::CliKit.confirm('Continue?')
# Continue? [y/n] _

Password Prompt

secret = Philiprehberger::CliKit.password('Enter password:')
# Enter password: _
# Input is read without echoing to the terminal when stdin is a TTY.

Validated Ask

# Without a block, any non-empty answer is accepted.
name = Philiprehberger::CliKit.ask('Name:')

# With a block, the prompt repeats until the block returns a truthy value.
port = Philiprehberger::CliKit.ask('Port:', error: 'Must be a number') do |answer|
  answer.match?(/\A\d+\z/)
end

Repeatable Options

result = Philiprehberger::CliKit.parse(ARGV) do
  option :tag, short: :t, multi: true, desc: 'Add a tag (repeatable)'
end

# Invoked as: mycli --tag ruby --tag cli -t kit
result.options[:tag]   # => ["ruby", "cli", "kit"]
env = Philiprehberger::CliKit.select('Choose env:', %w[dev staging prod])
#   Choose env:
#     1) dev
#     2) staging
#     3) prod
#   Choose: _

env = Philiprehberger::CliKit.select('Choose env:', %w[dev staging prod], default: 'staging')
#   Choose env:
#       1) dev
#     * 2) staging
#       3) prod
#   Choose [2]: _

Multi-Select Menu

tags = Philiprehberger::CliKit.multi_select('Pick tags:', %w[ruby cli dsl testing])
#   Pick tags:
#       1) ruby
#       2) cli
#       3) dsl
#       4) testing
#   Choose (comma-separated): 1,3
# => ["ruby", "dsl"]

tags = Philiprehberger::CliKit.multi_select('Pick tags:', %w[ruby cli dsl], defaults: %w[ruby dsl])
#   Pick tags:
#     * 1) ruby
#       2) cli
#     * 3) dsl
#   Choose (comma-separated): _   # empty answer => defaults

Spinners

data = Philiprehberger::CliKit.spinner('Loading data...') do
  # long-running operation
  fetch_remote_data
end

API

Method Description
.parse(args) { ... } Parse arguments with flag/option/command DSL
.prompt(message) Display prompt and read input
.confirm(message) Display yes/no confirmation
.password(message) Read input without echoing to the terminal
`.ask(message) { \ answer\
.select(message, choices) Present numbered menu and return one selection
.multi_select(message, choices, defaults:) Present numbered menu and return multiple selections
.spinner(message) { ... } Show spinner during block execution
Parser#option(name, multi: true) Collect repeated option values into an array
Parser#flags Hash of boolean flag values
Parser#options Hash of option values (arrays when multi: true)
Parser#arguments Array of positional arguments
Parser#command Matched subcommand name or nil
Parser#help_text Formatted help string
Parser#help_requested? Whether --help or -h was passed

Development

bundle install
bundle exec rspec
bundle exec rubocop

Support

If you find this project useful:

Star the repo

🐛 Report issues

💡 Suggest features

❤️ Sponsor development

🌐 All Open Source Projects

💻 GitHub Profile

🔗 LinkedIn Profile

License

MIT