项目作者: piotrmurach

项目描述 :
A declarative command-line parser
高级语言: Ruby
项目地址: git://github.com/piotrmurach/tty-option.git
创建时间: 2020-03-21T14:28:58Z
项目社区:https://github.com/piotrmurach/tty-option

开源协议:MIT License

下载



TTY Toolkit logo

TTY::Option

Gem Version
Actions CI
Build status
Maintainability
Coverage Status

Parser for command line arguments, keywords, options and environment variables

Features

  • Parse command line arguments, keywords, flags, options
    and environment variables.
  • Define command line parameters with DSL or keyword arguments.
  • Access all parameter values from hash-like params.
  • Define global parameters with inheritance.
  • Accept command line parameters in any order.
  • Handle complex inputs like lists and maps.
  • Convert inputs to basic and more complex object types.
  • Generate help from parameter definitions.
  • Customise help with usage methods such as header, example and more.
  • Collect parsing errors for a better user experience.

Installation

Add this line to your application’s Gemfile:

  1. gem "tty-option"

And then execute:

  1. $ bundle install

Or install it yourself as:

  1. $ gem install tty-option

Contents

1. Usage

Include the TTY::Option module and define parameters to parse command
line input.

Choose from arguments, keywords,
flags, options and
environment variables.

For example, here is a quick demo of how to create a command that mixes
all parameter types:

  1. class Command
  2. include TTY::Option
  3. usage do
  4. program "dock"
  5. command "run"
  6. desc "Run a command in a new container"
  7. example "Set working directory (-w)",
  8. " $ dock run -w /path/to/dir/ ubuntu pwd"
  9. example <<~EOS
  10. Mount volume
  11. $ dock run -v `pwd`:`pwd` -w `pwd` ubuntu pwd
  12. EOS
  13. end
  14. argument :image do
  15. required
  16. desc "The name of the image to use"
  17. end
  18. argument :command do
  19. optional
  20. desc "The command to run inside the image"
  21. end
  22. keyword :restart do
  23. default "no"
  24. permit %w[no on-failure always unless-stopped]
  25. desc "Restart policy to apply when a container exits"
  26. end
  27. flag :detach do
  28. short "-d"
  29. long "--detach"
  30. desc "Run container in background and print container ID"
  31. end
  32. flag :help do
  33. short "-h"
  34. long "--help"
  35. desc "Print usage"
  36. end
  37. option :name do
  38. required
  39. long "--name string"
  40. desc "Assign a name to the container"
  41. end
  42. option :port do
  43. arity one_or_more
  44. short "-p"
  45. long "--publish list"
  46. convert :list
  47. desc "Publish a container's port(s) to the host"
  48. end
  49. def run
  50. if params[:help]
  51. print help
  52. elsif params.errors.any?
  53. puts params.errors.summary
  54. else
  55. pp params.to_h
  56. end
  57. end
  58. end

Then create a command instance:

  1. cmd = Command.new

And given the following input on the command line:

  1. restart=always -d -p 5000:3000 5001:8080 --name web ubuntu:16.4 bash

Read the command line input (aka ARGV) with parse:

  1. cmd.parse

Or provide an array of inputs:

  1. cmd.parse(%w[restart=always -d -p 5000:3000 5001:8080 --name web ubuntu:16.4 bash])

Finally, run the command to see parsed values:

  1. cmd.run
  2. # =>
  3. # {:help=>false,
  4. # :detach=>true,
  5. # :port=>["5000:3000", "5001:8080"],
  6. # :name=>"web",
  7. # :restart=>"always",
  8. # :image=>"ubuntu:16.4",
  9. # :command=>"bash"}

Use the params to access all parameters:

  1. cmd.params[:name] # => "web"
  2. cmd.params["command"] # => "bash

Given the --help flag on the command line:

  1. cmd.parse(%w[--help])

Use the help method to print help information to the terminal:

  1. print cmd.help

This will result in the following output:

  1. Usage: dock run [OPTIONS] IMAGE [COMMAND] [RESTART=RESTART]
  2. Run a command in a new container
  3. Arguments:
  4. IMAGE The name of the image to use
  5. COMMAND The command to run inside the image
  6. Keywords:
  7. RESTART=RESTART Restart policy to apply when a container exits (permitted:
  8. no, on-failure, always, unless-stopped) (default "no")
  9. Options:
  10. -d, --detach Run container in background and print container ID
  11. -h, --help Print usage
  12. --name string Assign a name to the container
  13. -p, --publish list Publish a container's port(s) to the host
  14. Examples:
  15. Set working directory (-w)
  16. $ dock run -w /path/to/dir/ ubuntu pwd
  17. Mount volume
  18. $ dock run -v `pwd`:`pwd` -w `pwd` ubuntu pwd

Given an invalid command line input:

  1. cmd.parse(%w[--unknown])

Use the errors method to print all errors:

  1. puts params.errors.summary

This will print a summary of all errors:

  1. Errors:
  2. 1) Invalid option '--unknown'
  3. 2) Option '--publish' should appear at least 1 time but appeared 0 times
  4. 3) Option '--name' must be provided
  5. 4) Argument 'image' must be provided

2. API

2.1 argument

Use the argument method to parse positional arguments.

Provide a name as a string or symbol to define an argument. The name will
serve as a default label for the help display and a key to retrieve
a value from the params:

  1. argument :foo

