• Stars
    star
    2,505
  • Rank 18,324 (Top 0.4 %)
  • Language
    Ruby
  • License
    MIT License
  • Created about 12 years ago
  • Updated almost 2 years ago

Reviews 5.0 (1)

over 1 year ago by Marc Anguera

The best framework to build CLI applications in Ruby. It has a "core" package and then each component/feature (colors, prompts, tables, ...) is distributed as a different gem, so you install only what you need.

Repository Details

Toolkit for developing sleek command line apps.
TTY Toolkit logo

Gem Version Actions CI Build status Maintainability Test Coverage Inline docs Gitter

TTY is a toolbox for developing beautiful command line clients in Ruby with a fluid interface for gathering input, querying terminal properties and displaying information.

Motivation

All too often libraries that interact with terminals create their own interface logic that gathers input from users and displays information back. Many times utility files are created that contain methods for reading system or terminal properties. Shouldn't we focus our energy on building the actual client?

Building terminal tools takes time. I believe that modular components put together in a single package with project scaffolding will help people build things faster and produce higher quality results. It is easy to jump start a new project with available scaffolding and mix and match components to create new tooling.

Features

  • Jump-start development of your command line app the Unix way with scaffold provided by teletype.
  • Fully modular, choose out of many components to suit your needs or use any 3rd party ones.
  • All tty components are small packages that do one thing well.
  • Fully tested with major ruby interpreters.

Installation

Add this line to your application's Gemfile to install all components:

gem 'tty'

or install a particular component:

gem 'tty-*'

And then execute:

$ bundle

Or install it yourself as:

$ gem install tty

Contents

1. Overview

TTY provides you with commands and many components to get you onto the path of building awesome terminal applications in next to no time.

To simply jump start a new command line application use teletype executable:

$ teletype new app

Move in to your new app, and then add more commands:

$ cd app
$ teletype add config

Throughout the rest of this guide, I will assume a generated application called app, that you are in the working directory of 'app/', and a newly created bare command config.

2. Bootstrapping

2.1 new command

Running teletype new [app-name] will bootstrap an entire project file structure based on the bundler gem command setup enhanced by additional files and folders related to command application development.

For example, to create a new command line application called app do:

$ teletype new app

The output will contain all the files that have been created during setup:

Creating gem 'app'
    create app/Gemfile
    create app/.gitignore
    create app/lib/app.rb
    create app/lib/app/version.rb
    ...

In turn, the following files and directories will be generated in the app folder familiar to anyone who has created a gem beforehand:

â–¾ app/
├── ▾ exe/
│   └── app
├── ▾ lib/
│   ├── ▾ app/
│   │   ├── ▸ commands/
│   │   ├── ▸ templates/
│   │   ├── cli.rb
│   │   ├── command.rb
│   │   └── version.rb
│   └── app.rb
├── CODE_OF_CONDUCT.md
├── Gemfile
├── LICENSE.txt
├── README.md
├── Rakefile
└── app.gemspec

By convention the file lib/app/cli.rb provides the main entry point to your command line application:

module App
  class CLI < Thor
    # Error raised by this runner
    Error = Class.new(StandardError)

    desc 'version', 'app version'
    def version
      require_relative 'version'
      puts "v#{App::VERSION}"
    end
    map %w(--version -v) => :version
  end
end

This is where all your application commands and subcommands will be defined.

Teletype uses Thor as an option parsing library by directly inheriting from it.

And also by convention the start method is used to parse the command line arguments inside the app executable:

App::CLI.start

Run the new command with --help or -h flag to see all available options:

$ teletype new --help
$ teletype new -h

Execute teletype to see all available commands.

2.1.1 --author, -a flag

The teletype generator can inject name into documentation for you:

$ teletype new app --author 'Piotr Murach'

2.1.2 --ext flag

To specify that teletype should create a binary executable (as exe/GEM_NAME) in the generated project use the --ext flag. This binary will also be included in the GEM_NAME.gemspec manifest. This is disabled by default, to enable do:

$ teletype new app --ext

2.1.3 --license, -l flag

