CLI tool
JEG2/highline avatar
JEG2/highline

HighLine, the Ruby library that turns asks into typed, validated answers

A higher level command-line oriented interface.

1,308 stars136 forksRubyNOASSERTION

At a glance

What is it?
HighLine is a Ruby library for command line input and output that replaces gets and puts with a question object carrying a type, a validator and a default. Its interface is a block of configuration rather than a fluent builder.
Who is it for?
HighLine suits a Ruby script that asks a person questions, since the type conversion, the validation loop and the re-prompting are handled for you and the validation interface is open enough to plug in your own rules. It is the wrong choice if you need a full terminal user interface, because a menu is a numbered list rather than a curses interface, and it is the wrong choice for a graphical or web front end, since the whole library is about a terminal.
Can I use it commercially?
Check first. The repository uses a licence we do not classify automatically, so read its LICENSE file before any commercial use.
Is it still maintained?
Yes. The repository last received commits 53 days ago.
What is it written in?
Mainly Ruby, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 24, 2026, and from our analysis. They are not legal advice.

Editorial analysis

What it replaces, and why that matters

HighLine was designed to ease the tedious tasks of doing console input and output with low-level methods like gets and puts. The claim in the README is that it provides a robust system for requesting data from a user without needing to code all the error checking and validation rules, and without needing to convert the typed Strings into what the program really needs. The instruction is to tell HighLine what you are after and let it do all the work.

That is a small problem with a large amount of incidental work. A prompt that needs an integer, that must be between 0 and 105, that has a default of 0 when the user presses enter, and that re-prompts on bad input is roughly twenty lines of loop-and-rescue per question. HighLine makes that one call:

ruby
require 'highline'

# Basic usage
cli = HighLine.new
answer = cli.ask "What do you think?"
puts "You have answered: #{answer}"

The type argument is the second positional argument, so the type is a class rather than a method, and the block is where the constraints go:

ruby
cli.ask("Age?  ", Integer) { |q| q.in = 0..105 }
cli.ask("Name?  (last, first)  ") { |q| q.validate = /\A\w+, ?\w+\Z/ }

The in constraint is a range and the validate constraint is a regular expression, so the two most common cases need no custom code at all. The response comes back already converted to the Integer you asked for, which means the rest of your program never handles a String that might not be a String.

The library is a gem, published as highline, with the documentation on rubydoc.info, and the README points specifically at the HighLine and HighLine::Question classes as the two worth reading.

The question object is where the behaviour lives

Every ask yields a HighLine::Question object to the block, and that object is the configuration surface. The attributes used in the README are enough to show the shape.

A default value is set with q.default, and a default can be any object at all, which is why the README shows a plain Object.new being used as a default with q.default_hint_show = false to suppress the hint. The hint is a separate attribute because a default of nil is a legitimate default and you do not want the prompt showing a hint for it.

The echo attribute handles password entry, and it has three forms. q.echo = false hides the input entirely, which is the normal password case. q.echo = "x" shows a fixed character instead of what was typed, which is the convention Unix tools use when they want to show that something was typed without revealing it. The default is to echo normally.

Validation has two forms. The validate attribute takes either a regular expression or an object that responds to valid?, and the second form is where the design opens up. The README shows a custom class:

ruby
class ZeroToTwentyFourValidator
  def self.valid?(answer)
    (0..24).include? answer.to_i
  end

  def self.inspect
    "(0..24) rule"
  end
end

cli.ask("What hour of the day is it?:  ", Integer) do |q|
  q.validate = ZeroToTwentyFourValidator
end

The inspect method is not decoration. When a prompt fails and re-asks, the message shown to the user comes from the validator, so a custom validator has to be able to describe its own rule. Without inspect you would get a generic object inspection string in the middle of a user prompt.

The other validation form is any object that provides a valid? method, which is how Dry::Types integrates. The README requires dry-type, includes Dry.Types in a module, and then uses the type directly as the validator:

ruby
require 'dry-type'

module Types
  include Dry.Types
end

cli.ask("Type an integer:", Integer) do |q|
  q.validate = Types::Coercible::Integer
end

The reason this works is that Dry::Types provides a valid? method, so the library does not need to know anything about Dry. The validation contract is one method, and any library that has it can be used.

Type conversion, which is the feature that saves the most typing

Asking for a Date and getting a Date back is the part of HighLine that changes how you write the rest of the program. The README calls this type conversion for answers, and the examples are short:

ruby
cli.ask("Birthday?  ", Date)
cli.ask("Interests?  (comma sep list)  ", lambda { |str| str.split(/,\s*/) })

The second one is the interesting case. The type argument is not restricted to classes, it is anything that can turn a String into a value, and a lambda is exactly that. So a comma separated list becomes an Array of strings with a single argument, and there is no parsing code left in your program.

That generalisation is the pattern to take away. Anything that responds to being called with a String works, which covers Date.parse, Integer, Float, a symbol lookup table, a lambda, or a class of your own with a from-string constructor.

The examples/ directory in the repository is where the range of this becomes concrete. There is asking_for_arrays.rb, which is the array case in more depth, custom_parser_custom_validator.rb, which is the case where you need to change how the input is parsed before validation runs, get_character.rb, limit.rb for constrained ranges, repeat_entry.rb, password.rb, trapping_eof.rb, using_readline.rb, and overwrite.rb, which is the case of asking a yes or no question and requiring the user to type the word no to proceed. The README points at that directory for more examples, and it is the right place to look before assuming the main usage block covers what you need.