Given the following command line input:

  1. 11 12 13

This would result only in one argument parsed and the remaining ignored:

  1. params[:foo] # => "11"

The argument method accepts a block to define
parameter settings.

For example, use the arity and convert settings
to parse many positional arguments:

  1. argument :foo do
  2. name "foo(int)" # name for help display
  3. arity one_or_more # how many times can appear
  4. convert :int_list # convert input to a list of integers
  5. validate ->(v) { v < 14 } # validation rule
  6. desc "Argument description" # description for help display
  7. end

Parser would collect all values and convert them to integers given
previous input:

  1. params[:foo] # => [11, 12, 13]

The argument method can also accept settings as keyword arguments:

  1. argument :foo,
  2. name: "foo(int)",
  3. arity: "+",
  4. convert: :int_list,
  5. validate: ->(v) { v < 14 },
  6. desc: "Argument description"

2.2 keyword

Use the keyword method to parse keyword arguments.

Provide a name as a string or symbol to define a keyword argument. The name
will serve as a command line input name, a default label for the help
display and a key to retrieve a value from the params:

  1. keyword :foo

Parser will use the parameter name to match the input name on the command
line by default.

Given the following command line input:

  1. foo=11

This would result in:

  1. params[:foo] # => "11"

Note that the parser performs no conversion of the value.

The keyword method accepts a block to define
parameter settings.

For example, use the arity and convert
settings to parse many keyword arguments:

  1. keyword :foo do
  2. required # by default keyword is not required
  3. arity one_or_more # how many times can appear
  4. convert :int_list # convert input to a list of integers
  5. validate ->(v) { v < 14 } # validation rule
  6. desc "Keyword description" # description for help display
  7. end

Given the following command line input:

  1. foo=10,11 foo=12 13

This would result in an array of integers:

  1. params[:foo] # => [10, 11, 12, 13]

The keyword method can also accept settings as keyword arguments:

  1. keyword :foo,
  2. required: true,
  3. arity: :+,
  4. convert: :int_list,
  5. validate: ->(v) { v < 14 },
  6. desc: "Keyword description"

2.3 option

Use the flag or option methods to parse options.

Provide a name as a string or symbol to define an option. The name will
serve as a command line input name, a label for the help display and
a key to retrieve a value from the params:

  1. option :foo

Parser will use the parameter name to generate a long option name by default.

Given the following command line input:

  1. --foo

This would result in:

  1. params[:foo] # => true

The flag and option methods accept a block to define
parameter settings.

For example, to specify a different name for the parsed option,
use the short and long settings:

  1. option :foo do
  2. short "-f" # define a short name
  3. long "--foo" # define a long name
  4. end

Given the following short name on the command line:

  1. -f

This would result in:

  1. params[:foo] # => true

An option can accept an argument. The argument can be either required
or optional. To define a required argument, provide an extra label
in short or long settings. The label can be any string. When
both short and long names are present, only specify an argument
for the long name.

For example, for both short and long names to accept a required
integer argument:

  1. option :foo do
  2. short "-f"
  3. long "--foo int"
  4. # or
  5. long "--foo=int"
  6. end

Given the following command line input:

  1. --foo=11

This would result in:

  1. params[:foo] # => "11"

Note that the parser performs no conversion of the argument.

To define an optional argument, surround it with square brackets.

For example, to accept an optional integer argument:

  1. option :foo do
  2. long "--foo [int]"
  3. end

Use the arity and convert settings to parse
many options given as a list of integers:

  1. option :foo do
  2. required # by default option is not required
  3. arity one_or_more # how many times option can appear
  4. short "-f" # declare a short flag name
  5. long "--foo ints" # declare a long flag with a required argument
  6. convert :int_list # convert input to a list of integers
  7. validate ->(v) { v < 14 } # validation rule
  8. desc "Option description" # description for help display
  9. end

Given the following command line input:

  1. --foo=10,11 -f 12 13

This would result in an array of integers:

  1. params[:foo] # => [10, 11, 12, 13]

The option method can also accept settings as keyword arguments:

  1. option :foo,
  2. required: true,
  3. arity: :+,
  4. short: "-f",
  5. long: "--foo ints",
  6. convert: :int_list,
  7. validate: -> { |v| v < 14 },
  8. desc: "Option description"

There is a convenience flag method to specify a command line option that
accepts no argument:

  1. flag :foo

For example, a typical scenario is to specify the help flag:

  1. flag :help do
  2. short "-h"
  3. long "--help"
  4. desc "Print usage"
  5. end

2.4 environment

Use the environment or env methods to parse environment variables.

Provide a name as a string or symbol to define an environment variable.
The name will serve as a command line input name, a default label for the
help display and a key to retrieve a value from the params:

  1. environment :foo
  2. # or
  3. env :foo

Parser will use the parameter name to match the input name on the command
line by default.

Given the following command line input:

  1. FOO=11

The result would be:

  1. params[:foo] # => "11"

Note that the parser performs no conversion of the value.

The environment method accepts a block to define
parameter settings.

For example, use the name setting to change a default
variable name:

  1. environment :foo do
  2. name "FOO_ENV"
  3. end

Given the following command line input:

  1. FOO_ENV=11

This would result in:

  1. params[:foo] # => "11"

