Code Monkey home page Code Monkey logo

tty's Introduction

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.

tty's People

Contributors

askl56 avatar bougyman avatar bovender avatar caneroj1 avatar d3mash avatar doudou avatar enderahmetyurt avatar hasanen avatar huyderman avatar janko avatar mfurtak avatar piotrmurach avatar pjezusek avatar rrrene avatar sergeykorochansky avatar slowbro avatar sondnm avatar vaporyhumo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

tty's Issues

teletype new app -> wrong number of arguments (given 1, expected 0)

Describe the problem

Creating a new app fails

Steps to reproduce the problem

teletype new app

Actual behaviour

/Users/roeland/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/tty-command-0.9.0/lib/tty/command.rb:54:in `initialize': wrong number of arguments (given 1, expected 0) (ArgumentError)
	from /Users/roeland/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/tty-0.10.0/lib/tty/cmd.rb:33:in `new'
	from /Users/roeland/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/tty-0.10.0/lib/tty/cmd.rb:33:in `command'
	from /Users/roeland/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/tty-0.10.0/lib/tty/commands/new.rb:46:in `initialize'
	from /Users/roeland/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/tty-0.10.0/lib/tty/cli.rb:128:in `new'
	from /Users/roeland/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/tty-0.10.0/lib/tty/cli.rb:128:in `new'
	from /Users/roeland/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/thor-0.20.3/lib/thor/command.rb:27:in `run'
	from /Users/roeland/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/thor-0.20.3/lib/thor/invocation.rb:126:in `invoke_command'
	from /Users/roeland/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/thor-0.20.3/lib/thor.rb:387:in `dispatch'
	from /Users/roeland/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/thor-0.20.3/lib/thor/base.rb:466:in `start'
	from /Users/roeland/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/tty-0.10.0/exe/teletype:14:in `<top (required)>'
	from /Users/roeland/.rbenv/versions/3.0.0/bin/teletype:23:in `load'
	from /Users/roeland/.rbenv/versions/3.0.0/bin/teletype:23:in `<main>'

Expected behaviour

shouldn't fail

Describe your environment

  • OS version: Darwin macbook.local 20.3.0 Darwin Kernel Version 20.3.0: Thu Jan 21 00:06:51 PST 2021; root:xnu-7195.81.3~1/RELEASE_ARM64_T8101 arm64
  • Ruby version: ruby 3.0.0p0 (2020-12-25 revision 95aff21468) [arm64-darwin20]
  • TTY version: v0.10.0

`teletype new` errors if path contains space

Describe the problem

It seems teletype isn't escaping / quoting the relative path (i.e. app name) for bundler, which then errors due to invalid arguments.

Steps to reproduce the problem

cd ~/
mkdir 'weird dir' && cd 'weird dir'
teletype new app

Actual behaviour

teletype new app returns 1, prints traceback:

ERROR: "bundle gem" was called with arguments ["/Users/tim/weird", "dir/app"]
Usage: "bundle gem NAME [OPTIONS]"
Traceback (most recent call last):
        9: from /Users/tim/.rbenv/versions/2.6.5/bin/teletype:23:in `<main>'
        8: from /Users/tim/.rbenv/versions/2.6.5/bin/teletype:23:in `load'
        7: from /Users/tim/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/tty-0.10.0/exe/teletype:14:in `<top (required)>'
        6: from /Users/tim/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/thor-0.20.3/lib/thor/base.rb:466:in `start'
        5: from /Users/tim/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/thor-0.20.3/lib/thor.rb:387:in `dispatch'
        4: from /Users/tim/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/thor-0.20.3/lib/thor/invocation.rb:126:in `invoke_command'
        3: from /Users/tim/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/thor-0.20.3/lib/thor/command.rb:27:in `run'
        2: from /Users/tim/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/tty-0.10.0/lib/tty/cli.rb:128:in `new'
        1: from /Users/tim/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/tty-0.10.0/lib/tty/commands/new.rb:126:in `execute'
/Users/tim/.rbenv/versions/2.6.5/lib/ruby/gems/2.6.0/gems/tty-command-0.9.0/lib/tty/command.rb:106:in `run': Running `bundle gem /Users/tim/weird dir/app --no-mit --no-exe --coc --no-ext -t rspec` failed with (TTY::Command::ExitError)
  exit status: 15
  stdout: ERROR: "bundle gem" was called with arguments ["/Users/tim/weird", "dir/app"]
Usage: "bundle gem NAME [OPTIONS]"
  stderr: Nothing written

Expected behaviour

For the presence or absence of spaces in the project path to make no noticeable difference to an end-user's experience invoking teletype new.

Describe your environment

I only noticed this because I store some stuff in iCloud Drive, which has this unfortunate location on disk: /Users/tim/Library/Mobile Documents/com~apple~CloudDocs.

  • ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin19]
  • macOS 10.15.1
  • tty (0.10.0)

suggestion: banner

It would be great to support banners. We use this a lot in our scripts.

image

Only require `tty-...` gems that are used in the app

Describe the problem

Perhaps more of a feature suggestion than a problem :) I noticed that when making a new app with teletype, it adds all of the 'tty' gems as dependencies, even though (by default) none of them are used or required:

  spec.add_dependency "tty-box", "~> 0.4.1"
  spec.add_dependency "tty-color", "~> 0.5"
  spec.add_dependency "tty-command", "~> 0.9.0"
  spec.add_dependency "tty-config", "~> 0.3.2"
  spec.add_dependency "tty-cursor", "~> 0.7"
  spec.add_dependency "tty-editor", "~> 0.5"
  spec.add_dependency "tty-file", "~> 0.8.0"
  spec.add_dependency "tty-font", "~> 0.4.0"
  spec.add_dependency "tty-logger", "~> 0.2.0"
  spec.add_dependency "tty-markdown", "~> 0.6.0"
  spec.add_dependency "tty-pager", "~> 0.12"
  spec.add_dependency "tty-pie", "~> 0.3.0"
  spec.add_dependency "tty-platform", "~> 0.2"
  spec.add_dependency "tty-progressbar", "~> 0.17"
  spec.add_dependency "tty-prompt", "~> 0.19"
  spec.add_dependency "tty-screen", "~> 0.7"
  spec.add_dependency "tty-spinner", "~> 0.9"
  spec.add_dependency "tty-table", "~> 0.11.0"
  spec.add_dependency "tty-tree", "~> 0.3"
  spec.add_dependency "tty-which", "~> 0.4"

This seems inefficient; if I install this gem straight out of the box - no commands added, etc - I'd get 20 other gems along with it, even though none of them are used (seemingly).

Steps to reproduce the problem

teletype new test-app
grep 'dependency' test-app/test-app.gemspec

Expected Suggested behaviour

My initial thoughts were:

a) use the 'teletype' command to add a new component. for example, teletype require prompt (???)
b) when running rake build, scrape any requires that reference a tty gem

Option "b" sounds more automated (yay!) but hacky (boo!). Option "a" feels clunky (i.e. why would i need to run teletype instead of just adding a require 'tty/prompt' line..?)... so I'm not sure either suggestion is the best. Opening this more for discussion than anything!

Describe your environment

  • OS version: Arch Linux/kernel 5.8.9
  • Ruby version: 2.6.5
  • TTY version: 0.10.0

Teletype subcommand creation causes nil reference error

Reposted from tty-file, by request.

Thanks, John for reporting!

Does the contacts command already exist when you're trying to create contacts phonelist subcommand? Would it be possible for you to report this issue at https://github.com/piotrmurach/tty since that's where the teletype tool is developed?

I cannot use teletype to create a subcommand of a command.
When I do I get an exception

> teletype add contacts phonelist --force                                                                                                                                    136ms
      create  spec/integration/contacts/phonelist_spec.rb
      create  spec/unit/contacts/phonelist_spec.rb
      create  lib/ddca/commands/contacts/phonelist.rb
      create  lib/ddca/templates/contacts/phonelist/.gitkeep
Traceback (most recent call last):
	10: from /Users/jschank/.asdf/installs/ruby/2.5.1/bin/teletype:23:in `<main>'
	 9: from /Users/jschank/.asdf/installs/ruby/2.5.1/bin/teletype:23:in `load'
	 8: from /Users/jschank/.asdf/installs/ruby/2.5.1/lib/ruby/gems/2.5.0/gems/tty-0.8.1/exe/teletype:14:in `<top (required)>'
	 7: from /Users/jschank/.asdf/installs/ruby/2.5.1/lib/ruby/gems/2.5.0/gems/thor-0.20.0/lib/thor/base.rb:466:in `start'
	 6: from /Users/jschank/.asdf/installs/ruby/2.5.1/lib/ruby/gems/2.5.0/gems/thor-0.20.0/lib/thor.rb:387:in `dispatch'
	 5: from /Users/jschank/.asdf/installs/ruby/2.5.1/lib/ruby/gems/2.5.0/gems/thor-0.20.0/lib/thor/invocation.rb:126:in `invoke_command'
	 4: from /Users/jschank/.asdf/installs/ruby/2.5.1/lib/ruby/gems/2.5.0/gems/thor-0.20.0/lib/thor/command.rb:27:in `run'
	 3: from /Users/jschank/.asdf/installs/ruby/2.5.1/lib/ruby/gems/2.5.0/gems/tty-0.8.1/lib/tty/cli.rb:88:in `add'
	 2: from /Users/jschank/.asdf/installs/ruby/2.5.1/lib/ruby/gems/2.5.0/gems/tty-0.8.1/lib/tty/commands/add.rb:131:in `execute'
	 1: from /Users/jschank/.asdf/installs/ruby/2.5.1/lib/ruby/gems/2.5.0/gems/tty-file-0.6.0/lib/tty/file.rb:512:in `inject_into_file'
/Users/jschank/.asdf/installs/ruby/2.5.1/lib/ruby/gems/2.5.0/gems/tty-file-0.6.0/lib/tty/file.rb:512:in `escape': no implicit conversion of nil into String (TypeError)