The teletype generator comes prepackaged with most popular open source licenses: agplv3, apache, bsd2, bsd3, gplv2, gplv3, lgplv3, mit, mplv2, custom. By default the mit license is used. To change that do:

$ teletype new app --license bsd3

2.1.4 --test, -t flag

The teletype comes configured to work with rspec and minitest frameworks which are the only two acceptable values. The GEM_NAME.gemspec will be configured and appropriate testing directory setup. By default the RSpec framework is used.

$ teletype new app --test=minitest
$ teletype new app -t=minitest

2.2 add command

Once application has been initialized, you can create additional command by using teletype add [command-name] task:

$ teletype add config
$ teletype add create

This will add create.rb and config.rb commands to the CLI client:

â–¾ app/
├── ▾ commands/
│   ├── config.rb
│   └── create.rb
├── ▸ templates/
│   ├── ▸ config/
│   └── ▸ create/
├── command.rb
├── cli.rb
└── version.rb

Then you will be able to call the new commands like so:

$ app config
$ app create

The commands require you to specify the actual logic in their execute methods.

Please note that command names should be provided as camelCase or snake_case. For example:

$ teletype add addConfigCommand   # => correct
$ teletype add add_config_command # => correct
$ teletype add add-config-command # => incorrect

2.2.1 --args flag

You can specify that teletype should add a command with a variable number of arguments using the --args flag. The --args flag accepts space delimited variable names. To specify required argument use a string name, for an optional argument pass name = nil enclosed in quote marks and any variable number of arguments needs to be preceded by asterisk:

$ teletype add config --args name           # required argument
$ teletype add config --args "name = nil"   # optional argument
$ teletype add config --args *names         # variadic argument

For more in-depth usage see 2.4 Arguments.

2.2.2 --desc flag

Every generated command will have a default description 'Command description...', however whilst generating a command you can and should specify a custom description to provide more context with --desc flag:

$ teletype add config --desc 'Set and get configuration options'

For more in-depth usage see 2.5 Description.

2.2.3 --force flag

If you wish to overwrite currently implemented command use --force flag:

$ teletype add config --force

2.3 Working with Commands

Running

teletype add config

a new command config will be added to commands folder creating the following files structure inside the lib folder:

â–¾ app/
├── ▾ commands/
│   └── config.rb
├── ▾ templates/
│   └── ▸ config/
├── cli.rb
├── command.rb
└── version.rb

The lib/app/cli.rb file will contain generated command entry which handles the case where the user asks for the config command help or invokes the actual command:

module App
  class CLI < Thor
    desc 'config', 'Command description...'
    def config(*)
      if options[:help]
        invoke :help, ['config']
      else
        require_relative 'commands/config'
        App::Commands::Config.new(options).execute
      end
    end
  end
end

And the lib/app/commands/config.rb will allow you to specify all the command logic. In the Config class which by convention matches the command name, the execute method provides a place to implement the command logic:

module App
  module Commands
    class Config < App::Command
      def initialize(options)
        @options = options
      end

      def execute
        # Command logic goes here ...
      end
    end
  end
end

Notice that Config inherits from App::Cmd class which you have full access to. This class is meant to provide all the convenience methods to lay foundation for any command development. It will lazy load many tty components inside helper methods which you have access to by opening up the lib/app/command.rb file.

For example in the lib/app/command.rb file, you have access to prompt helper for gathering user input:

# The interactive prompt
#
# @see http://www.rubydoc.info/gems/tty-prompt
#
# @api public
def prompt(**options)
  require 'tty-prompt'
  TTY::Prompt.new(options)
end

or a command helper for running external commands:

# The external commands runner
#
# @see http://www.rubydoc.info/gems/tty-command
#
# @api public
def command(**options)
  require 'tty-command'
  TTY::Command.new(options)
end

You have full control of the file, so you can use only the tty components that you require. Please bear in mind that all the components are added by default in your app.gemspec which you can change to suite your needs and pick only tty components that fit your case.

2.4 Arguments

A command may accept a variable number of arguments.

For example, if we wish to have a config command that accepts a location of configuration file, then we can run teletype add command passing --args flag:

$ teletype add config --args file