For example, use the arity and convert settings
to parse many environment variables given as a list of integers:

  1. environment :foo do
  2. required # by default environment is not required
  3. arity one_or_more # how many times env var can appear
  4. name "FOO_ENV" # the command line input name
  5. convert :int_list # convert input to a map of integers
  6. validate ->(v) { v < 14 } # validation rule
  7. desc "Environment description" # description for help display
  8. end

Given the following command line input:

  1. FOO_ENV=10,11 FOO_ENV=12 13

This would result in an array of integers:

  1. params[:foo] # => [10, 11, 12, 13]

The environment method can also accept settings as keyword arguments:

  1. environment :foo,
  2. required: true,
  3. arity: :+,
  4. name: "FOO_ENV",
  5. convert: :int_list,
  6. validate: ->(v) { v < 14 },
  7. desc: "Environment description"

2.5 parameter settings

All parameter types support the following settings except for
short and long, which are option specific.

2.5.1 arity

Use the arity setting to describe how many times a given parameter may
appear on the command line.

Every parameter can appear only once by default. In the case of arguments,
the parser will match the first input and ignore the rest. For other
parameter types, any extra parameter occurrence will override previously
parsed input. Setting the arity requirement overrides this behaviour.

For example, to match an argument exactly two times:

  1. argument :foo do
  2. arity 2
  3. end

Given the following command line input:

  1. bar baz

This would result in an array of strings:

  1. params[:foo] # => ["bar", "baz"]

Another example is to match exactly three occurrences of a keyword:

  1. keyword :foo do
  2. arity 3
  3. end

And then given the following on the command line:

  1. foo=1 foo=2 foo=3

This would result in an array of strings:

  1. params[:foo] # => ["1", "2", "3"]

Use :any, :*, -1, any or zero_or_more to specify that parameter
may appear any number of times.

For example, to expect an argument to appear zero or more times:

  1. argument :foo do
  2. arity zero_or_more
  3. end

Use :+ or one_or_more to specify that parameter must appear at least once.

For example, to expect an option with an argument to appear one or more times:

  1. option :foo do
  2. arity one_or_more
  3. short "-f"
  4. long "--foo string"
  5. end

Use at_least to specify the least number of times a parameter can appear:

For example, to expect a keyword to appear at least three times:

  1. keyword :foo do
  2. arity at_least(3)
  3. end

The help method will handle the arity for the usage banner.

For example, given the following argument definition:

  1. argument :foo do
  2. arity one_or_more
  3. end

The usage banner would display:

  1. Usage: foobar FOO [FOO...]

2.5.2 convert

Use the convert setting to transform any parameter argument to another type.

The convert accepts a conversion name as a predefined symbol or class.

For example, to convert an argument to an integer:

  1. argument :foo do
  2. convert :int
  3. # or
  4. convert Integer
  5. end

The supported conversion types are:

  • :bool or :boolean - e.g. yes,1,y,t becomes true,
    no,0,n,f becomes false
  • :date - e.g. 28/03/2020 becomes #<Date: 2020-03-28...>
  • :float - e.g. -1 becomes -1.0
  • :int or :integer - e.g. +1 becomes 1
  • :path or :pathname - e.g. /foo/bar/baz becomes
    #<Pathname:/foo/bar/baz>
  • :regex or :regexp - e.g. foo|bar becomes /foo|bar/
  • :uri - e.g. foo.com becomes #<URI::Generic foo.com>
  • :sym or :symbol - e.g. foo becomes :foo
  • :list or :array - e.g. a,b,c becomes ["a", "b", "c"]
  • :map or :hash - e.g. a:1 b:2 c:3 becomes {a: "1", b: "2", c: "3"}

To convert to an array of a given type, specify plural or append an array
orlist to any base type:

  • :bools, :bool_array or :bool_list - e.g. t,f,t becomes
    [true, false, true]
  • :floats, :float_array or :float_list - e.g. 1,2,3 becomes
    [1.0, 2.0, 3.0]
  • :ints, :int_array or :int_list - e.g. 1,2,3 becomes [1, 2, 3]

Or, use the list_of method and pass the type as a first argument.

To convert to a hash with values of a given type, append a hash or map to
any base type:

  • :bool_hash or :bool_map - e.g a:t b:f c:t becomes
    {a: true, b: false, c: true}
  • :float_hash or :float_map - e.g a:1 b:2 c:3 becomes
    {a: 1.0, b: 2.0, c: 3.0}
  • :int_hash or :int_map - e.g a:1 b:2 c:3 becomes {a: 1, b: 2, c: 3}

Or, use the map_of method and pass the type as a first argument.

For example, given options with a required list and map arguments:

  1. option :foo do
  2. long "--foo list"
  3. convert :bools
  4. # or
  5. convert list_of(:bool)
  6. end
  7. option :bar do
  8. long "--bar map"
  9. convert :int_map
  10. # or
  11. convert map_of(:int)
  12. end

And then parsing the following command line input:

  1. --foo t,f,t --bar a:1 b:2 c:3

This would result in an array of booleans and a hash with integer values:

  1. params[:foo] # => [true, false, true]
  2. params[:bar] # => {:a=>1, :b=>2, :c=>3}

Use a Proc object to define custom conversion.

For example, to convert the command line input to uppercase:

  1. option :foo do
  2. long "--foo string"
  3. convert ->(val) { val.upcase }
  4. end

2.5.3 default

Use the default setting to specify a default value for an optional parameter.
The parser will use default when the command line input isn’t present.

For example, given the following option definition:

  1. option :foo do
  2. long "--foo string"
  3. default "bar"
  4. end

When the option --foo isn’t present on the command line, the params
will have a default value set:

  1. params[:foo] # => "bar"

Or, use a Proc object to specify a default value:

  1. option :foo do
  2. long "--foo string"
  3. default -> { "bar" }
  4. end

A parameter cannot be both required and have a default value. This will raise
ConfigurationError. The parser treats positional arguments as required.
To have a default for a required argument make it optional:

  1. argument :foo do
  2. optional
  3. default "bar"
  4. desc "Argument description"
  5. end

The usage description for a given parameter will display the default value:

  1. Usage: foobar [FOO]
  2. Arguments:
  3. FOO Argument description (default "bar")

2.5.4 description

Use the description or desc setting to provide a summary for
a parameter. The help method uses a parameter
description to generate a usage display.

For example, given an option with a description:

  1. option :foo do
  2. desc "Option description"
  3. end

This will result in the following help display:

  1. Usage: foobar [OPTIONS]
  2. Options:
  3. --foo Option description

2.5.5 hidden

Use the hidden setting to hide a parameter from the help display.

For example, given a standard argument and a hidden one:

  1. argument :foo
  2. argument :bar do
  3. hidden
  4. end

The above will hide the :bar parameter from the usage banner:

  1. Usage: foobar FOO

2.5.6 long

Only flag and option parameters can use
the long setting.

Use the long setting to define a long name for an option. By convention,
a long name uses a double dash followed by many characters.

When you don’t specify a short or long name, the parameter name
will serve as the option’s long name by default.

For example, to define the --foo option:

  1. option :foo

To change the default name to the --fuu option:

  1. option :foo do
  2. long "--fuu"
  3. end

A long option can accept an argument. The argument can be either required
or optional. To define a required argument, separate it from the option
name with a space or an equal sign.

For the :foo option to accept a required integer argument:

  1. option :foo do
  2. long "--foo int"
  3. end

These are all equivalent ways to define a long option with a required
argument:

  1. long "--foo int"
  2. long "--foo=int"

To define an optional argument, surround it with square brackets. Like
the required argument, separate it from the option name with a space
or an equal sign. It is possible to skip the space, but that would
make the option description hard to read.

For the :foo option to accept an optional integer argument:

  1. option :foo do
  2. long "--foo [int]"
  3. end

These are all equivalent ways to define a long option with
an optional argument:

  1. long "--foo [int]"
  2. long "--foo=[int]"
  3. long "--foo[int]"

When specifying short and long option names, only define the argument
for the long name.

For example, to define an option with short and long names that accepts
a required integer argument:

  1. option :foo do
  2. short "-f"
  3. long "--foo int"
  4. end

Note that the parser performs no conversion of the argument. Use the
convert setting to transform the argument type.

2.5.7 name

The parser will use a parameter name to match command line inputs by default.
It will convert underscores in a name into dashes when matching input.

For example, given the :foo_bar keyword definition:

  1. keyword :foo_bar

And the following command line input:

  1. foo-bar=baz

This would result in:

  1. params[:foo_bar] # => "baz"

Use the name setting to change the parameter default input name.

  1. keyword :foo_bar do
  2. name "fum"
  3. end

Given the following command line input:

  1. fum=baz

This would result in:

  1. params[:foo_bar] # => "baz"

Use uppercase characters when changing the input name for
environment variables:

  1. env :foo do
  2. name "FOO_VAR"
  3. end

2.5.8 optional

All parameters are optional apart from positional arguments.

Use the optional setting to mark a parameter as optional.

For example, given a required argument and an optional one:

  1. argument :foo do
  2. desc "Foo argument description"
  3. end
  4. argument :bar do
  5. optional
  6. desc "Bar argument description"
  7. end

And given the following command line input:

  1. baz

This would result in:

  1. params[:foo] # => "baz"
  2. params[:bar] # => nil

The usage banner will display an optional argument surrounded by brackets:

  1. Usage: foobar FOO [BAR]
  2. Arguments:
  3. FOO Foo argument description
  4. BAR Bar argument description

2.5.9 permit

Use the permit setting to restrict input to a set of possible values.

For example, to restrict the :foo option to only "bar" and "baz" strings:

  1. option :foo do
  2. long "--foo string"
  3. permit %w[bar baz]
  4. end

Given the following command line input:

  1. --foo bar

This would result in:

  1. params[:foo] # => "bar"

Given not permitted value qux on the command line:

  1. --foo qux

This would raise a TTY::Option::UnpermittedArgument error and
make the params invalid.

The parser checks permitted values after applying conversion first. Because
of this, permit setting needs its values to be already of the correct type.

For example, given integer conversion, permitted values need to
be integers as well:

  1. option :foo do
  2. long "--foo int"
  3. convert :int
  4. permit [11, 12, 13]
  5. end

Then given not permitted integer:

  1. --foo 14

This would invalidate params and collect the
TTY::Option::UnpermittedArgument error.

The help method displays permitted values in the
parameter description.