Menus, colour and ERb output

The menu API is the other half of the library, and it is a numbered list rather than a full screen interface.

ruby
cli.choose do |menu|
  menu.prompt = "Please choose your favorite programming language?  "
  menu.choice(:ruby) { cli.say("Good choice!") }
  menu.choices(:python, :perl) { cli.say("Not from around here, are you?") }
  menu.default = :ruby
end

The structure is that each choice is a symbol, and the block attached to it runs when that choice is selected. A choice can be single or plural, with choice for one and choices for several sharing a block, and a default can be set so pressing enter takes the highlighted option.

The colour of the index is configurable, and the README shows both the global default and the per-menu override, with named constants as an alternative to the rgb form:

ruby
HighLine::Menu.index_color   = :rgb_77bbff # set default index color

cli.choose do |menu|
  menu.index_color  = :rgb_999999      # override default color of index
  menu.prompt = "Please choose your favorite programming language?  "
  menu.choice(:ruby) { cli.say("Good choice!") }
  menu.choices(:python, :perl) { cli.say("Not from around here, are you?") }
end

The output side has ERb based output combined with HighLine's ANSI colour tools, so a string can carry inline formatting:

ruby
cli.say("This should be <%= color('bold', BOLD) %>!")

There is also a colour scheme example in the examples directory, and a separate ansi_colors.rb. The point of the ERb form is that colour does not need to be a separate call with interpolation, it can live inside a longer message, which is what you want when a line is mostly text with one emphasised word.

One more convenience is worth knowing about. If you want to save some characters, you can inject or import HighLine methods on Kernel with require 'highline/import', after which say is available directly. The README's warning is to avoid name collisions in the top level namespace, which is the usual cost of a DSL injection and the reason the explicit HighLine.new form is the better default in a library.

Installing, building, and where the project stands

Installation is a gem install, or a Gemfile entry and a bundle run:

sh
$ gem install highline
ruby
source "https://rubygems.org"
gem 'highline'
sh
$ bundle

The README notes that you add sudo if you are installing under a POSIX system as root. For local development there are two rake targets, rake package to build the gem from the root of the sources and rake install to build and install it directly.

The Ruby version requirement is stated plainly: HighLine from version >= 3.0.0 requires ruby >= 3.0.0. That is a floor, not a ceiling, and it is the sort of detail that decides whether a library still fits in a project whose Ruby is pinned.

The repository layout is a conventional gem layout, with lib/ for the code, test/ for the tests, a Rakefile, a highline.gemspec, a Gemfile, a.rubocop.yml for style, a.simplecov for coverage, a Changelog.md, an AUTHORS file, and COPYING. The README shows a coverage badge in the header that is fed from a gist rather than from a service, and the tests badge points at a CI workflow. There is also a site/ directory, which is where the project website lives, and a bin/ directory whose test script the contributing section recommends for anyone working in a containerised environment, since the alternative is installing the toolchain locally.

The contributing instructions are worth skimming because they describe the shape of the project. Tests run with rake test, there is a separate rake acceptance target, and pronto run is part of the workflow. The core team is three names, James Edward Gray II as the author, and Gregory Brown and Abinoam P. Marques Jr. as core contributors.

On maintenance, the repository is not archived, the last push was on 2026-09-01, and the project has no GitHub releases, so the Changelog.md in the repository root is the version record. The licence field is not classified by GitHub, and the terms are stated in the LICENSE file.

Editorial conclusion

HighLine suits a Ruby script that asks a person questions, since the type conversion, the validation loop and the re-prompting are handled for you and the validation interface is open enough to plug in your own rules. It is the wrong choice if you need a full terminal user interface, because a menu is a numbered list rather than a curses interface, and it is the wrong choice for a graphical or web front end, since the whole library is about a terminal. Before adopting it, check the Ruby version requirement, since from version 3.0.0 it requires Ruby 3.0.0 or newer, and read the examples/ directory, because the README points there for the range of behaviours that are not obvious from the main usage block.

Frequently asked questions

How do I install HighLine in Ruby?

Run gem install highline, or add gem 'highline' to your Gemfile alongside the rubygems source and run bundle. To build the gem from the sources use rake package, or rake install to build and install it directly.

How do I validate and convert a user's answer in HighLine?

Pass the target type as the second argument to ask and configure the question in the block, for example cli.ask("Age? ", Integer) { |q| q.in = 0..105 }. The answer comes back already converted to the type you asked for.

Can I use my own validation rule with HighLine?

Yes. Set q.validate to any object that responds to valid?, which covers a regular expression, a class of your own, or a Dry::Types type. A custom validator should also implement inspect, because the rule description is what gets shown to the user when the answer is rejected.

How do I read a password with HighLine?

Set q.echo = false to hide the input entirely, or q.echo = "x" to show a fixed character instead of the typed text. The default is to echo what the user types.

What Ruby version does current HighLine require?

The README states that HighLine from version 3.0.0 requires Ruby 3.0.0 or newer.

Official sources

  1. Issues
  2. JEG2/highline on GitHub
  3. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/jeg2-highline.svg)](https://hysenlabs.com/projects/jeg2-highline)

Was this useful?