Skip to content

postmodern/command_kit.rb

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

command_kit

Build Status Code Climate Gem 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'

module Foo
  module CLI
    class MyCmd < CommandKit::Command

      usage '[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 += 1
      end

      argument :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'

      def initialize(**kwargs)
        super(**kwargs)

        @verbose = 0
      end

      def run(file)
        puts "count=#{options[:count].inspect}"
        puts "output=#{options[:output].inspect}"
        puts "file=#{file.inspect}"
        puts "verbose=#{@verbose.inspect}"
      end

    end
  end
end

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'

describe Foo::CLI::MyCmd do
  let(:stdin)  { StringIO.new }
  let(:stdout) { StringIO.new }
  let(:stderr) { StringIO.new }
  let(:env)    { ENV }

  subject do
    described_class.new(
      stdin:   stdin,
      stdout:  stdout,
      stderr:  stderr,
      env:     env
    )
  end

  # testing with raw options/arguments
  describe "#main" do
    context "when executed with no arguments" do
      it "must exit with -1" do
        expect(subject.main([])).to eq(-1)
      end
    end

    context "when executed with -o OUTPUT" do
      let(:file)   { ... }
      let(:output) { ... }

      before { subject.main(["-o", output, file]) }

      it "must create the output file" do
        ...
      end
    end
  end
end

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

License

Stars

Watchers

Forks

Sponsor this project

 

Packages

No packages published

Contributors 3

  •  
  •  
  •  
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy