Prompt behaviour (prompt v0.6.0) View Source

Prompt provides a complete solution for building interactive command line applications.

It's very flexible and can be used just to provide helpers for taking input from the user and displaying output.

Basic Usage

import Prompt includes utilities for:

  • printing to the screen -> display/1
  • printing tables to the screen -> table/1
  • asking for confirmation -> confirm/1
  • picking from a list of choices -> select/1
  • asking for passwords -> password/1
  • free form text -> text/1

Advanced usage

To build a more advanced terminal application including sub-commands, define a module and use Prompt, otp_app: :your_app then build a keyword list of Prompt.Command that represents your commands and arguments and pass them to process/2.

Doing this will give you the following options out of the box:

  • --version will pull your app version from mix.exs
  • --help will print your @moduledoc for help.

Example

defmodule MyApp.CLI do
  @moduledoc "This will print when a user types `myapp --help` in the commandline"
  use Prompt, otp_app: :my_app

  # the entry point to your app, takes the command line args
  def main(argv), do:
    argv
    |> process(first: MyApp.CLI.FirstCommand)
  end
end

# a command
defmodule MyApp.CLI.FirstCommand do
  @moduledoc "This prints when the help() command is called"
  use Prompt.Command

  @impl true
  def init(argv) do
    argv
    |> OptionParser.parse(
      strict: [help: :boolean, switch1: :boolean, swtich2: :boolean],
      aliases: [h: :help]
    )
    |> parse() #whatever you return from init will be what is passed to `process/1`
  end

  @impl true
  def process(%{help: true}, do: help()
  def process(%{switch1: switch1, switch2: switch2} do
    # do something based on the command and switches
    display("command output")
  end

  defp parse({[help: true], _, _}, do: %{help: true}
  defp parse({opts, _, _}) do
    switch1 = Keyword.get(opts, :switch1, false)
    switch2 = Keyword.get(opts, :switch2, false)
    %{help: false, switch1: switch1, switch2: switch2}
  end
end

The key in the Keyword list that you pass to process/2 is what you expect the user to use as a command, the value is the Prompt.Command module that will process the command.

Once built, your command will be able to take a first subcommand.

>>> my_app first --switch1
command output
...
>>> my_app --version
0.0.1

By default we try to display the @moduledoc when there is an error or when --help is passed in. This is overrideable though by implementing your own version of help/0.

Building for Distribution

There are a couple of different options for building a binary ready for distributing. Which ever approach you decide to use, you'll probably want to keep the docs instead of stripping them. For escripts, you'll add the following to the escript key in mix.exs, if using Bakeware, you'll add it to the releases key.

:strip_beams: [keep: ["Docs"]]

Escript

An escript is the most straightforward approach, but requires that erlang is already installed on the system.

Bakeware

This has been my preferred approach recently. Bakeware uses releases to build a single executable binary that can be run on the system without the dependency on erlang or elixir.

For Bakeware, I also set export RELEASE_DISTRIBUTION=none in rel/env.sh.eex and rel/env.bat.eex - unless you need erlang distribution in your CLI.

For a complete example see Slim

Link to this section Summary

Functions

Display a choice prompt with custom answers. Takes a keyword list of answers in the form of atom to return and string to display.

Display a Y/n prompt.

Writes text to the screen.

Prompt the user for input, but conceal the users typing.

Displays options to the user denoted by numbers.

Print an ASCII table of data. Requires a list of lists as input.

Use this to get an iolist back of the table. Useful when you want an ascii table/1 for other mediums like markdown files.

Display text on the screen and wait for the users text imput.

Callbacks

Prints help to the screen when there is an error, or --help is passed as an argument

Process the command line arguments based on the defined commands

Link to this section Functions

Link to this function

choice(question, custom, opts \\ [])

View Source

Specs

choice(String.t(), keyword(), keyword()) :: atom()

Display a choice prompt with custom answers. Takes a keyword list of answers in the form of atom to return and string to display.

[yes: "y", no: "n"]

will show "(y/n)" and return :yes or :no based on the choice.

Available options:

  • default_answer: the default answer. If default isn't passed, the first is the default.
  • color: A color from the IO.ANSI module

Examples

iex> Prompt.choice("Save password?",
...>   [yes: "y", no: "n", regenerate: "r"],
...>   default_answer: :regenerate
...> )
"Save Password? (y/n/R):" [enter]
iex> :regenerate
Link to this function

confirm(question, opts \\ [])

View Source

Specs

confirm(String.t(), keyword()) :: :yes | :no | :error

Display a Y/n prompt.

Sets 'Y' as the the default answer, allowing the user to just press the enter key. To make 'n' the default answer pass the option default_answer: :no

Available options:

  • color: A color from the IO.ANSI module
  • default_answer: :yes or :no
  • mask_line: should the line be erased after the confirm

Examples

iex> Prompt.confirm("Send the email?")
"Send the email? (Y/n):" Y
iex> :yes

iex> Prompt.confirm("Send the email?", default_answer: :no)
"Send the email? (y/N):" [enter]
iex> :no
Link to this function

display(text, opts \\ [])

View Source

Specs

display(String.t() | [String.t()], keyword()) :: :ok

Writes text to the screen.

Takes a single string argument or a list of strings where each item in the list will be diplayed on a new line.

Available options:

  • color: A color from the IO.ANSI module
  • trim: true | false --- Defaults to false (will put a at the end of the text
  • position: :left | :right --- Print the content starting from the leftmost position or the rightmost position
  • mask_line: true | false --- Prompts the user to press enter and afterwards masks the line just printed
    • the main use case here is a password that you may want to show the user but hide after the user has a chance to write it down, or copy it.

Examples

iex> Prompt.display("Hello from the terminal!")
"Hello from the terminal!"

iex> Prompt.display(["Hello", "from", "the", "terminal"])
"Hello"
"from"
"the"
"terminal"
Link to this function

password(display, opts \\ [])

View Source

Specs

password(String.t(), keyword()) :: String.t()

Prompt the user for input, but conceal the users typing.

Available options:

  • color: A color from the IO.ANSI module

Examples

iex> Prompt.password("Enter your passsword")
"Enter your password:"
iex> "super_secret_passphrase"
Link to this function

select(display, choices, opts \\ [])

View Source

Specs

select(String.t(), [String.t()] | [{String.t(), any()}], keyword()) ::
  any() | :error

Displays options to the user denoted by numbers.

Allows for a list of 2 tuples where the first value is what is displayed and the second value is what is returned to the caller.

Available options:

  • color: A color from the IO.ANSI module
  • multi: true | false (default) - allow for the user to select multiple values?

Examples

iex> Prompt.select("Choose One", ["Choice A", "Choice B"])
"  [1] Choice A"
"  [2] Choice B"
"Choose One [1-2]:" 1
iex> "Choice A"

iex> Prompt.select("Choose One", [{"Choice A", 1000}, {"Choice B", 1001}])
"  [1] Choice A"
"  [2] Choice B"
"Choose One [1-2]:" 2
iex> 1001

iex> Prompt.select("Choose as many as you want", ["Choice A", "Choice B"], multi: true)
"  [1] Choice A"
"  [2] Choice B"
"Choose as many as you want [1-2]:" 1 2
iex> ["Choice A", "Choice B"]
Link to this function

table(matrix, opts \\ [])

View Source

Specs

table([list()], keyword()) :: :ok

Print an ASCII table of data. Requires a list of lists as input.

Available Options

  • header: true | false (default) --- use the first element as a header of the table
  • border: :normal (default) | :markdown --- determine how the border is displayed

Examples

iex> Prompt.table([["Hello", "from", "the", "terminal!"],["this", "is", "another", "row"]])
"
 +-------+------+---------+----------+
 | Hello | from | the     | terminal |
 | this  | is   | another | row      |
 +-------+------+---------+----------+
"

iex> Prompt.table([["One", "Two", "Three", "Four"], ["Hello", "from", "the", "terminal!"],["this", "is", "another", "row"]], header: true)
"
 +-------+------+---------+----------+
 | One   | Two  | Three   | Four     |
 +-------+------+---------+----------+
 | Hello | from | the     | terminal |
 | this  | is   | another | row      |
 +-------+------+---------+----------+
"

iex> Prompt.table([["One", "Two", "Three", "Four"], ["Hello", "from", "the", "terminal!"],["this", "is", "another", "row"]], header: true, border: :markdown)
"
 | One   | Two  | Three   | Four     |
 |-------|------|---------|----------|
 | Hello | from | the     | terminal |
 | this  | is   | another | row      |
"
Link to this function

table_data(matrix, opts \\ [])

View Source

Specs

table_data([list()], keyword()) :: [<<_::0>> | [any()], ...]

Use this to get an iolist back of the table. Useful when you want an ascii table/1 for other mediums like markdown files.

Link to this function

text(display, opts \\ [])

View Source

Specs

text(String.t(), keyword()) :: String.t()

Display text on the screen and wait for the users text imput.

Available options:

  • color: A color from the IO.ANSI module

Examples

iex> Prompt.text("Enter your email")
"Enter your email:" t@t.com
iex> t@t.com

Link to this section Callbacks

Specs

help() :: :ok

Prints help to the screen when there is an error, or --help is passed as an argument

Specs

process(list(), [{String.t(), Process.Command}]) :: non_neg_integer()

Process the command line arguments based on the defined commands