For example, given the following option:

  1. option :foo do
  2. short "-f"
  3. long "--foo string"
  4. permit %w[a b c d]
  5. desc "Option description"
  6. end

Then the description for the option would be:

  1. Usage: foobar [OPTIONS]
  2. Options:
  3. -f, --foo string Option description (permitted: a, b, c, d)

2.5.10 required

Parser only requires arguments to be present on the command line by default.
Any other parameters like options, keywords and environment variables
are optional.

Use the required setting to force parameter presence in command line input.

For example, given a required keyword and an optional one:

  1. keyword :foo do
  2. required
  3. desc "Foo keyword description"
  4. end
  5. keyword :bar do
  6. desc "Bar keyword description"
  7. end

And given the following command line input:

  1. foo=baz

This would result in:

  1. params[:foo] # => "baz"
  2. params[:bar] # => nil

Given the following command line input without the foo keyword:

  1. bar=baz

This would raise a TTY::Option::MissingParameter error.

Then printing errors summary would display the following
error description:

  1. Error: keyword 'foo' must be provided

The usage banner displays the required parameters first. Then surrounds any
optional parameters in brackets.

The help display for the above keywords would be:

  1. Usage: foobar FOO=FOO [BAR=BAR]
  2. Keywords:
  3. FOO=FOO Foo keyword description
  4. BAR=BAR Bar keyword description

2.5.11 short

Only flag and option parameters can use
the short setting.

Use the short setting to define a short name for an option. By convention,
a short name uses a single dash followed by a single alphanumeric character.

For example, to define the -f option:

  1. option :foo do
  2. short "-f"
  3. end

A short option can accept an argument. The argument can be either required
or optional. To define a required argument, separate it from the option
name with a space or an equal sign. It is possible to skip the space,
but that would make the option description hard to read.

For the :foo option to accept a required integer argument:

  1. option :foo do
  2. short "-f int"
  3. end

These are all equivalent ways to define a short option with a required
argument:

  1. short "-f int"
  2. short "-f=int"
  3. short "-fint"

To define an optional argument, surround it with square brackets. Like
the required argument, separate it from the option name with a space
or an equal sign. It is possible to skip the space, but that would
make the option description hard to read.

For the :foo option to accept an optional integer argument:

  1. option :foo do
  2. short "-f [int]"
  3. end

These are all equivalent ways to define a short option with
an optional argument:

  1. short "-f [int]"
  2. short "-f=[int]"
  3. short "-f[int]"

When specifying short and long option names, only define the argument
for the long name.

For example, to define an option with short and long names that accepts
a required integer argument:

  1. option :foo do
  2. short "-f"
  3. long "--foo int"
  4. end

Note that the parser performs no conversion of the argument. Use
the convert setting to transform the argument type.

2.5.12 validate

Use the validate setting to ensure that inputs match a validation rule.
The rule can be a string, a regular expression or a Proc object.

For example, to ensure the --foo option only accepts digits:

  1. option :foo do
  2. long "--foo int"
  3. validate "\d+"
  4. end

Given the following command line input:

  1. --foo bar

This would raise a TTY::Option::InvalidArgument error that would
make params invalid.

Then printing errors summary would output:

  1. Error: value of `bar` fails validation for '--foo' option

To define a validation rule as a Proc object that accepts
an argument to check:

  1. keyword :foo do
  2. convert :int
  3. validate ->(val) { val < 12 }
  4. end

The parser validates a value after applying conversion first. Because of
this, the value inside a validation rule is already of the correct type.

Given the following command line input:

  1. foo=13

This would raise a TTY::Option::InvalidArgument error and make
params invalid.

Then using the errors summary would print the following error:

  1. Error: value of `13` fails validation for 'foo' keyword

2.6 parse

Use the parse method to match command line inputs against defined parameters.

The parse method reads the input from the command line (aka ARGV) and
the environment variables (aka ENV) by default. It also accepts inputs
as an argument. This is useful when testing commands.

For example, given the following parameter definitions:

  1. argument :foo
  2. flag :bar
  3. keyword :baz
  4. env :qux

Then parsing the command line inputs:

  1. parse(%w[12 --bar baz=a QUX=b])

This would result in:

  1. params[:foo] # => "12"
  2. params[:bar] # => true
  3. params[:baz] # => "a"
  4. params[:qux] # => "b"

The parser doesn’t force any order for the parameters except for arguments.

For example, reordering inputs for the previous parameter definitions:

  1. parse(%w[12 QUX=b --bar baz=a])

This would result in the same values:

  1. params[:foo] # => "12"
  2. params[:bar] # => true
  3. params[:baz] # => "a"
  4. params[:qux] # => "b"

The parser handles compact shorthand options that start with
a single dash. These must be boolean options except for
the last one that can accept an argument.

For example, passing three flags and an option with an argument to parse:

  1. parse(%w[-f -b -q -s 12])

This is equivalent to parsing:

  1. parse(%w[-fbqs 12])

Parameter parsing stops at the -- terminator. The parser collects leftover
inputs and makes them accessible with the remaining method.

For example, given extra input after the terminator:

  1. parse(%w[12 baz=a QUX=b -- --fum])

This would result in:

  1. params[:foo] # => 12
  2. params[:bar] # => false
  3. params[:baz] # => "a"
  4. params[:qux] # => "b"
  5. params.remaining # => ["--fum"]