which will include the required file as an argument to the config method:

module App
  class CLI < Thor
    desc 'config FILE', 'Set and get configuration options'
    def config(file)
      ...
    end
  end
end

Similarly, if we want to generate command with two required arguments, we run teletype add command with --args flag that can accept variable names delimited by space character:

$ teletype add set --args name value

will generate the following:

module App
  class CLI < Thor
    desc 'set NAME VALUE', 'Set configuration option'
    def set(name, value)
      ...
    end
  end
end

If we want to have a command that has an optional argument, for example, the file argument is an optional argument in the config command, then you need to enclose --args argument in parentheses:

$ teletype add config --args 'file = nil'

In well behaved command line application, any optional argument in a command will be enclosed in square brackets:

module App
  class CLI < Thor
    desc 'config [FILE]', 'Set and get configuration options'
    def config(file = nil)
      ...
    end
  end
end

If you intend for your command to accept any number of arguments, you need to prefix such argument with an asterisk. For example, if we wish to accept many configuration names:

$ teletype add get --args *names

which will append ... to the argument description:

module App
  class CLI < Thor
    desc 'get NAMES...', 'Get configuration options'
    def get(*names)
      ...
    end
  end
end

You can mix and match all the above styles of arguments definitions:

$ teletype add config --args file *names

2.5 Description

Use the desc method call to describe your command when displayed in terminal. There are two arguments to this method. First, specifies the command name and the actual positional arguments it will accept. The second argument is an actual text description of what the command does.

For example, given the command config generated in add command section, we can add description like so:

module App
  class CLI < Thor
    desc 'config [FILE]', 'Set and get configuration options'
    def config(file = nil)
      ...
    end
  end
end

Running app executable will include the new description:

Commands:
  app config [FILE]  # Set and get configuration options

To provide long form description of your command use long_desc method.

module App
  class CLI < Thor
    desc 'config [FILE]', 'Set and get configuration options'
    long_desc <<-DESC
      You can query/set/replace/unset options with this command.

      The name is an optional key separated by a dot, and the value will be escaped.

      This command will fail with non-zero status upon error.
    DESC
    def config(file = nil)
      ...
    end
  end
end

Running app config --help will produce the following output:

Usage:
  app config

You can query/set/replace/unset options with this command.

The name is an optional key separated by a dot, and the value will be escaped.

This command will fail with non-zero status upon error.

2.6 Options and Flags

Flags and options allow to customize how particular command is invoked and provide additional configuration.

To specify individual flag or option use method_option before the command method. All the flags and options can be accessed inside method body via the options hash.

Available metadata for an option are:

  • :aliases - A list of aliases for this option.
  • :banner — A description of the value if the option accepts one.
  • :default - The default value of this option if it is not provided.
  • :lazy_default — A default that is only passed if the cli option is passed without a value.
  • :desc - The short description of the option, printed out in the usage description.
  • :required — Indicates that an option is required.
  • :type - :string, :hash, :array, :numeric, :boolean
  • :enum — A list of allowed values for this option.

The values for :type option are:

  • :boolean is parsed as --option
  • :string is parsed as --option=VALUE or --option VALUE
  • :numeric is parsed as --option=N or --option N
  • :array is parsed as --option=one two three or --option one two three
  • :hash is parsed as --option=name:string age:integer

For example, you wish to add an option that allows you to add a new line to a configuration file for a given key with a value thus being able to run app config --add name value. To do this, you would need to specify :array type for accepting more than one value and :banner to provide meaningful description of values:

method_option :add, type: :array, banner: "name value", desc: "Adds a new line the config file. "

The above option would be included in the config method like so:

module App
  class CLI < Thor
    desc 'config [<file>]', 'Set and get configuration options'
    method_option :add, type: :array, banner: "name value",
                        desc: "Adds a new line the config file. "
    def config(*)
      ...
    end
  end
end

Running app help config will output new option:

Usage:
  app config [<file>]

  Options:
    [--add=name value]  # Adds a new line the config file.

You can also specify an option as a flag without an associated value. Let us assume you want to be able to open a configuration file in your system editor when running app config --edit or app config -e. This can be achieved by adding the following option:

method_option :edit, type: :boolean, aliases: ['-e'],
                     desc: "Opens an editor to modify the specified config file."

And adding it to the config method:

module App
  class CLI < Thor
    desc 'config [<file>]', 'Set and get configuration options'
    method_option :edit, type: :boolean, aliases: ['-e'],
                         desc: "Opens an editor to modify the specified config file."
    def config(*)
      ...
    end
  end
end

Next, running app help config will produce:

Usage:
  app config [<file>]

Options:
      [--add=name value]     # Adds a new line the config file.
  -e, [--edit], [--no-edit]  # Opens an editor to modify the specified config file.

You can use method_options as a shorthand for specifying multiple options at once.

method_options %w(list -l) => :boolean, :system => :boolean, :local => :boolean

Once all the command options and flags have been setup, you can access them via options hash in command file lib/app/commands/config.rb:

module App
  module Commands
    class Config < App::Command
      def initialize(options)
        @options = options
      end

      def execute
        if options[:edit]
          editor.open('path/to/config/file')
        end
      end
    end
  end
end

2.7 Global Flags

You can specify an option or a flag that is applicable to all commands and subcommands within a given class by using the class_option method. This method takes exactly the same parameters as method_option for an individual command. The options hash in a given command will always include a global level flag information.

For example, if you want a global flag debug that is visible to all commands in your tool then you need to add it to your CLI class like so:

module App
  class CLI < Thor
    class_option :debug, type: :boolean, default: false, desc: 'Run in debug mode'

    ...
  end
end

2.8. Working with Subcommands

If your tool grows in complexity you may want to add more refined behaviour for each individual command, a subcommand is a great choice to accomplish this. For example, git utility and its git remote command have various subcommands add, rename, remove, set-url, prune and so on that themselves accept many options and arguments.

The teletype executable allows you to easily create new subcommands by issuing the same add command that is also used for generating commands. The only difference is that you need to provide a command name together with a subcommand name. For example, let's say we want the config with a set subcommand with a description and two positional arguments name and value:

$ teletype add config set --desc 'Set configuration option' --args name value

This will add set.rb command to the commands/config folder:

â–¾ app/
├── ▾ commands/
│   ├── ▾ config/
│   │   └── set.rb
│   └── config.rb
├── ▾ templates/
│   └── ▾ config/
│       └── ▸ set/
├── cli.rb
├── command.rb
└── version.rb

The lib/app/cli.rb will contain code that registers config namespace with our CLI root application:

module App
  class CLI < Thor
    require_relative 'commands/config'
    register App::Commands::Config, 'config', 'config [SUBCOMMAND]', 'Set configuration option'
  end
end

The lib/app/commands/config.rb will contain code that handles dispatching subcommands to the Config instance:

# frozen_string_literal: true

require 'thor'

module App
  module Commands
    class Config < Thor

      namespace :config

      desc 'set NAME VALUE', 'Set configuration option'
      def set(name, value)
        if options[:help]
          invoke :help, ['set']
        else
          require_relative 'config/set'
          App::Commands::Config::Set.new(name, value, options).execute
        end
      end
    end
  end
end

And finally, the lib/app/commands/config/set.rb will contain the actual set command implementation:

# frozen_string_literal: true

require_relative '../../command'

module App
  module Commands
    class Config
      class Set < App::Command
        def initialize(name, value, options)
          @name = name
          @value = value
          @options = options
        end

        def execute
          # Command logic goes here ...
        end
      end
    end
  end
end

You can now run your command in terminal:

bundle exec app config set debug true

Note that it is not possible to add subcommands to an existing command. Attempting to do so will currently cause teletype to crash. The reason why it is not possible to add subcommands to existing commands is that it is impossible for tty to distinguish between normal arguments to a command, and subcommands for that command. However, you may very well add multiple subcommands one after another.

3. Components

The TTY allows you to mix & match any components you need to get your job done. The command line applications generated with teletype executable references all of the below components.

Component Description API docs
pastel Terminal strings styling with intuitive and clean API. docs
tty-box Draw various frames and boxes in your terminal. docs
tty-color Terminal color capabilities detection. docs
tty-command Execute shell commands with pretty logging and capture stdout, stderr and exit status. docs
tty-config Define, read and write any Ruby app configurations with a penchant for terminal clients. docs
tty-cursor Move terminal cursor around. docs
tty-editor Open a file or text in the user preferred editor. docs
tty-file File manipulation utility methods. docs
tty-font Write text in large stylized characters using a variety of terminal fonts. docs
tty-link Hyperlinks in your terminal. docs
tty-logger A readable and structured logging for the terminal. docs
tty-markdown Convert a markdown document or text into a terminal friendly output. docs
tty-option Parser for command line arguments, keywords and options. docs
tty-pager Terminal output paging in a cross-platform way. docs
tty-pie Draw pie charts in your terminal window. docs
tty-platform Detecting different operating systems. docs
tty-progressbar A flexible progress bars drawing in terminal emulators. docs
tty-prompt A beautiful and powerful interactive command line prompt. docs
tty-reader A set of methods for processing keyboard input in character, line and multiline modes. docs
tty-screen Terminal screen properties detection. docs
tty-spinner A terminal spinner for tasks with non-deterministic time. docs
tty-table A flexible and intuitive table output generator. docs
tty-tree Print directory or structured data in a tree like format. docs
tty-which Platform independent implementation of Unix which command. docs

4. Contributing

You can contribute by posting feature requests, evaluating the APIs or simply by hacking on TTY components:

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the Contributor Covenant code of conduct.

This project uses EditorConfig to maintain consistent tabbing and file formats. Consider installing the plugin for your editor to help maintain proper code formatting.

Copyright

Copyright (c) 2012 Piotr Murach. See LICENSE.txt for further details.

More Repositories

1

tty-prompt

A beautiful and powerful interactive command line prompt
Ruby
1,467
star
2

github

Ruby interface to GitHub API
Ruby
1,151
star
3

finite_machine

A minimal finite state machine with a straightforward syntax.
Ruby
807
star
4

pastel

Terminal output styling with intuitive and clean API.
Ruby
638
star
5

rspec-benchmark

Performance testing matchers for RSpec
Ruby
602
star
6

tty-spinner

A terminal spinner for tasks that have non-deterministic time frame.
Ruby
428
star
7

tty-progressbar

Display a single or multiple progress bars in the terminal.
Ruby
422
star
8

loaf

Manages and displays breadcrumb trails in Rails app - lean & mean.
Ruby
407
star
9

tty-command

Execute shell commands with pretty output logging and capture stdout, stderr and exit status.
Ruby
400
star
10

tty-markdown

Convert a markdown document or text into a terminal friendly output.
Ruby
307
star
11

tty-logger

A readable, structured and beautiful logging for the terminal
Ruby
294
star
12

github_cli

GitHub on your command line. Use your terminal, not the browser.
Ruby
266
star
13

tty-table

A flexible and intuitive table generator
Ruby
190
star
14

tty-box

Draw various frames and boxes in your terminal window
Ruby
183
star
15

awesome-ruby-cli-apps

A curated list of awesome command-line applications in Ruby.
Ruby
169
star
16

rack-policy

Rack middleware for the EU ePrivacy Directive compliance in Ruby Web Apps
Ruby
147
star
17

tty-pie

Draw pie charts in your terminal window
Ruby
140
star
18

necromancer

Conversion from one object type to another with a bit of black magic.
Ruby
135
star
19

strings

A set of useful functions for transforming strings.
Ruby
129
star
20

coinpare

Compare cryptocurrency trading data across multiple exchanges and blockchains in the comfort of your terminal
Ruby
113
star
21

tty-exit

Terminal exit codes.
Ruby
99
star
22

strings-case

Convert strings between different cases.
Ruby
97
star
23

tty-reader

A set of methods for processing keyboard input in character, line and multiline modes.
Ruby
89
star
24

tty-screen

Terminal screen detection - cross platform, major ruby interpreters
Ruby
86
star
25

tty-option

A declarative command-line parser
Ruby
85
star
26

merkle_tree