It doesn't matter if I try to define parameters or not. I met this problem when I tried a more complex call:

> teletype add contacts phone_list --desc "Color code the phone extension list" --args contacts_file extension_xslx

Ultimately resulted in the same exception, from the same place

fatal: Not a git repository (or any of the parent directories): .git

Not sure if it's properly an error on TTY once it happens on ruby-2.6.0dev, but requiring tty lib prints a git error out, even when -W0 flag is set it makes no difference:

Ruby version:
➜ test ruby --version
ruby 2.6.0dev (2018-07-06 trunk 63870) [x86_64-linux]

Weird git message:
➜ test ruby -W0 -e "require 'tty'; p 'ok'"
fatal: Not a git repository (or any of the parent directories): .git
"ok"

RVM version:
➜ test rvm --version
rvm 1.29.4 (latest) by Michal Papis, Piotr Kuczynski, Wayne E. Seguin [https://rvm.io]

Problems with detecting a keypress

Hello Peter,

First of all, thank you for creating this very nice gem, I really like how it will be this ultimate Terminal gem.

I'm making a console application, and I want to detect a keypress (more specifically, arrow keys). What I want is that when the user presses an arrow key, that something happens.

So I was trying things out, but they don't work as I expected. I tried the following code:

require "tty"

shell = TTY::Shell.new
p shell.ask("").echo(false).read_char

I expected it to return after I type a single character, but it waited for me to press "Enter". I also expected that the input won't be displayed at any point, but it was displayed after I pressed "Enter". Concretely, when I typed foo<Enter>, I got the following output:

foo"f"

So, my input has been displayed + the first character inspected. I'm aware that detecting arrow keys will probably be a bit trickier, because they're two characters (e.g. ^[[B), but I first have to figure out how to read letters without "Enter" needing to be pressed.

I saw on StackOverflow that the raw setting needs to be enabled for "character input":

system "stty raw"
# do some work
system "stty -raw"

tables do not handle colored strings

When printing a table, the ANSI escape sequences factor into the column width detection, so columns with colored fields are significantly wider than they appear to need to be.

I tried patching the length function in TTY::Table::Field to strip color codes before length detection, which corrected the top borders. However, the rows including coloring still wouldn't pad correctly.
screen shot 2015-01-30 at 4 30 54 pm

Is there some other way to do this? If not, I'll keep poking through for the function which pads the fields and submit a pull request if I fix it.

suggestion: tty-console

I have a particular use-case where I'm connecting to an LXD container via the REST api, and websockets, using a windows client - kitchen login if you're familiar with that... much like vagrant - similar to an ssh connection, but more raw (no ssh client providing terminal emulation). The goal being an interactive session where you can inspect your container to see what is 'right' or 'wrong' in order to fine-tune your code and/or your tests. As such, it doesn't need to be 'fancy' or 'fully functional' but it does need to 'just work'.

The IO piping is stable, and I'm now in a place where I'm enabling the user to connect to the container and getting the visuals to render correctly.

On that note, I'm looking at writing a terminal emulator supporting either ANSI and/or VT100, and I wonder if you'd like that as an addition to your TTY ecosystem? It could be extended to support additional terminals in the future.

I've only kind of 'glanced' through the functions you currently provide, and I've already used a couple of your TTY gems to translate windows input and detect screen size. But, is there anything that I've overlooked that could do this rendering work for me?

Or if not, would you be adverse to including a 'tty-console' gem to your ecosystem? I've only just begun learning about console handling, but I'm assuming at this point (pending further research/testing) that a linux client would not need this special handling (that I could just directly pipe I/O from the container to the user's terminal in linux) so this 'might' be a windows specific deal.

Bundler 2 compatibility

Describe the problem

Bundling a project with this gem and bundler 2 yields the following error:

Bundler could not find compatible versions for gem "bundler":
  In Gemfile:
    tty (~> 0.9.1) was resolved to 0.9.1, which depends on
      bundler (< 2.0, ~> 1.16)

  Current Bundler version:
    bundler (2.0.1)
This Gemfile requires a different version of Bundler.
Perhaps you need to update Bundler by running `gem install bundler`?

Could not find gem 'bundler (< 2.0, ~> 1.16)', which is required by gem 'tty (~> 0.9.1)', in any of the sources.

This is because Bundler is pinned to <2.0 here.

Steps to reproduce the problem

  1. Install Bundler 2.0 or greater.
  2. Create a Gemfile with the following:
source 'https://rubygems.org'
gem 'tty', '~> 0.9.1'
  1. Run bundle

Describe your environment

  • Bundler version: 2.0.1
  • OS version: OSX Mojave
  • Ruby version: ruby 2.5.1p57
  • TTY version: 0.9.1

Cannot align individual fields

Hello!

When I take a rows set with a single row like this:

rows = [['swag', 'derp', 'foo', 'bar']]

And render a new table with it, it works just swimmingly.

However, when I amend one of those with the syntax specified in the alignment section of the documentation...

rows = [['swag', 'derp', {:value => 'foo', :align => :right}, 'bar']]

...there is no change reflected in the table. The alignment of the selected cell does not change.

This remains true when attempting to align to the center, as well. Any help would be much appreciated.

Thanks!

teletype new causes 'fatal: not a git repository (or any of the parent directories): .git' which is not really fatal

Describe the problem

Running teletype new project will cause the following error to be emitted under ruby 2.5.1 on MacOS:

fatal: not a git repository (or any of the parent directories): .git

Steps to reproduce the problem

$ teletype new fubar

Cause

There is a call to Gem::Specification.load. This load invokes 'git ls-files' to expand the 'spec.files' value. Despite having a Dir.chdir in the generated spec code it is still in the wrong directory when this runs.

This is the code generated

  # Specify which files should be added to the gem when it is released.
  # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
  spec.files         = Dir.chdir(File.expand_path('..', __FILE__)) do
    `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
  end

Putting a Dir.chdir in the calling code mitigates this issue.

Fix

( I couldn't get the gem package built properly so I will not put in a PR. Instead offer the following edit)

In lib\tty\commands\new.rb

      def add_required_libs_to_gemspec
        Dir.chdir target_path do
   ....
       end
     end

Describe your environment

  • OS version: MacOS 10.14
  • Ruby version: 2.5.1 and 2.3.7
  • TTY version: 0.8.1

Allow streaming of rows for Table

Creating tables where you know the desired column widths (number of characters or percentages) but you don't know the number of rows yet, because you'd like to stream the rows, not wait for all the data until rendering.

Table rendering is not working

I have a simple script as part of a gem... the prompt, commands are working fine but rendering the tables doesn't work at all...

    TTY::Command.new(printer: :quiet).run('clear')
    TTY::Prompt.new.say("Updated at: #{Time.now}")
    table = TTY::Table.new ['header1','header2'], [['a1', 'a2'], ['b1', 'b2']]
    table.render :ascii

this doesn't output only the table to the terminal... but puts table.to_s outputs the following

"+-------+-------+\n|header1|header2|\n+-------+-------+\n|a1     |a2     |\n|b1     |b2     |\n+-------+-------+"

macOS Sierra (10.12.1)
ruby 2.3.2

Error with add config

C:/Ruby26-x64/lib/ruby/gems/2.6.0/gems/tty-0.10.0/lib/tty/commands/add.rb:69:in `read': No such file or directory @ rb_sysopen - li
b/rev2test/cli.rb (Errno::ENOENT)

integration tests don't work ootb

$ teletype new test-cmd
# remove todos from gemspec
$ teletype add config
$ rake
  1) `test-cmd config` command executes `test-cmd help config` command successfully
     Failure/Error: output = `test-cmd help config`

     Errno::ENOENT:
       No such file or directory - test-cmd

"fix" by prefixing runner to use exe/test-cmd :

RSpec.describe "`test-cmd config` command", type: :cli do
  it "executes `test-cmd help config` command successfully" do
    output = `exe/test-cmd help config`

default boolean value fails

I tried to set up a simple yes/no question with a default of 'yes' and get an error when I simply press enter.

Running

TTY::Shell.new.ask 'So, yes or no ?' do
  default 'yes'
end.read_bool

and pressing ENTER on the prompt leads to

[1] pry(main)> TTY::Shell.new.ask 'So, yes or no ?' do
[1] pry(main)*   default 'yes'  
[1] pry(main)* end.read_bool  
So, yes or no ?

Necromancer::ConversionTypeError: '
' could not be converted from `string` into `boolean` 
from /home/doudou/dev/flat_fish/.gems/gems/necromancer-0.3.0/lib/necromancer/converter.rb:48:in `fail_conversion_type'

(Note: the newline between the two quotes just after ConvertionTypeError is not a formatting glitch, it is displayed as-is on the terminal)

It seems that Question#evaluate_response expects 'nil' as 'no answer' but gets a newline character. Weirdly enough, if I do read_string, I get an empty string (no more newline)

Updated Release?

The dependencies were updated recently (thanks!). Is there a new release being pushed to RubyGems anytime soon?

Looking forward to it!

undefined method `header'

In IRB, if I run this example from the documentation:

table = TTY::Table[['a1', 'a2'], ['b1', 'b2']]

I get:

NoMethodError: undefined method `header' for #<Object:0x007ffc110e1568>
  from /Users/gferguson/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/tty-0.1.0/lib/tty/support/equatable.rb:90:in `block (2 levels) in define_compare'
  from /Users/gferguson/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/tty-0.1.0/lib/tty/support/equatable.rb:90:in `each'
  from /Users/gferguson/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/tty-0.1.0/lib/tty/support/equatable.rb:90:in `all?'
  from /Users/gferguson/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/tty-0.1.0/lib/tty/support/equatable.rb:90:in `block in define_compare'
  from /Users/gferguson/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/tty-0.1.0/lib/tty/support/equatable.rb:146:in `=='
  from /Users/gferguson/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/awesome_print-1.2.0/lib/awesome_print/formatter.rb:66:in `awesome_self'
  from /Users/gferguson/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/awesome_print-1.2.0/lib/awesome_print/formatter.rb:28:in `format'
  from /Users/gferguson/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/awesome_print-1.2.0/lib/awesome_print/inspector.rb:137:in `unnested'
  from /Users/gferguson/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/awesome_print-1.2.0/lib/awesome_print/inspector.rb:104:in `awesome'
  from /Users/gferguson/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/awesome_print-1.2.0/lib/awesome_print/core_ext/kernel.rb:10:in `ai'
  from /Users/gferguson/.rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/awesome_print-1.2.0/lib/awesome_print/core_ext/kernel.rb:20:in `ap'
  from /Users/gferguson/.irbrc:33:in `output_value'
  from /Users/gferguson/.rbenv/versions/2.1.3/bin/irb:11:in `<main>'

Warnings with ruby 2.7

Describe the problem

Specs fail on ruby 2.7.0p0, mostly due to deprecation of hash argument being treated as keywords by ruby < 2.7.0 but deprecated in 2.7.0+.

Steps to reproduce the problem

bundle exec rake spec using ruby 2.7.0p0

Actual behaviour

Finished in 10.59 seconds (files took 0.39076 seconds to load)
42 examples, 20 failures, 1 pending

Failed examples:

rspec ./spec/integration/new_spec.rb:271 # teletype new generates C extensions boilerplate
rspec ./spec/integration/new_spec.rb:292 # teletype new generates code of conduct file
rspec ./spec/integration/new_spec.rb:321 # teletype new displays help
rspec ./spec/integration/new_spec.rb:5 # teletype new generates cli application
rspec ./spec/integration/new_namespaced_spec.rb:2 # teletype new generates cli application namespaced
rspec ./spec/integration/add_force_spec.rb:2 # `teletype add --force` command forces adding already existing command
rspec ./spec/integration/add_subcommand_spec.rb:2 # `teletype add` subcommad adds a new subcommand
rspec ./spec/integration/add_subcommand_spec.rb:175 # `teletype add` subcommad adds a new subcommand with minitest
rspec ./spec/integration/add_spec.rb:238 # `teletype add` command adds more than one command to cli file
rspec ./spec/integration/add_spec.rb:2 # `teletype add` command adds a command
rspec ./spec/integration/add_spec.rb:343 # `teletype add` command adds complex command name as camel case
rspec ./spec/integration/add_spec.rb:415 # `teletype add` command adds complex command name as snake case
rspec ./spec/integration/add_spec.rb:122 # `teletype add` command adds a command with minitests
rspec ./spec/integration/add_spec.rb:498 # `teletype add` command displays help
rspec ./spec/integration/add_spec.rb:186 # `teletype add` command adds command in cli without any commands
rspec ./spec/integration/add_namespaced_spec.rb:127 # teletype add adds a subcommand to namespaced application
rspec ./spec/integration/add_namespaced_spec.rb:2 # teletype add adds a command to namespaced application
rspec ./spec/integration/add_desc_args_spec.rb:124 # `teletype add --desc --args` command adds command with variadic number of arguments
rspec ./spec/integration/add_desc_args_spec.rb:250 # `teletype add --desc --args` command adds subcommand with description and custom arguments
rspec ./spec/integration/add_desc_args_spec.rb:4 # `teletype add --desc --args` command adds command with description and custom arguments

Expected behaviour

Specs to pass

Describe your environment

  • OS version: Ubuntu 18.04
  • Ruby version: 2.7.0p0
  • TTY version: master

suggestion: file operations

Perhaps we can add file operations? TTY could add a lot of value. There are some subtleties here that go beyond what FileUtils provides.

For one thing, it can echo the commands if verbose is set to true. Note the if_necessary variants - they only echo if the command is actually run. For example, mkdir_if_necessary("xyz") will only print "mkdir xyz" if that directory doesn't exist.

Also note that user and owner can be specified as strings instead of ids. etc.

Let me know if you are interested.

attr_accessor :verbose

def chmod(file, mode)
def chown(file, user)

def cp(src, dst, mkdir: false, owner: nil, mode: nil)
def ln(src, dst)
def mkdir(dir, owner: nil, mode: nil)
def mv(src, dst, mkdir: false)
def rm(file)

def rm_and_mkdir(dir)

def cp_if_necessary(src, dst, mkdir: false, owner: nil, mode: nil)
def ln_if_necessary(src, dst)
def mkdir_if_necessary(dir, owner: nil, mode: nil)
def rm_if_necessary(file)

NameError: uninitialized constant TT

[1] pry(main)> require 'rubygems'
true
[2] pry(main)> prompt = TTY::Prompt.new
NameError: uninitialized constant TTY

and in test.rb

require 'rubygems'

prompt = TTY::Prompt.new
prompt.ask('What is your name?', default: ENV['USER'
tty.rb:3:in `<main>': uninitialized constant TTY (NameError)

My setup:

[16] pry(main)> $:
[
    [ 0] "/home/uminded/.rvm/gems/ruby-2.2.1@global/gems/executable-hooks-1.3.2/lib",
    [ 1] "/home/uminded/.rvm/gems/ruby-2.2.1@global/extensions/x86_64-linux/2.2.0/executable-hooks-1.3.2",
    [ 2] "/home/uminded/.rvm/gems/ruby-2.2.1@global/gems/bundler-unload-1.0.2/lib",
    [ 3] "/home/uminded/.rvm/gems/ruby-2.2.1@global/gems/rubygems-bundler-1.4.4/lib",
    [ 4] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/bundler-1.11.2/lib",
    [ 5] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/io-console-0.4.5/lib",
    [ 6] "/home/uminded/.rvm/gems/ruby-2.2.1/extensions/x86_64-linux/2.2.0/io-console-0.4.5",
    [ 7] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/coderay-1.1.1/lib",
    [ 8] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/slop-3.6.0/lib",
    [ 9] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/method_source-0.8.2/lib",
    [10] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/pry-0.10.3/lib",
    [11] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/awesome_print-1.6.1/lib",
    [12] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/tty-color-0.3.0/lib",
    [13] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/tty-cursor-0.2.0/lib",
    [14] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/tty-screen-0.5.0/lib",
    [15] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/tty-which-0.1.0/lib",
    [16] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/unicode_utils-1.4.0/lib",
    [17] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/verse-0.4.0/lib",
    [18] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/tty-pager-0.4.0/lib",
    [19] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/tty-platform-0.1.0/lib",
    [20] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/tty-progressbar-0.8.1/lib",
    [21] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/necromancer-0.3.0/lib",
    [22] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/equatable-0.5.0/lib",
    [23] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/pastel-0.6.0/lib",
    [24] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/wisper-1.6.1/lib",
    [25] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/tty-prompt-0.4.0/lib",
    [26] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/tty-spinner-0.1.0/lib",
    [27] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/tty-table-0.5.0/lib",
    [28] "/home/uminded/.rvm/gems/ruby-2.2.1/gems/tty-0.4.0/lib",
    [29] "/home/uminded/.rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby/2.2.0",
    [30] "/home/uminded/.rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby/2.2.0/x86_64-linux",
    [31] "/home/uminded/.rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby",
    [32] "/home/uminded/.rvm/rubies/ruby-2.2.1/lib/ruby/vendor_ruby/2.2.0",
    [33] "/home/uminded/.rvm/rubies/ruby-2.2.1/lib/ruby/vendor_ruby/2.2.0/x86_64-linux",
    [34] "/home/uminded/.rvm/rubies/ruby-2.2.1/lib/ruby/vendor_ruby",
    [35] "/home/uminded/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0",
    [36] "/home/uminded/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/x86_64-linux"
]

In test.rb

/home/uminded/.rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby/2.2.0
/home/uminded/.rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby/2.2.0/x86_64-linux
/home/uminded/.rvm/rubies/ruby-2.2.1/lib/ruby/site_ruby
/home/uminded/.rvm/rubies/ruby-2.2.1/lib/ruby/vendor_ruby/2.2.0
/home/uminded/.rvm/rubies/ruby-2.2.1/lib/ruby/vendor_ruby/2.2.0/x86_64-linux
/home/uminded/.rvm/rubies/ruby-2.2.1/lib/ruby/vendor_ruby
/home/uminded/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0
/home/uminded/.rvm/rubies/ruby-2.2.1/lib/ruby/2.2.0/x86_64-linux

gem env

RubyGems Environment:
  - RUBYGEMS VERSION: 2.4.8
  - RUBY VERSION: 2.2.1 (2015-02-26 patchlevel 85) [x86_64-linux]
  - INSTALLATION DIRECTORY: /home/uminded/.rvm/gems/ruby-2.2.1
  - RUBY EXECUTABLE: /home/uminded/.rvm/rubies/ruby-2.2.1/bin/ruby
  - EXECUTABLE DIRECTORY: /home/uminded/.rvm/gems/ruby-2.2.1/bin
  - SPEC CACHE DIRECTORY: /home/uminded/.gem/specs
  - SYSTEM CONFIGURATION DIRECTORY: /home/uminded/.rvm/rubies/ruby-2.2.1/etc
  - RUBYGEMS PLATFORMS:
    - ruby
    - x86_64-linux
  - GEM PATHS:
     - /home/uminded/.rvm/gems/ruby-2.2.1
     - /home/uminded/.rvm/gems/ruby-2.2.1@global
  - GEM CONFIGURATION:
     - :update_sources => true
     - :verbose => true
     - :backtrace => false
     - :bulk_threshold => 1000
  - REMOTE SOURCES:
     - https://rubygems.org/
  - SHELL PATH:
     - /home/uminded/.rvm/gems/ruby-2.2.1/bin
     - /home/uminded/.rvm/gems/ruby-2.2.1@global/bin
     - /home/uminded/.rvm/rubies/ruby-2.2.1/bin
     - /usr/local/heroku/bin
     - /usr/local/sbin
     - /usr/local/bin
     - /usr/sbin
     - /usr/bin
     - /sbin
     - /bin
     - /usr/games
     - /usr/local/games
     - /home/uminded/stlink
     - /home/uminded/gcc-arm-none-eabi/bin
     - /home/uminded/.rvm/bin

Installing gem doesn't provide binary (0.7.0)

I ran gem install tty to get the teletype binary. It didn't work. I dug into it and found that the exe dir is missing from the gem's installation dir.

I dug further and found that when you download the gem from rubygems.org, tty.gemspec is different from what's here on GitHub. Notable differences:

  • spec.bindir is missing
  • spec.executables = spec.files.grep(%r{^bin/}).map{ |f| File.basename(f) } is used instead, which won't work since this project doesn't have a bin dir

Also, the exe dir is missing entirely when you download the gem from rubygems.org

So...did something go wrong in the gem cutting process that fouled-up tty.gemspec?

tty-file breaks tty-which

ruby: 2.4.0p0 (2016-12-24 revision 57164) [x86_64-linux]
tty: 0.6.0
tty-which: 0.2.1
OS: Linux (Arch)

With 0.5.0, I could use TTY::Which by requiring tty e.g.:

require 'tty'

TTY::Which.which 'ruby' # => "/usr/bin/ruby"

That no longer works in 0.6.0. It appears that unqualified references to File in TTY::Which are now being resolved to TTY::File rather than ::File.

I can work around this by requiring tty-which directly, but this was an unexpected (breaking) change, and I can't see any mention of it in the changelog.

(This might affect other tty- modules. Some of them have qualified ::File references, some unqualified, and some (e.g. tty-color) have a mixture of the two.)

test-0.5.0.rb

gem 'tty', '= 0.5.0'
require 'tty'
puts TTY::Which.which('ruby')

output

$ ruby test-0.5.0.rb
/usr/bin/ruby

test-0.6.0.rb

gem 'tty', '= 0.6.0'
require 'tty'
puts TTY::Which.which('ruby')

output

$ ruby test-0.6.0.rb
~/.gem/ruby/2.4.0/gems/tty-which-0.2.1/lib/tty/which.rb:157:in `file_with_path?': undefined method `expand_path' for TTY::File:Module (NoMethodError)
	from ~/.gem/ruby/2.4.0/gems/tty-which-0.2.1/lib/tty/which.rb:23:in `which'
	from test-0.6.0.rb:3:in `<main>'

Add vim modeline for indentation

I'd like to add the following vim modeline to all .rb files in tty-* gems:

# vim: expandtab ts=2 sw=2

It would be the first line in each file. This would help contributors who use vim greatly, especially if they are on the tab side of the space-tab wars - no more fighting with indentation.

I'd be willing to do pull requests for tty-* repos with these changes; I just wanted to bring it up here first and test the waters.


Semi-related:

I'd also like to argue the following: for a public code project, I believe tabs are a more 'friendly' option. I used to use spaces, myself (just look at my old code - wait... don't do that) - but my friend @mahlonsmith set me straight.

I believe this because most, if not all code editors allow customization of how tabs are displayed. Do you want a tab to be 2 spaces? 4? 150? Easy - in vim, set ts and sw. But if a project uses spaces, it's not easy to expand the 'tabs' if I need a bit more definition. Tabs enable readability when needed can be suited to any one programmer's sensibilities.

My :2cents: - I'd be happy with just the modeline :)

Feature request: add hyperlinks support

Describe the problem

Some terminals support hyperlinks. You can try following code in terminal:

printf '\e]8;;http://example.com\e\\This is a link\e]8;;\e\\\n'

In iTerm it prints underlined This is a link and hovering it with cmd key I'm able to click on it and browser opens http://example.com link.

It would be nice to have a gem to:

suggestion: csv

Another one - TTY could support csv reading & writing. Arrays of hashes/structs/ostructs can be written to csv, and reading a csv returns an array of structs.

def csv_read(path)
def csv_write(path, rows)
def csv_to_s(rows)

Let me know if you are interested.

Created new project, can't run `version` command for it

I created a new app with version from master. After running bin/setup I tried to run following command and got an expection

./exe/project version
/Users/hasanen/.rvm/rubies/ruby-2.4.2/lib/ruby/2.4.0/rubygems/core_ext/kernel_require.rb:55:in `require': cannot load such file -- ../project/cli (LoadError)
        from /Users/hasanen/.rvm/rubies/ruby-2.4.2/lib/ruby/2.4.0/rubygems/core_ext/kernel_require.rb:55:in `require'
        from ./exe/project:5:in `<main>'

On the file exe/project in line 5 is following code:

require 'project/cli'

I changed it to following which solved the problem

require_relative '../lib/project/cli'

Is this correct way to fix this?

tty not working with Ruby 3.0.0

Describe the problem

I am building a CLI for work and wanted to try tty as I have heard great things about it.

As an FYI, I switched my Ruby environment to 2.7.2 and tried again and it worked just fine, so I'm guessing the issue is with the new Ruby version.

Steps to reproduce the problem

teletype new app

Actual behaviour

❯ teletype new app
/Users/kowilson/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/tty-command-0.9.0/lib/tty/command.rb:54:in `initialize': wrong number of arguments (given 1, expected 0) (ArgumentError)
	from /Users/kowilson/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/tty-0.10.0/lib/tty/cmd.rb:33:in `new'
	from /Users/kowilson/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/tty-0.10.0/lib/tty/cmd.rb:33:in `command'
	from /Users/kowilson/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/tty-0.10.0/lib/tty/commands/new.rb:46:in `initialize'
	from /Users/kowilson/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/tty-0.10.0/lib/tty/cli.rb:128:in `new'
	from /Users/kowilson/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/tty-0.10.0/lib/tty/cli.rb:128:in `new'
	from /Users/kowilson/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/thor-0.20.3/lib/thor/command.rb:27:in `run'
	from /Users/kowilson/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/thor-0.20.3/lib/thor/invocation.rb:126:in `invoke_command'
	from /Users/kowilson/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/thor-0.20.3/lib/thor.rb:387:in `dispatch'
	from /Users/kowilson/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/thor-0.20.3/lib/thor/base.rb:466:in `start'
	from /Users/kowilson/.asdf/installs/ruby/3.0.0/lib/ruby/gems/3.0.0/gems/tty-0.10.0/exe/teletype:14:in `<top (required)>'
	from /Users/kowilson/.asdf/installs/ruby/3.0.0/bin/teletype:23:in `load'
	from /Users/kowilson/.asdf/installs/ruby/3.0.0/bin/teletype:23:in `<main>'

Expected behaviour

I expected the gem to generate a new CLI app.

Describe your environment

  • OS version: Mac OS Catalina 10.15.7
  • Ruby version: 3.0
  • TTY version: v0.10.0

Documentation for template usage

I'm currently building a CLI with Teletype, which so far I'm loving - great work on the user experience of getting from zero to a decent CLI tool!

One thing that's not at all clear is how to make use of templates. Every command seems to generate a sub-directory in templates/, which I presume is meant to have a bunch of Erb files dropped in it or something, but there's no documentation I can find on how to actually make use of those templates!

After installing gem, `teletype` command is not found

I use rvm on macos. After installing gem, command teletype is not found. It seems that folder exe is missing from the released version (not sure if that's related to the issue):

tmp  → la ~/.rvm/gems/ruby-2.4.2/gems/tty-0.7.0/
total 104
-rw-r--r--  1 ext-hasanen  956722523   166B Oct  2 11:39 .gitignore
-rw-r--r--  1 ext-hasanen  956722523    52B Oct  2 11:39 .rspec
-rw-r--r--  1 ext-hasanen  956722523   366B Oct  2 11:39 .travis.yml
-rw-r--r--  1 ext-hasanen  956722523    42B Oct  2 11:39 .yardopts
-rw-r--r--  1 ext-hasanen  956722523   3.3K Oct  2 11:39 CHANGELOG.md
-rw-r--r--  1 ext-hasanen  956722523   2.3K Oct  2 11:39 CODE_OF_CONDUCT.md
-rw-r--r--  1 ext-hasanen  956722523   359B Oct  2 11:39 Gemfile
-rw-r--r--  1 ext-hasanen  956722523   1.0K Oct  2 11:39 LICENSE.txt
-rw-r--r--  1 ext-hasanen  956722523   7.3K Oct  2 11:39 README.md
-rw-r--r--  1 ext-hasanen  956722523   141B Oct  2 11:39 Rakefile
-rw-r--r--  1 ext-hasanen  956722523   482B Oct  2 11:39 appveyor.yml
drwxr-xr-x  3 ext-hasanen  956722523   102B Oct  2 11:39 images
drwxr-xr-x  4 ext-hasanen  956722523   136B Oct  2 11:39 lib
drwxr-xr-x  5 ext-hasanen  956722523   170B Oct  2 11:39 spec
drwxr-xr-x  6 ext-hasanen  956722523   204B Oct  2 11:39 tasks
-rw-r--r--  1 ext-hasanen  956722523   1.7K Oct  2 11:39 tty.gemspec

teletype new broken in 0.9.0

Describe the problem

teletype new does not work in the lates version (0.9.0)

Steps to reproduce the problem

teletype new app

Actual behaviour

	 9: from /usr/local/lib/ruby/gems/2.6.0/bin/teletype:23:in `load'
	 8: from /usr/local/lib/ruby/gems/2.6.0/gems/tty-0.9.0/exe/teletype:14:in `<top (required)>'
	 7: from /usr/local/lib/ruby/gems/2.6.0/gems/thor-0.20.3/lib/thor/base.rb:466:in `start'
	 6: from /usr/local/lib/ruby/gems/2.6.0/gems/thor-0.20.3/lib/thor.rb:387:in `dispatch'
	 5: from /usr/local/lib/ruby/gems/2.6.0/gems/thor-0.20.3/lib/thor/invocation.rb:126:in `invoke_command'
	 4: from /usr/local/lib/ruby/gems/2.6.0/gems/thor-0.20.3/lib/thor/command.rb:27:in `run'
	 3: from /usr/local/lib/ruby/gems/2.6.0/gems/tty-0.9.0/lib/tty/cli.rb:128:in `new'
	 2: from /usr/local/lib/ruby/gems/2.6.0/gems/tty-0.9.0/lib/tty/commands/new.rb:146:in `execute'
	 1: from /usr/local/lib/ruby/gems/2.6.0/gems/tty-0.9.0/lib/tty/commands/new.rb:234:in `add_required_libs_to_gemspec'
/usr/local/lib/ruby/gems/2.6.0/gems/tty-0.9.0/lib/tty/plugins.rb:58:in `load_from': undefined method `runtime_dependencies' for nil:NilClass (NoMethodError)

What happened? This could be a description, log output, error raised etc...

Expected behaviour

teletype should scaffold a new app

Describe your environment

  • OS version: macOS 10.14.1
  • Ruby version: 2.6
  • TTY version: 0.9.0

suggestion: run

It would be great if TTY supported running external commands. Example API below. Note that commands will be echoed if verbose is true.

attr_accessor :verbose

# run a command and echo output, raise an exception if it fails
def run(command, args = nil)
# run a command and return output, raise an exception if it fails
def run_capture(command, args = nil)
# run a command and suppress output, raise an exception if it fails
def run_quietly(command, args = nil)

# returns true if the command fails
def run_fails?(command, args = nil)
# returns true if the command succeeds
def run_succeeds?(command, args = nil)

e2e testing doesn't feel good

adding method_option :services, type: :boolean

causes tests (correctly) to fail:

  1) `test-cmd config` command executes `test-cmd help config` command successfully
     Failure/Error: expect(output).to eq(expected_output)

       expected: "Usage:\n  test-cmd config\n\nOptions:\n  -h, [--help], [--no-help]  # Display usage information\n\nCommand description...\n"
            got: "Usage:\n  test-cmd config\n\nOptions:\n  -h, [--help], [--no-help]          # Display usage information\n      [--services], [--no-services]  \n\nCommand description...\n"

and then when I copy/paste the output in

    expected_output = <<-OUT
Usage:
  test-cmd config

Options:
  -h, [--help], [--no-help]          # Display usage information
      [--services], [--no-services]

Command description...
    OUT

and I re-run the tests I get:

     Failure/Error: expect(output).to eq(expected_output)

       expected: "Usage:\n  test-cmd config\n\nOptions:\n  -h, [--help], [--no-help]          # Display usage information\n      [--services], [--no-services]\n\nCommand description...\n"
            got: "Usage:\n  test-cmd config\n\nOptions:\n  -h, [--help], [--no-help]          # Display usage information\n      [--services], [--no-services]  \n\nCommand description...\n"

And I'm giving up because this just feels stupid way to test and adjust whitespaces.

Resizing terminal window causes RuntimeError

Steps

  1. Open iterm2
  2. Run the progressbar example
  3. Attempt to resize the iterm2 window
progressbar.rb:188:in `resize': Cannot resize finished progress bar (RuntimeError)
    from /Users/aeufemio/.rvm/gems/ruby-2.3.0@digixdao/gems/tty-progressbar-0.8.1/lib/tty/progressbar.rb:309:in `block in register_signals'
    from /Users/aeufemio/.rvm/gems/ruby-2.3.0@digixdao/gems/ethereum-0.4.95/lib/ethereum/transaction.rb:23:in `sleep'
    from /Users/aeufemio/.rvm/gems/ruby-2.3.0@digixdao/gems/ethereum-0.4.95/lib/ethereum/transaction.rb:23:in `wait_for_miner'
    from /Users/aeufemio/.rvm/gems/ruby-2.3.0@digixdao/gems/ethereum-0.4.95/lib/ethereum/contract.rb:205:in `block (3 levels) in build'
    from bin/deploy:139:in `<main>'

Error when creating new project with `teletype new PROJECT` from master

In master, I build the gem and installed it

gem build tty.gemspec
gem install tty-0.7.0.gem

And then I tried to create new project

tmp  → teletype new test
Creating gem 'test'...
Code of conduct enabled in config
      create  test/Gemfile
      create  test/lib/test.rb
      create  test/lib/test/version.rb
      create  test/test.gemspec
      create  test/Rakefile
      create  test/README.md
      create  test/bin/console
      create  test/bin/setup
      create  test/.gitignore
      create  test/.travis.yml
      create  test/.rspec
      create  test/spec/spec_helper.rb
      create  test/spec/test_spec.rb
      create  test/CODE_OF_CONDUCT.md
      append  test/README.md
Invalid gemspec in [/Users/hasanen/.rvm/gems/ruby-2.4.2/gems/tty-0.7.0/tty.gemspec]: uninitialized constant TTY::VERSION
/Users/hasanen/.rvm/gems/ruby-2.4.2/gems/tty-0.7.0/lib/tty/plugins.rb:58:in `load_from': undefined method `runtime_dependencies' for nil:NilClass (NoMethodError)
        from /Users/hasanen/.rvm/gems/ruby-2.4.2/gems/tty-0.7.0/lib/tty/commands/new.rb:222:in `add_required_libs_to_gemspec'
        from /Users/hasanen/.rvm/gems/ruby-2.4.2/gems/tty-0.7.0/lib/tty/commands/new.rb:138:in `execute'
        from /Users/hasanen/.rvm/gems/ruby-2.4.2/gems/tty-0.7.0/lib/tty/cli.rb:111:in `new'
        from /Users/hasanen/.rvm/gems/ruby-2.4.2/gems/thor-0.19.4/lib/thor/command.rb:27:in `run'
        from /Users/hasanen/.rvm/gems/ruby-2.4.2/gems/thor-0.19.4/lib/thor/invocation.rb:126:in `invoke_command'
        from /Users/hasanen/.rvm/gems/ruby-2.4.2/gems/thor-0.19.4/lib/thor.rb:369:in `dispatch'
        from /Users/hasanen/.rvm/gems/ruby-2.4.2/gems/thor-0.19.4/lib/thor/base.rb:444:in `start'
        from /Users/hasanen/.rvm/gems/ruby-2.4.2/gems/tty-0.7.0/exe/teletype:13:in `<top (required)>'
        from /Users/hasanen/.rvm/gems/ruby-2.4.2/bin/teletype:23:in `load'
        from /Users/hasanen/.rvm/gems/ruby-2.4.2/bin/teletype:23:in `<main>'
        from /Users/hasanen/.rvm/gems/ruby-2.4.2/bin/ruby_executable_hooks:15:in `eval'
        from /Users/hasanen/.rvm/gems/ruby-2.4.2/bin/ruby_executable_hooks:15:in `<main>'

Using ruby 2.4.2. Let me know if you need more details.

Unable to render table

Hello!

Upon using the following line from the example listed in README.md, under section 1.2:

table.render :ascii, multiline: true

I am greeted with the following outpt:

The system cannot find the path specified.
The system cannot find the path specified.
C:/RailsInstaller/Ruby1.9.3/lib/ruby/gems/1.9.1/gems/tty-0.0.9/lib/tty/table/renderer/basic.rb:77:in `render': undefined method `border_class' for :ascii:Symbol (NoMethodError)
    from C:/RailsInstaller/Ruby1.9.3/lib/ruby/gems/1.9.1/gems/tty-0.0.9/lib/tty/support/delegatable.rb:38:in `render'
    from C:/Users/psgs/Documents/GitHub/btcl/lib/btcl.rb:107:in `<top (required)>'
    from -e:1:in `load'
    from -e:1:in `<main>'

The table I am attempting to render is initialized at the top of the page using the following line of code:

table = TTY::Table.new(header: %w['Exchange', 'Price'])

Would anybody happen to know how to ensure that the table renders correctly?
Thankyou! 🌴

Getting a success indicator in TTY::File.copy_file()

Describe the problem

I noticed that unlike TTY::File.copy_directory(), TTY::File.copy_file() does't return a value on success.
Would be possible that both methods behave similar?

Steps to reproduce the problem

# Need of rescuing the method in order to know about the success 

begin
  TTY::File.copy_file(  ...  )
rescue
  success = false
  ...
else
  success = true
end

Actual behaviour

result = TTY::File.copy_file()
# result == nil in both error and success

Expected behaviour

Equivalent to its 'brother' method TTY::File.copy_directory()

Describe your environment

  • OS version: macOS Big Sur 11.2
  • Ruby version: ruby 2.6.5p114 (2019-10-01 revision 67812) [x86_64-darwin20]
  • TTY version: tty-file-0.10.0

TTY is using old version of gem unicorn-display_width

I gave follows when installed gem tty:

NOTE: Gem.gunzip is deprecated; use Gem::Util.gunzip instead. It will be removed on or after 2018-12-01.
Gem.gunzip called from /home/yart/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/unicode-display_width-1.1.3/lib/unicode/display_width/index.rb:5.

Sample of code:

require 'tty'

cursor = TTY::Cursor

cursor.invisible do
  # some code
end

My environment

  • OS version: Ubuntu 18.04.5 LTS
  • Ruby version: ruby 2.7.1p83 (2020-03-31 revision a0c7c23c9c) [x86_64-linux]
  • TTY version: 0.10.0

Website navigation

Hi there!

This looks like an awesome project. I'm installing 1.8.7 right now to see if your gem is compatible with it (I might be using it to create an install script).

The navigation on the website is a little troublesome. Some of the nav bar links scroll down, some are new pages. It'd be easier if it were all one kind of behavior. I almost didn't click on any after the first one, since it scrolled.

Overwrite executable warning when trying to install gem

#11 # Describe the problem
Overwrite executable warning

Steps to reproduce the problem

gem install tty-reader
# or 
gem install tty-prompt

Actual behaviour

I get this warning in the terminal

wisper's executable "console" conflicts with countries
Overwrite the executable? [yN]

# for gem install tty-prompt
Fetching: necromancer-0.5.1.gem (100%)
Successfully installed necromancer-0.5.1
Fetching: equatable-0.6.1.gem (100%)
Successfully installed equatable-0.6.1
Fetching: tty-color-0.5.0.gem (100%)
Successfully installed tty-color-0.5.0
Fetching: pastel-0.7.3.gem (100%)
Successfully installed pastel-0.7.3
wisper's executable "console" conflicts with countries
Overwrite the executable? [yN]  ERROR:  Error installing tty-prompt:
	"console" from wisper conflicts with installed executable from countries

I tried to find out what whisper even is and only found
https://rubygems.org/gems/whisper with no link to any viewable source code or documentation. So what does this even do?

Expected behaviour

Provide information of why I'm overwriting an executable, and what the dependency is even doing.

Describe your environment

  • OS version: MacOS Mojave 10.14.3 (18D109
  • Ruby version: ruby 2.4.1p111
  • TTY version: none, afraid to install until this is resolved.

Initial project creation; 'teletype' command throws exception until gemspec is edited

Describe the problem

A brief description of the issue/feature.

Steps to reproduce the problem

teletype new test-app
cd test-app
teletype help

Actual behaviour

When using any teletype command, you get a series of exceptions related to metadata:

[katelyn@h test-app]$ teletype help
Traceback (most recent call last):
	6: from /home/katelyn/.rvm/gems/ruby-2.6.5/bin/ruby_executable_hooks:10:in `<main>'
	5: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/executable-hooks-1.6.0/lib/executable-hooks/hooks.rb:49:in `run'
	4: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/executable-hooks-1.6.0/lib/executable-hooks/hooks.rb:49:in `each'
	3: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/executable-hooks-1.6.0/lib/executable-hooks/hooks.rb:50:in `block in run'
	2: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/rubygems-bundler-1.4.5/lib/rubygems_executable_plugin.rb:4:in `block in <top (required)>'
	1: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require'
/home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/rubygems/core_ext/kernel_require.rb:54:in `require': cannot load such file -- rubygems-bundler/noexec (LoadError)
	33: from /home/katelyn/.rvm/gems/ruby-2.6.5/bin/ruby_executable_hooks:10:in `<main>'
	32: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/executable-hooks-1.6.0/lib/executable-hooks/hooks.rb:49:in `run'
	31: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/executable-hooks-1.6.0/lib/executable-hooks/hooks.rb:49:in `each'
	30: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/executable-hooks-1.6.0/lib/executable-hooks/hooks.rb:50:in `block in run'
	29: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/rubygems-bundler-1.4.5/lib/rubygems_executable_plugin.rb:4:in `block in <top (required)>'
	28: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/rubygems/core_ext/kernel_require.rb:34:in `require'
	27: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130:in `rescue in require'
	26: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130:in `require'
	25: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/rubygems-bundler-1.4.5/lib/rubygems-bundler/noexec.rb:131:in `<top (required)>'
	24: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/rubygems-bundler-1.4.5/lib/rubygems-bundler/noexec.rb:124:in `check'
	23: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/rubygems-bundler-1.4.5/lib/rubygems-bundler/noexec.rb:92:in `setup'
	22: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/rubygems-bundler-1.4.5/lib/rubygems-bundler/noexec.rb:51:in `candidate?'
	21: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/definition.rb:203:in `missing_specs'
	20: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/definition.rb:247:in `resolve'
	19: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/definition.rb:744:in `converge_locked_specs'
	18: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/definition.rb:744:in `each'
	17: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/definition.rb:758:in `block in converge_locked_specs'
	16: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/source/path.rb:108:in `specs'
	15: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/source/path.rb:100:in `local_specs'
	14: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/source/path.rb:165:in `load_spec_files'
	13: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/source/path.rb:165:in `each'
	12: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/source/path.rb:171:in `block in load_spec_files'
	11: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/source/path.rb:157:in `validate_spec'
	10: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/rubygems_integration.rb:61:in `validate'
	 9: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/ui/silent.rb:61:in `silence'
	 8: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/rubygems_integration.rb:61:in `block in validate'
	 7: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/rubygems/specification.rb:2654:in `validate'
	 6: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/rubygems/specification_policy.rb:64:in `validate'
	 5: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/rubygems/specification_policy.rb:95:in `validate_metadata'
	 4: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/rubygems/specification_policy.rb:95:in `each'
	 3: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/rubygems/specification_policy.rb:114:in `block in validate_metadata'
	 2: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/rubygems/specification_policy.rb:399:in `error'
	 1: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/2.6.0/delegate.rb:85:in `method_missing'
/home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/2.6.0/delegate.rb:85:in `call': metadata['homepage_uri'] has invalid link: "TODO: Put your gem's website or public repo URL here." (Gem::InvalidSpecificationException)
	24: from /home/katelyn/.rvm/gems/ruby-2.6.5/bin/ruby_executable_hooks:10:in `<main>'
	23: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/executable-hooks-1.6.0/lib/executable-hooks/hooks.rb:49:in `run'
	22: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/executable-hooks-1.6.0/lib/executable-hooks/hooks.rb:49:in `each'
	21: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/executable-hooks-1.6.0/lib/executable-hooks/hooks.rb:50:in `block in run'
	20: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/rubygems-bundler-1.4.5/lib/rubygems_executable_plugin.rb:4:in `block in <top (required)>'
	19: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/rubygems/core_ext/kernel_require.rb:34:in `require'
	18: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130:in `rescue in require'
	17: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/rubygems/core_ext/kernel_require.rb:130:in `require'
	16: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/rubygems-bundler-1.4.5/lib/rubygems-bundler/noexec.rb:131:in `<top (required)>'
	15: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/rubygems-bundler-1.4.5/lib/rubygems-bundler/noexec.rb:124:in `check'
	14: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/rubygems-bundler-1.4.5/lib/rubygems-bundler/noexec.rb:92:in `setup'
	13: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/gems/2.6.0/gems/rubygems-bundler-1.4.5/lib/rubygems-bundler/noexec.rb:51:in `candidate?'
	12: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/definition.rb:203:in `missing_specs'
	11: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/definition.rb:247:in `resolve'
	10: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/definition.rb:744:in `converge_locked_specs'
	 9: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/definition.rb:744:in `each'
	 8: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/definition.rb:758:in `block in converge_locked_specs'
	 7: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/source/path.rb:108:in `specs'
	 6: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/source/path.rb:100:in `local_specs'
	 5: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/source/path.rb:165:in `load_spec_files'
	 4: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/source/path.rb:165:in `each'
	 3: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/source/path.rb:171:in `block in load_spec_files'
	 2: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/source/path.rb:157:in `validate_spec'
	 1: from /home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/rubygems_integration.rb:60:in `validate'
/home/katelyn/.rvm/rubies/ruby-2.6.5/lib/ruby/site_ruby/2.6.0/bundler/rubygems_integration.rb:65:in `rescue in validate': The gemspec at /home/katelyn/code/test-app/test-app.gemspec is not valid. Please fix this gemspec. (Gem::InvalidSpecificationException)
The validation error was 'metadata['homepage_uri'] has invalid link: "TODO: Put your gem's website or public repo URL here."'

Other errors I ran into while stepping through the errors:

The validation error was '"FIXME" or "TODO" is not a description'

The validation error was 'metadata['source_code_uri'] has invalid link: "TODO: Put your gem's public repo URL here."'

The validation error was 'metadata['changelog_uri'] has invalid link: "TODO: Put your gem's CHANGELOG.md URL here."'

The validation error was '"TODO: Put your gem's website or public repo URL here." is not a valid HTTP URI'

Expected behaviour

I'd expect either a pretty message ("First, modify test-app.gemspec!"), or for the "TODO:" lines to be commented out. The issue was solved by commenting out the 'uri' metadata(s), and modifying the summary/description... but it wasn't immediately obvious (especially since I missed the 'validation error' and went straight to 'wait, what's wrong with rubygems-bundler/noexec?').

I think it could be fixed globally by changing the default spec like this:

-  spec.summary       = %q{TODO: Write a short summary, because RubyGems requires one.}
-  spec.description   = %q{TODO: Write a longer description or delete this line.}
-  spec.homepage      = "TODO: Put your gem's website or public repo URL here."
+  spec.summary       = %q{My new teletype app.}
+  spec.description   = %q{My new teletype app.}
+  #spec.homepage      = "TODO: Put your gem's website or public repo URL here."
 
   # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
   # to allow pushing to a single host or delete this section to allow pushing to any host.
   if spec.respond_to?(:metadata)
-    spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
+    #spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
 
-    spec.metadata["homepage_uri"] = spec.homepage
-    spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
-    spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
+    spec.metadata["homepage_uri"] = spec.homepage if spec.homepage
+    #spec.metadata["source_code_uri"] = "TODO: Put your gem's public repo URL here."
+    #spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
   else
     raise "RubyGems 2.0 or newer is required to protect against " \
       "public gem pushes."

Alternatively, if the validations are coming from tty itself, maybe just printing a 'update your gemspec first!' message would be enough. Unsure what would be the best approach.

Describe your environment

  • OS version: Arch Linux, x86_64, kernel 5.8.9
  • Ruby version: 2.6.5 (rvm)
  • TTY version:
tty (0.10.0)
tty-box (0.4.1)
tty-color (0.5.2)
tty-command (0.9.0)
tty-config (0.3.2)
tty-cursor (0.7.1)
tty-editor (0.5.1)
tty-file (0.8.0)
tty-font (0.4.0)
tty-logger (0.2.0)
tty-markdown (0.6.0)
tty-pager (0.13.0)
tty-pie (0.3.0)
tty-platform (0.3.0)
tty-progressbar (0.17.0)
tty-prompt (0.22.0, 0.21.0)
tty-reader (0.8.0, 0.7.0)
tty-screen (0.8.0)
tty-spinner (0.9.3)
tty-table (0.11.0)
tty-tree (0.4.0)
tty-which (0.4.2)

suggestion: odds and ends

Last one, I promise. Here are a few more common helpers that we tend to use in scripts:

# Who is the current user?
def whoami

# Return true if the current user is "root".
def root?

# Return the md5 checksum for the file at +path+.
def md5_file(path)

# Return the md5 checksum for +str+.
def md5_string(str)

# Return a random alphanumeric string of length +len+.
def random_string(len)

I'd be happy to add these too! I might need some guidance on where to place them.

Question: How does options work?

I see that @options get passed for the command, but how do I get anything in there?

For example if I wanted to have following commands:

# using config-command to set value
app config foo=bar

# using config-command to read value
app config foo

# giving a flag for the command
app calculate_something --refresh-cache

Perhaps this is something that is documented somewhere, but I've missed that.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.