2.6.1 :raise_on_parse_error

The parse method doesn’t raise any errors by default. Why? Displaying
error backtraces in the terminal output may not be helpful for users.
Instead, the parser collects any errors and exposes them through the
errors method.

Use the :raise_on_parse_error keyword set to true to raise parsing errors:

  1. parse(raise_on_parse_error: true)

Parsing errors inherit from TTY::Option::ParseError.

For example, to catch parsing errors:

  1. begin
  2. parse(raise_on_parse_error: true)
  3. rescue TTY::Option::ParseError => err
  4. ...
  5. end

2.6.2 :check_invalid_params

Users can provide any input, including parameters the parser
didn’t expect and define.

When the parser finds an unknown input on the command line, it raises
a TTY::Option::InvalidParameter error and adds it to the
errors array.

Use the :check_invalid_params keyword set to false to ignore unknown
inputs during parsing:

  1. parse(check_invalid_params: false)

This way, the parser will collect all the unrecognised inputs into the
remaining array.

2.7 params

All defined parameters are accessible from the params object.

The params object behaves like a hash with indifferent access. It doesn’t
differentiate between arguments, keywords, options or environment variables.
Because of that, each parameter needs to have a unique name.

For example, given a command with all parameter types:

  1. class Command
  2. include TTY::Option
  3. argument :foo
  4. keyword :bar
  5. option :baz do
  6. long "--baz string"
  7. end
  8. env :qux
  9. def run
  10. print params[:foo]
  11. print params["bar"]
  12. print params["baz"]
  13. print params[:qux]
  14. end
  15. end

And the following command line input:

  1. a bar=b --baz c QUX=d

Then instantiating the command:

  1. cmd = Command.new

And parsing command line input:

  1. cmd.parse

And running the command:

  1. cmd.run

This would result in the following output:

  1. abcd

2.7.1 errors

The parse method only raises configuration errors. The parsing errors are
not raised by default. Instead, the errors method on the params object
gives access to any parsing error.

  1. params.errors # => TTY::Option::AggregateErrors

The errors method returns an TTY::Option::AggregateErrors object that
is an Enumerable.

For example, to iterate over all the errors:

  1. params.errors.each do |error|
  2. ...
  3. end

The TTY::Option::AggregateErrors object has the following
convenience methods:

  • messages - an array of all error messages
  • summary - a string of formatted error messages ready to display
    in the terminal

For example, given an argument that needs to appear at least two times in
the command line input:

  1. argument :foo do
  2. arity at_least(2)
  3. end

And parsing only one argument from the command line input:

  1. parse(%w[12])

Then printing errors summary:

  1. puts params.errors.summary

This would print the following error message:

  1. Error: argument 'foo' should appear at least 2 times but appeared 1 time

Adding integer conversion to the previous example:

  1. argument :foo do
  2. arity at_least(2)
  3. convert :int
  4. end

And given only one invalid argument to parse:

  1. parse(%w[zzz])

The summary would be:

  1. Errors:
  2. 1) Argument 'foo' should appear at least 2 times but appeared 1 time
  3. 2) Cannot convert value of `zzz` into 'int' type for 'foo' argument

Use the :raise_on_parse_error keyword to raise
parsing errors on invalid input.

Consider using the tty-exit gem
for more expressive exit code reporting.

For example, the TTY::Exit module provides the exit_with method:

  1. class Command
  2. include TTY::Exit
  3. include TTY::Option
  4. def run
  5. if params.errors.any?
  6. exit_with(:usage_error, params.errors.summary)
  7. end
  8. ...
  9. end
  10. end

2.7.2 remaining

When the parser finds an unknown input on the command line, it raises
a TTY::Option::InvalidParameter error and adds it to the
errors array.

Use the :check_invalid_params keyword
set to false to ignore unknown inputs during parsing:

  1. parse(check_invalid_params: false)

This way, the parser will collect all the unrecognised inputs
into an array. The remaining method on the params gives access
to all invalid inputs.

For example, given an unknown option to parse:

  1. parse(%w[--unknown])

Then inspecting the remaining inputs:

  1. params.remaining # => ["--unknown"]

The parser leaves any inputs after the -- terminator alone. Instead,
it collects them into the remaining array. This is useful when passing
inputs over to other command line applications.

2.7.3 valid?

Use the valid? method to check that command line inputs meet all
validation rules.

The valid? method is available on the params object:

  1. params.valid? # => true

Use the errors method to check for any errors and not only
validation rules:

  1. params.errors.any?

2.8 usage

The usage method accepts a block that configures the
help display.

2.8.1 header

Use the header setting to display information above the banner.

For example, to explain a program’s purpose:

  1. usage do
  2. header "A command line interface for foo service"
  3. end

This would print:

  1. A command line interface for foo service
  2. Usage: foo [OPTIONS]

The header setting accepts many arguments, each representing a single
paragraph. An empty string displays as a new line.

For example, to create an introduction with two paragraphs separated by
an empty line:

  1. usage do
  2. header "A command line interface for foo service",
  3. "",
  4. "Access and retrieve data from foo service"
  5. end

Or, add two paragraphs using the header setting twice:

  1. usage do
  2. header "A command line interface for foo service"
  3. header "Access and retrieve data from foo service"
  4. end