A merkle tree is a data structure used for efficiently summarizing sets of data, often one-time signatures.
Ruby
82
star
27

verse

[DEPRECATED] Text transformations
Ruby
71
star
28

tty-cursor

Terminal cursor movement and manipulation of cursor properties such as visibility
Ruby
70
star
29

tty-file

File manipulation utility methods
Ruby
67
star
30

supervision

Write distributed systems that are resilient and self-heal.
Ruby
65
star
31

tty-config

A highly customisable application configuration interface for building terminal tools.
Ruby
63
star
32

tty-font

Terminal fonts
Ruby
60
star
33

benchmark-trend

Measure performance trends of Ruby code
Ruby
60
star
34

lex

Lex is an implementation of lex tool in Ruby.
Ruby
56
star
35

tty-tree

Print directory or structured data in a tree like format
Ruby
56
star
36

strings-truncation

Truncate strings with fullwidth characters and ANSI codes.
Ruby
50
star
37

slideck

Present Markdown-powered slide decks in the terminal.
Ruby
44
star
38

tty-pager

Terminal output paging - cross-platform, major ruby interpreters
Ruby
40
star
39

tty-color

Terminal color capabilities detection
Ruby
35
star
40

tty-link

Hyperlinks in your terminal
Ruby
32
star
41

strings-inflection

Convert between singular and plural forms of English nouns
Ruby
31
star
42

tty-platform

Operating system detection
Ruby
29
star
43

tty-sparkline

Sparkline charts for terminal applications.
Ruby
29
star
44

tty-editor

Opens a file or text in the user's preferred editor
Ruby
28
star
45

communist

Library for mocking CLI calls to external APIs
Ruby
25
star
46

splay_tree

A self-balancing binary tree optimised for fast access to frequently used nodes.
Ruby
24
star
47

equatable

Allows ruby objects to implement equality comparison and inspection methods.
Ruby
24
star
48

minehunter

Terminal mine hunting game.
Ruby
23
star
49

rotation.js

Responsive and mobile enabled jQuery plugin to help create rotating content.
JavaScript
22
star
50

strings-ansi

Handle ANSI escape codes in strings
Ruby
20
star
51

benchmark-malloc

Trace memory allocations and collect stats
Ruby
20
star
52

strings-numeral

Express numbers as string numerals
Ruby
20
star
53

tty-which

Cross-platform implementation of Unix `which` command
Ruby
19
star
54

benchmark-perf

Benchmark execution time and iterations per second
Ruby
13
star
55

tty-runner

A command routing tree for terminal applications
Ruby
12
star
56

queen

English language linter to hold your files in high esteem.
Ruby
8
star
57

impact

Ruby backend for Impact.js framework
Ruby
8
star
58

pastel-cli

CLI tool for intuitive terminal output styling
Ruby
7
star
59

dotfiles

Configuration files for Unix tools
Vim Script
7
star
60

tty-markdown-cli

CLI tool for displaying nicely formatted Markdown documents in the terminal
Ruby
6
star
61

static_deploy

Automate deployment of static websites
Ruby
6
star
62

tenpin

Terminal tenpin bowling game
Ruby
4
star
63

tty.github.io

TTY toolkit website.
SCSS
3
star
64

tytus

Helps you manage page titles in your Rails app.
Ruby
3
star
65

peter-murach.github.com

Personal webpage
JavaScript
2
star
66

wc.rb

A Ruby clone of Unix wc utility.
Ruby
2
star
67

exportable

Rails plugin to ease exporting tasks.
Ruby
1
star
68

capistrano-git-stages

Multistage capistrano git tags
Ruby
1
star
69

leek

Cucumber steps and RSpec expectations for command line apps
Ruby
1
star
70

tabster

Ruby
1
star
71

unicorn.github.io

Website for the github_api and github_cli ruby gems.
CSS
1
star
72

tty-color-cli

CLI tool for terminal color capabilities detection
Ruby
1
star
73

finite_machine.github.io

Website for finite_machine Ruby gem
SCSS
1
star
74

strings-wrapping

Wrap strings with fullwidth characters and ANSI codes
Ruby
1
star