Skip to content

Repository files navigation

command_kit

Build StatusCode ClimateGem Version

Description

A modular Ruby toolkit for building clean, correct, and robust CLI commands as plain-old Ruby classes.

Features

  • Simple - Commands are plain-old ruby classes, with options and arguments declared as attributes. All features are Ruby modules that can be included into command classes.
  • Correct - CommandKit behaves like a standard UNIX command.
    • Safely handles Ctrl^C / SIGINT interrupts and exits with 130.
    • Safely handles broken pipes (aka mycmd | head).
    • Respects common environment variables (ex: TERM=dumb and NO_COLOR).
    • Uses OptionParser for POSIX option parsing.
    • Disables ANSI color when output is redirected to a file or when NO_COLOR is set.
  • Complete - Provides many additional CLI features.
    • OS detection.
    • Terminal size detection.
    • ANSI coloring support.
    • Interactive input.
    • Rich text printing support (fields, lists, and tables).
    • Subcommands (explicit or lazy-loaded) and command aliases.
    • Displaying man pages for --help/help.
    • Using the pager (aka less).
    • XDG directories (aka ~/.config/, ~/.local/share/, ~/.cache/).
    • Exception handling / Bug reporting.
  • Testable - Since commands are plain-old Ruby classes, it's easy to initialize them and call #main or #run.

Anti-Features

  • No additional runtime dependencies.
  • Does not implement it's own option parser.
  • Not named after a comic-book Superhero.

Requirements

Install

$ gem install command_kit

gemspec

gem.add_dependency'command_kit','~> 0.3'

Gemfile

gem'command_kit','~> 0.3'

Examples

lib/foo/cli/my_cmd.rb

require'command_kit'moduleFoomoduleCLIclassMyCmd < CommandKit::Commandusage'[OPTIONS] [-o OUTPUT] FILE'option:count,short: '-c',value: {type: Integer,default: 1},desc: "Number of times"option:output,short: '-o',value: {type: String,usage: 'FILE'},desc: "Optional output file"option:verbose,short: '-v',desc: "Increase verbose level"do@verbose += 1endargument:file,required: true,usage: 'FILE',desc: "Input file"examples['-o path/to/output.txt path/to/input.txt','-v -c 2 -o path/to/output.txt path/to/input.txt',]description'Example command'definitialize(**kwargs)super(**kwargs)@verbose=0enddefrun(file)puts"count=#{options[:count].inspect}"puts"output=#{options[:output].inspect}"puts"file=#{file.inspect}"puts"verbose=#{@verbose.inspect}"endendendend

bin/my_cmd

#!/usr/bin/env ruby
$LOAD_PATH.unshift(File.expand_path('../../lib',__FILE__))require'foo/cli/my_cmd'Foo::CLI::MyCmd.start

--help

Usage: my_cmd [OPTIONS] [-o OUTPUT] FILE
Options:
-c, --count INT Number of times (Default: 1)
-o, --output FILE Optional output file
-v, --verbose Increase verbose level
-h, --help Print help information
Arguments:
FILE Input file
Examples:
my_cmd -o path/to/output.txt path/to/input.txt
my_cmd -v -c 2 -o path/to/output.txt path/to/input.txt
Example command

Testing

RSpec

require'spec_helper'require'stringio'require'foo/cli/my_cmd'describeFoo::CLI::MyCmddolet(:stdin){StringIO.new}let(:stdout){StringIO.new}let(:stderr){StringIO.new}let(:env){ENV}subjectdodescribed_class.new(stdin: stdin,stdout: stdout,stderr: stderr,env: env)end# testing with raw options/argumentsdescribe"#main"docontext"when executed with no arguments"doit"must exit with -1"doexpect(subject.main([])).toeq(-1)endendcontext"when executed with -o OUTPUT"dolet(:file){ ... }let(:output){ ... }before{subject.main(["-o",output,file])}it"must create the output file"do
...
endendendend

Reference

Real-World Examples

Alternatives

Special Thanks

Special thanks to everyone who answered my questions and gave feedback on Twitter.

Copyright

Copyright (c) 2021-2025 Hal Brodigan

See {file:LICENSE.txt} for details.

About

A Ruby toolkit for building complete and robust CLI commands.

Resources

Stars

167 stars

Watchers

1 watching

Forks

Releases

Packages

Used by

Contributors

Languages