Both would result in the same output:

  1. A command line interface for foo service
  2. Access and retrieve data from foo service
  3. Usage: foo [OPTIONS]

2.8.2 program

The program setting uses an executable file name to generate a program
name by default.

For example, to override the default name:

  1. usage do
  2. program "custom-name"
  3. end

Then usage banner will display a custom program name:

  1. Usage: custom-name

2.8.3 command

The command setting uses a class name to generate a command name by default.
It converts a class name into a dash case.

For example, given the following command class name:

  1. class NetworkCreate
  2. include TTY::Option
  3. end

The command name would become network-create.

Use the command or commands setting to change the default command name.

For example, to change the previous class’s default command name:

  1. class NetworkCreate
  2. include TTY::Option
  3. usage do
  4. command "net-create"
  5. end
  6. end

The usage banner would be:

  1. Usage: program net-create

Use the commands setting for naming a subcommand.

For example, to add create command as a subcommand:

  1. module Network
  2. class Create
  3. include TTY::Option
  4. usage do
  5. commands "network", "create"
  6. end
  7. end
  8. end

This will result in the following usage banner:

  1. Usage: program network create

Use the no_command setting to skip having a command name:

  1. usage do
  2. no_command
  3. end

This will display only the program name:

  1. Usage: program

2.8.4 banner

The banner setting combines program, command and parameter names
to generate usage banner.

For example, given the following usage and parameter definitions:

  1. usage do
  2. program "prog"
  3. command "cmd"
  4. end
  5. argument :foo
  6. keyword :bar
  7. option :baz
  8. env :qux

Then usage banner would print as follows:

  1. Usage: prog cmd [OPTIONS] [ENVIRONMENT] FOO [BAR=BAR]

The help generator displays the usage banner first
unless a header is set.

Use the banner setting to create a custom usage display.

For example, to change the parameters format:

  1. usage do
  2. program "prog"
  3. command "cmd"
  4. banner "Usage: #{program} #{command.first} <opts> <envs> foo [bar=bar]"
  5. end

This would display as:

  1. Usage: prog cmd <opts> <envs> foo [bar=bar]

Use the :param_display setting to change the banner
parameters format.

2.8.5 description

Use description or desc setting to display information right after
the usage banner.

For example, to give extra information:

  1. usage do
  2. desc "A description for foo service"
  3. end

This would print:

  1. Usage: foo [OPTIONS]
  2. A description for foo service

The desc setting accepts many arguments, each representing a single
paragraph. An empty string displays as a new line.

For example, to create a description with two paragraphs separated by
an empty line:

  1. usage do
  2. desc "A description for foo service",
  3. "",
  4. "Learn more about foo service\nby reading tutorials"
  5. end

Or, add two paragraphs using the desc setting twice:

  1. usage do
  2. desc "A description for foo service",
  3. desc <<~EOS
  4. Learn more about foo service
  5. by reading tutorials
  6. EOS
  7. end

Both would result in the same output:

  1. Usage: foo [OPTIONS]
  2. A description for foo service
  3. Learn more about foo service
  4. by reading tutorials

2.8.6 example

Use the example or examples setting to add a usage examples section
to the help display.

The example setting accepts many arguments, each representing a single
paragraph. An empty string displays as a new line.

For instance, to create an example usage displayed on two lines:

  1. usage do
  2. example "Some example how to use foo",
  3. " $ foo bar"
  4. end

This will result in the following help output:

  1. Examples:
  2. Some example how to use foo
  3. $ foo bar

Or, add two examples using the example setting twice:

  1. usage do
  2. example "Some example how to use foo",
  3. " $ foo bar"
  4. example <<~EOS
  5. Another example how to use foo"
  6. $ foo baz
  7. EOS
  8. end

The examples section would display the following:

  1. Examples:
  2. Some example how to use foo
  3. $ foo bar
  4. Another example how to use foo
  5. $ foo baz

Use the footer setting to display text after all information
in the usage help.

For example, to reference further help:

  1. usage do
  2. footer "Run a command followed by --help to see more info."
  3. end

This would print as follows:

  1. Usage: foo [OPTIONS]
  2. Run a command followed by --help to see more info.

The footer setting accepts many arguments, each representing a single
paragraph. An empty string displays as a new line.

For example, to display further help with two paragraphs separated by
an empty line:

  1. usage do
  2. footer "Run a command followed by --help to see more info.",
  3. "",
  4. "Report bugs to the mailing list."
  5. end

Or, add two paragraphs using the footer setting twice:

  1. usage do
  2. footer "Run a command followed by --help to see more info."
  3. footer "Report bugs to the mailing list."
  4. end

Both would result in the same output:

  1. Usage: foo [OPTIONS]
  2. Run a command followed by --help to see more info.
  3. Report bugs to the mailing list.

2.9 help

Use the help method to generate usage information about defined parameters.

The usage describes how to add different sections to the
help display.

For example, given the following command class definition with
a run method that prints help:

  1. class Command
  2. include TTY::Option
  3. usage do
  4. program "foobar"
  5. no_command
  6. header "foobar CLI"
  7. desc "CLI description"
  8. example "Example usage"
  9. footer "Run --help to see more info"
  10. end
  11. argument :foo, desc: "Argument description"
  12. keyword :bar, desc: "Keyword description"
  13. option :baz, desc: "Option description"
  14. env :qux, desc: "Environment description"
  15. flag :help do
  16. short "-h"
  17. long "--help"
  18. desc "Print usage"
  19. end
  20. def run
  21. if params[:help]
  22. print help
  23. exit
  24. end
  25. end
  26. end

Running the command with --help flag:

  1. cmd = Command.new
  2. cmd.parse(%w[--help])
  3. cmd.run

This would result in the following help display:

  1. foobar CLI
  2. Usage: foobar [OPTIONS] [ENVIRONMENT] FOO [BAR=BAR]
  3. CLI description
  4. Arguments:
  5. FOO Argument description
  6. Keywords:
  7. BAR=BAR Keyword description
  8. Options:
  9. --baz Option description
  10. -h, --help Print usage
  11. Environment:
  12. QUX Environment description
  13. Examples:
  14. Example usage
  15. Run --help to see more info

2.9.1 sections

Pass a block to the help method to change generated usage information.
The block accepts a single argument, a TTY::Option::Sections object.
This object provides hash-like access to each named part of the help display.

The following are the names of all supported sections ordered by help display
from top to bottom:

  • :header
  • :banner
  • :description
  • :arguments
  • :keywords
  • :options
  • :environments
  • :examples
  • :footer

Accessing a named section returns a TTY::Option::Section object
with name and content methods.

For example, to access the arguments section content:

  1. help do |sections|
  2. sections[:arguments].content # => "\nArguments:\n FOO Argument description"
  3. end

To add a new section, use the add_after and add_before methods. These
methods accept three arguments. The first argument is the section name
to add after or before. The second argument is a new section name,
and the last is content to add.

For example, to insert a new commands section after the description:

  1. help do |sections|
  2. sections.add_after :description, :commands, <<~EOS.chomp
  3. Commands:
  4. create Create command description
  5. delete Delete command description
  6. EOS
  7. end

Given the following usage and parameter definition:

  1. usage do
  2. program "prog"
  3. command "cmd"
  4. desc "Program description"
  5. end
  6. argument :foo do
  7. desc "Foo argument description"
  8. end

The help display would be:

  1. Usage: prog cmd FOO
  2. Program description
  3. Commands:
  4. create Create command description
  5. delete Delete command description
  6. Arguments:
  7. FOO Argument description

Use delete and replace methods to change existing sections.

For example, to remove a header section:

  1. help do |sections|
  2. sections.delete :header
  3. end

Or, to replace the content of a footer section:

  1. help do |sections|
  2. sections.replace :footer, "\nReport bugs to the mailing list."
  3. end

2.9.2 :indent

The help output has no indentation except for displaying parameters by default.

Use the :indent keyword to change the indentation of the help display.

For example, to indent help display by two spaces:

  1. help(indent: 2)

2.9.3 :order

The help generator orders parameters alphabetically within
each section by default.

Use the :order keyword to change the default ordering.

The :order expects a Proc object as a value. The Proc accepts
a single argument, an array of parameters within a section.

For example, to preserve the parameter definition order:

  1. help(order: ->(params) { params })

2.9.4 :param_display

The usage banner displays positional and keyword arguments
in uppercase letters by default.

For example, given the following parameter definitions:

  1. usage do
  2. program "prog"
  3. end
  4. argument :foo, desc: "Argument description"
  5. keyword :bar, desc: "Keyword description"
  6. option :baz, desc: "Option description"
  7. env :qux, desc: "Environment description"

The usage banner would print as follows:

  1. Usage: prog [OPTIONS] [ENVIRONMENT] FOO [BAR=BAR]

Use the :param_display keyword to change the banner parameter formatting.

The :param_display expects a Proc object as a value. The Proc accepts
a single argument, a parameter name within a section.

For example, to lowercase and surround parameters with < and > brackets:

  1. help(param_display: ->(param) { "<#{param.downcase}>" })

This would result in the following usage banner and parameter sections:

  1. Usage: prog [<options>] [<environment>] <foo> [<bar>=<bar>]
  2. Arguments:
  3. <foo> Argument description
  4. Keywords:
  5. <bar>=<bar> Keyword description
  6. Options:
  7. --baz Option description
  8. Environment:
  9. QUX Environment description

2.9.5 :width

The help generator wraps content at the width of 80 columns by default.

Use the :width keyword to change it, for example, to 120 columns:

  1. help(width: 120)

Use the tty-screen gem
to change the help display based on terminal width.

For example, to expand the help display to the full width of
the terminal window:

  1. help(width: TTY::Screen.width)

Development

After checking out the repo, run bin/setup to install dependencies.
Then, run rake spec to run the tests. You can also run bin/console
for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run bundle exec rake install.
To release a new version, update the version number in version.rb, and then
run bundle exec rake release, which will create a git tag for the version,
push git commits and tags, and push the .gem file to
rubygems.org.

Contributing

Bug reports and pull requests are welcome on GitHub at
https://github.com/piotrmurach/tty-option. This project is intended to be
a safe, welcoming space for collaboration, and contributors are expected
to adhere to the code of conduct.

License

The gem is available as open source under the terms of the
MIT License.

Code of Conduct

Everyone interacting in the TTY::Option project’s codebases, issue trackers,
chat rooms and mailing lists is expected to follow the
code of conduct.

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