{"id":13878367,"url":"https://github.com/svenfuchs/cl","last_synced_at":"2025-04-21T23:31:10.902Z","repository":{"id":56843526,"uuid":"87672449","full_name":"svenfuchs/cl","owner":"svenfuchs","description":"Object-oriented OptionParser based CLI support for rapid CLI development","archived":false,"fork":false,"pushed_at":"2021-09-26T18:04:43.000Z","size":298,"stargazers_count":12,"open_issues_count":3,"forks_count":4,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-16T22:09:34.640Z","etag":null,"topics":["cli","option","option-parser","ruby"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/svenfuchs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-04-09T00:27:32.000Z","updated_at":"2023-11-07T12:45:11.000Z","dependencies_parsed_at":"2022-09-07T07:11:26.738Z","dependency_job_id":null,"html_url":"https://github.com/svenfuchs/cl","commit_stats":null,"previous_names":[],"tags_count":51,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svenfuchs%2Fcl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svenfuchs%2Fcl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svenfuchs%2Fcl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/svenfuchs%2Fcl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/svenfuchs","download_url":"https://codeload.github.com/svenfuchs/cl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250150652,"owners_count":21383206,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cli","option","option-parser","ruby"],"created_at":"2024-08-06T08:01:47.494Z","updated_at":"2025-04-21T23:31:10.302Z","avatar_url":"https://github.com/svenfuchs.png","language":"Ruby","readme":"# Cl [![Build Status](https://travis-ci.org/svenfuchs/cl.svg?branch=master)](https://travis-ci.org/svenfuchs/cl) [![Code Climate](https://api.codeclimate.com/v1/badges/870e448eb8162d3e1ed7/maintainability)](https://codeclimate.com/github/svenfuchs/cl) [![Code Coverage](https://coveralls.io/repos/github/svenfuchs/cl/badge.svg?branch=master)](https://coveralls.io/github/svenfuchs/cl?branch=master) [![Gem Version](https://img.shields.io/gem/v/cl?cache=2019-08-10)](http://rubygems.org/gems/cl) [![Rubydocs](http://img.shields.io/badge/yard-docs-blue.svg)](http://rubydoc.info/github/svenfuchs/cl)\n\nOptionParser based CLI support for rapid CLI development in an object-oriented\ncontext.\n\nThis library wraps Ruby's OptionParser for parsing your options under the hood,\nso you get all the goodness that the Ruby standard library provides.\n\nOn top of that it adds a rich and powerful DSL for defining, validating, and\nnormalizing options, as well as automatic and gorgeous help output (modeled\nafter `gem --help`).\n\nFurther documentation is available on [rubydoc.info](https://www.rubydoc.info/github/svenfuchs/cl)\n\nExamples in this README are included from [examples/readme](https://github.com/svenfuchs/cl/tree/master/examples/readme).\nMore examples can be found in [examples](https://github.com/svenfuchs/cl/tree/master/examples).\nAll examples are guaranteed to be up to date by the way of being [verified](https://github.com/svenfuchs/cl/blob/master/.travis.yml#L14)\non CI.\n\n## Table of Contents\n\n* [Basic Usage](#basic-usage)\n* [Command Registry](#command-registry)\n* [Runners](#runners)\n* [Command DSL](#command-dsl)\n  * [Commands](#commands)\n    * [Description, Summary, Examples](#description-summary-examples)\n    * [Abstract](#abstract)\n  * [Arguments](#arguments)\n    * [Types](#types)\n    * [Splat](#splat)\n  * [Options](#options)\n    * [Aliases](#aliases)\n    * [Defaults](#defaults)\n    * [Deprecations](#deprecations)\n    * [Downcase](#downcase)\n    * [Enum](#enum)\n    * [Example](#example)\n    * [Format](#format)\n    * [Internal](#internal)\n    * [Min and Max](#min-and-max)\n    * [Negations](#negations)\n    * [Note](#note)\n    * [Secret](#secret)\n    * [See Also](#see-also)\n    * [Types](#types)\n    * [Required Options](#required-options)\n* [Config Files](#config-files)\n* [Environment Variables](#environment-variables)\n\n## Basic Usage\n\n```ruby\nmodule Owners\n  class Add \u003c Cl::Cmd\n    register :add\n\n    summary 'Add one or more owners to an existing owner group'\n\n    description \u003c\u003c~str\n      Use this command to add one or more owners to an existing\n      owner group.\n\n      [...]\n    str\n\n    args :owner\n\n    opt '-t', '--to TO', 'An existing owner group'\n\n    def run\n      # implement adding the owner as given in `owner` (as well as `args`)\n      # to the group given in `to` (as well as `opts[:to]`).\n      p owner: owner, to: to, to?: to?, args: args, opts: opts\n    end\n  end\nend\n\n# Running this, e.g. using `bin/owners add one,two --to group` will instantiate the\n# class `Owners::Add`, and call the method `run` on it.\n\n# e.g. bin/owners\n#\n# args normally would be ARGV\nargs = %w(add one --to group)\n\nCl.new('owners').run(args)\n\n# Output:\n#\n#   {:owner=\u003e\"one\", :to=\u003e\"group\", :to?=\u003etrue, :args=\u003e[\"one\"], :opts=\u003e{:to=\u003e\"group\"}}\n\nCl.new('owners').run(%w(add --help))\n\n# Output:\n#\n#   Usage: owners add [owner] [options]\n#\n#   Summary:\n#\n#     Add one or more owners to an existing owner group\n#\n#   Description:\n#\n#     Use this command to add one or more owners to an existing\n#     owner group.\n#\n#     [...]\n#\n#   Arguments:\n#\n#     owner           type: string\n#\n#   Options:\n#\n#     -t --to TO      An existing owner group (type: string)\n#        --help       Get help on this command\n\n```\n\n### Command Registry\n\nCommands are Ruby classes that extend the class `Cl::Cmd`.\n\nThey register to a [Ruby class registry](https://github.com/svenfuchs/registry) in order\nto decouple looking up command classes from their Ruby namespace.\n\nFor example:\n\n```ruby\nmodule Cmd\n  class One \u003c Cl::Cmd\n    register :one\n  end\n\n  class Two \u003c Cl::Cmd\n    register :two\n  end\nend\n\np Cl::Cmd[:one] # =\u003e Cmd::One\np Cl::Cmd[:two] # =\u003e Cmd::Two\n\n```\n\nCommands can be registered like so:\n\n```ruby\nmodule One\n  class Cmd \u003c Cl::Cmd\n    register :'cmd:one'\n  end\nend\n```\n\nBy default commands auto register themselves with the underscored name of the\nlast part of their class name (as seen in the example above). It is possible to\nturn this off using:\n\n```ruby\nCl::Cmd.auto_register = false\n```\n\nCommand auto-registration can cause name clashes when namespaced commands have\nthe same demodulized class name. For example:\n\n```ruby\nclass Git \u003c Cl::Cmd\n  # auto-registers as :git\nend\n\nmodule Heroku\n  class Git \u003c Cl::Cmd\n    # also auto-registers as :git\n  end\nend\n```\n\nIt is recommended to turn auto-registration off when using such module\nstructures.\n\n\n### Runners\n\nRunners lookup the command to execute from the registry, by checking the\narguments given by the user for registered command keys.\n\nWith the two command classes `One` and `Two` from the example above (and\nassuming that the executable that calls `Cl` is `bin/run`) the default runner\nwould recognize and run the following commands:\n\n```\n$ bin/run one something else\n# instantiates One, passing the args array `[\"something\", \"else\"]`, and calls the instance method `run`\n\n$ bin/run two\n# instantiates Two, passing an empty args arry `[]`, and calls the instance method `run`\n```\n\nThe default runner also supports nested namespaces, and checks for command classes\nwith keys separated by colons. For instance:\n\n```ruby\nmodule Git\n  class Pull \u003c Cl::Cmd\n    register :'git:pull'\n\n    arg :branch\n\n    def run\n      p cmd: registry_key, args: args\n    end\n  end\nend\n\n# With this class registered (and assuming the executable that calls `Cl` is\n# `bin/run`) the default runner would recognize and run it:\n#\n# $ bin/run git:pull master # instantiates Git::Pull, and passes [\"master\"] as args\n# $ bin/run git pull master # does the same\n\nCl.new('run').run(%w(git:pull master))\n# Output:\n#\n#   {:cmd=\u003e:\"git:pull\", :args=\u003e[\"master\"]}\n\nCl.new('run').run(%w(git pull master))\n# Output:\n#\n#   {:cmd=\u003e:\"git:pull\", :args=\u003e[\"master\"]}\n\n```\n\nRunners are registered on the module `Cl::Runner`. It is possible to register custom\nrunners, and use them by passing the option `runner` to `Cl.new`:\n\n```ruby\nmodule Git\n  class Pull \u003c Cl::Cmd\n    register :'git:pull'\n\n    arg :branch\n\n    def run\n      p cmd: registry_key, args: args\n    end\n  end\nend\n\n# With this class registered (and assuming the executable that calls `Cl` is\n# `bin/run`) the default runner would recognize and run it:\n#\n# $ bin/run git:pull master # instantiates Git::Pull, and passes [\"master\"] as args\n# $ bin/run git pull master # does the same\n\nCl.new('run').run(%w(git:pull master))\n# Output:\n#\n#   {:cmd=\u003e:\"git:pull\", :args=\u003e[\"master\"]}\n\nCl.new('run').run(%w(git pull master))\n# Output:\n#\n#   {:cmd=\u003e:\"git:pull\", :args=\u003e[\"master\"]}\n\n```\n\nSee `Cl::Runner::Default` for more details.\n\nThere also is an experimental runner `:multi`, which supports rake-style\nexecution of multiple commands at once, like so:\n\n```\nbin/rake db:drop production -f db:create db:migrate production -v 1\n```\n\nSee the example [rakeish](blob/master/examples/rakeish) for more details.\n\n## Command DSL\n\nThe DSL is defined on the class body.\n\n### Commands\n\nCommands are classes that are derived from the base class `Cl::Cmd`.\n\n#### Description, Summary, Examples\n\nThe description, summary, and examples are used in the help output.\n\n```ruby\nmodule Owners\n  class Add \u003c Cl::Cmd\n    register :add\n\n    summary 'Add one or more owners to an existing owner group'\n\n    description \u003c\u003c~str\n      Use this command to add one or more owners to an existing\n      owner group.\n    str\n\n    examples \u003c\u003c~str\n      Adding a single user to the group admins:\n\n        owners add user --to admins\n\n      Adding a several users at once:\n\n        owners add one two three --to admins\n    str\n  end\nend\n\nCl.new('owners').run(%w(add --help))\n\n# Output:\n#\n#   Usage: owners add [options]\n#\n#   Summary:\n#\n#     Add one or more owners to an existing owner group\n#\n#   Description:\n#\n#     Use this command to add one or more owners to an existing\n#     owner group.\n#\n#   Options:\n#\n#     --help      Get help on this command\n#\n#   Examples:\n#\n#     Adding a single user to the group admins:\n#\n#       owners add user --to admins\n#\n#     Adding a several users at once:\n#\n#       owners add one two three --to admins\n\n```\n\n#### Abstract\n\nCommand base classes can be declared abstract in order to prevent them from\nbeing identified as a runnable command and to  omit them from help output.\n\nThis is only relevant if a command base class is registered. See [Command\nRegistry](#command-registry) for details.\n\n```ruby\nclass Base \u003c Cl::Cmd\n  abstract\nend\n\nclass Add \u003c Base\n  register :add\n\n  def run\n    puts 'Success'\n  end\nend\n\nCl.new('owners').run(%w(add))\n\n# Output:\n#\n#   Success\n\nCl.new('owners').run(%w(base))\n\n# Output:\n#\n#   Unknown command: base\n\n```\n\n### Arguments\n\nArguments can be declared like so:\n\n```ruby\narg :arg_name, description: 'arg description', type: :[array|string|integer|float|boolean]\n```\n\nThis will define an `attr_accessor` on the `Cmd` class. I.e. in the following\nexample the method `ownsers` will be available on the `Cmd` instance:\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  arg :owner\n\n  def run\n    p owner: owner\n  end\nend\n\nCl.new('owners').run(%w(add one))\n\n# Output:\n#\n#   {:owner=\u003e\"one\"}\n\n```\n\n#### Types\n\nArguments can have a type. Known types are: `:array`, `:string`, `:integer`,\n`:float`, `:boolean`.\n\nThe type `:array` makes sure the argument accessible on the `Cmd` instance is a\nRuby Array. (This currently only supports arrays of strings).\n\nIf the option `sep` is given on the argument, then the argument value is split\nusing this separator.\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  arg :owners, type: :array, sep: ','\n\n  def run\n    p owners: owners\n  end\nend\n\nCl.new('owners').run(%w(add one,two))\n\n# Output:\n#\n#   {:owners=\u003e[\"one\", \"two\"]}\n\n```\n\nOther types cast the given argument to the expected Ruby type.\n\n```ruby\nclass Cmd \u003c Cl::Cmd\n  register :cmd\n\n  arg :one, type: :integer\n  arg :two, type: :float\n  arg :three, type: :boolean\n\n  def run\n    p [one.class, two.class, three.class]\n  end\nend\n\nCl.new('owners').run(%w(cmd 1 2.1 yes))\n\n# Output:\n#\n#   [Integer, Float, TrueClass]\n\n```\n\n#### Splat\n\nArray arguments support splats, modeled after Ruby argument splats.\n\nFor example:\n\n```ruby\nclass Lft \u003c Cl::Cmd\n  register :lft\n\n  arg :a, type: :array, splat: true\n  arg :b\n  arg :c\n\n  def run\n    p [a, b, c]\n  end\nend\n\nclass Mid \u003c Cl::Cmd\n  register :mid\n\n  arg :a\n  arg :b, type: :array, splat: true\n  arg :c\n\n  def run\n    p [a, b, c]\n  end\nend\n\nclass Rgt \u003c Cl::Cmd\n  register :rgt\n\n  arg :a\n  arg :b\n  arg :c, type: :array, splat: true\n\n  def run\n    p [a, b, c]\n  end\nend\n\nCl.new('splat').run(%w(lft 1 2 3 4 5))\n\n# Output:\n#\n#   [[\"1\", \"2\", \"3\"], \"4\", \"5\"]\n\nCl.new('splat').run(%w(mid 1 2 3 4 5))\n\n# Output:\n#\n#   [\"1\", [\"2\", \"3\", \"4\"], \"5\"]\n\nCl.new('splat').run(%w(rgt 1 2 3 4 5))\n\n# Output:\n#\n#   [\"1\", \"2\", [\"3\", \"4\", \"5\"]]\n\n```\n\n### Options\n\nDeclaring options can be done by calling the method `opt` on the class body.\n\nThis will add the option, if given by the user, to the hash `opts` on the `Cmd` instance.\nIt also defines a reader method that returns the respective value from the `opts` hash,\nand a predicate that will be true if the option has been given.\n\nFor example:\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--to GROUP', 'Target group to add owners to'\n\n  def run\n    p opts: opts, to: to, to?: to?\n  end\nend\n\nCl.new('owners').run(%w(add --to one))\n\n# Output:\n#\n#   {:opts=\u003e{:to=\u003e\"one\"}, :to=\u003e\"one\", :to?=\u003etrue}\n\nCl.new('owners').run(%w(add --help))\n\n# Output:\n#\n#   Usage: owners add [options]\n#\n#   Options:\n#\n#     --to GROUP      Target group to add owners to (type: string)\n#     --help          Get help on this command\n\n```\n\nOptions optionally accept a block in case custom normalization is needed.\n\nDepending on the block's arity the following arguments are passed to the block:\noption value, option name, option type, collection of all options defined on\nthe command.\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  # depending on its arity the block can receive:\n  #\n  # * value\n  # * value, name\n  # * value, name, type\n  # * value, name, type, opts\n  opt '--to GROUP' do |value|\n    opts[:to] = \"group-#{value}\"\n  end\n\n  def run\n    p to: to\n  end\nend\n\nCl.new('owners').run(%w(add --to one))\n\n# Output:\n#\n#   {:to=\u003e\"group-one\"}\n\n\n```\n\n#### Aliases\n\nOptions can have one or many alias names, given as a Symbol or Array of Symbols:\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--to GROUP', alias: :group\n\n  def run\n    # p opts: opts, to: to, to?: to?, group: group, group?: group?\n    p opts: opts, to: to, to?: to?\n  end\nend\n\nCl.new('owners').run(%w(add --group one))\n\n# Output:\n#\n#   {:opts=\u003e{:to=\u003e\"one\", :group=\u003e\"one\"}, :to=\u003e\"one\", :to?=\u003etrue}\n\n```\n\n#### Defaults\n\nOptions can have a default value.\n\nI.e. this value is going to be used if the user does not provide the option:\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--to GROUP', default: 'default'\n\n  def run\n    p to: to\n  end\nend\n\nCl.new('owners').run(%w(add))\n\n# Output:\n#\n#   {:to=\u003e\"default\"}\n\n```\n\n#### Deprecations\n\nOptions, and option alias name can be deprecated.\n\nFor a deprecated option:\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--target GROUP', deprecated: 'Deprecated.'\n\n  def run\n    p target: target, deprecations: deprecations\n  end\nend\n\nCl.new('owners').run(%w(add --target one))\n\n# Output:\n#\n#   {:target=\u003e\"one\", :deprecations=\u003e{:target=\u003e\"Deprecated.\"}}\n\n```\n\nFor a deprecated option alias name:\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--to GROUP', alias: :target, deprecated: :target\n\n  def run\n    p to: to, deprecations: deprecations\n  end\nend\n\nCl.new('owners').run(%w(add --target one))\n\n# Output:\n#\n#   {:to=\u003e\"one\", :deprecations=\u003e{:target=\u003e:to}}\n\n```\n\n#### Downcase\n\nOptions can be declared to be downcased.\n\nFor example:\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--to GROUP', downcase: true\n\n  def run\n    p to: to\n  end\nend\n\nCl.new('owners').run(%w(add --to ONE))\n\n# Output:\n#\n#   {:to=\u003e\"one\"}\n\n```\n\n#### Enum\n\nOptions can be enums (i.e. have known values).\n\nIf an unknown values is given by the user the parser will reject the option,\nand print the help output for this command.\n\nFor example:\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--to GROUP', enum: %w(one two)\n\n  def run\n    p to: to\n  end\nend\n\nCl.new('owners').run(%w(add --to one))\n\n# Output:\n#\n#   {:to=\u003e\"one\"}\n\nCl.new('owners').run(%w(add --to unknown))\n\n# Output:\n#\n#   Unknown value: to=unknown (known values: one, two)\n#\n#   Usage: owners add [options]\n#\n#   Options:\n#\n#     --to GROUP      type: string, known values: one, two\n#     --help          Get help on this command\n\n```\n\n#### Example\n\nOptions can have examples that will be printed in the help output.\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--to GROUP', example: 'group-one'\nend\n\nCl.new('owners').run(%w(add --help))\n\n# Output:\n#\n#   Usage: owners add [options]\n#\n#   Options:\n#\n#     --to GROUP      type: string, e.g.: group-one\n#     --help          Get help on this command\n\n```\n\n#### Format\n\nOptions can have a required format.\n\nIf a value is given by the user that does not match the format then the parser\nwill reject the option, and print the help output for this command.\n\nFor example:\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--to GROUP', format: /^\\w+$/\n\n  def run\n    p to: to\n  end\nend\n\nCl.new('owners').run(%w(add --to one))\n\n# Output:\n#\n#   {:to=\u003e\"one\"}\n\nCl.new('owners').run(['add', '--to', 'does not match!'])\n\n# Output:\n#\n#   Invalid format: to (format: /^\\w+$/)\n#\n#   Usage: owners add [options]\n#\n#   Options:\n#\n#     --to GROUP      type: string, format: /^\\w+$/\n#     --help          Get help on this command\n\n```\n\n#### Internal\n\nOptions can be declared to be internal, hiding the option from the help output.\n\nFor example:\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--to GROUP'\n  opt '--hidden', internal: true\nend\n\nCl.new('owners').run(%w(add --help))\n\n# Output:\n#\n#   Usage: owners add [options]\n#\n#   Options:\n#\n#     --to GROUP      type: string\n#     --help          Get help on this command\n\n```\n\n#### Min and Max\n\nOptions can have mininum and/or maximum values.\n\nIf a value is given by the user that does not match the required min and/or max\nvalues then the parser will reject the option, and print the help output for\nthis command.\n\nFor example:\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--retries COUNT', type: :integer, min: 1, max: 5\n\n  def run\n    p retries: retries\n  end\nend\n\nCl.new('owners').run(%w(add --retries 1))\n\n# Output:\n#\n#   {:retries=\u003e1}\n\nCl.new('owners').run(%w(add --retries 10))\n\n# Output:\n#\n#   Out of range: retries (min: 1, max: 5)\n#\n#   Usage: owners add [options]\n#\n#   Options:\n#\n#     --retries COUNT      type: integer, min: 1, max: 5\n#     --help               Get help on this command\n\n```\n\n#### Negations\n\nFlags (boolean options) automatically allow negation using `--no-*` and\n`--no_*` using OptionParser's support for these. However, sometimes it can be\nconvenient to allow other terms for negating an option. Flags therefore accept\nan option `negate` like so:\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--notifications', 'Send out notifications to the team', negate: %w(skip)\n\n  def run\n    p notifications?\n  end\nend\n\nCl.new('owners').run(%w(add --notifications))\n\n# Output:\n#\n#   true\n\nCl.new('owners').run(%w(add --no_notifications))\n\n# Output:\n#\n#   false\n\nCl.new('owners').run(%w(add --no-notifications))\n\n# Output:\n#\n#   false\n\nCl.new('owners').run(%w(add --skip_notifications))\n\n# Output:\n#\n#   false\n\nCl.new('owners').run(%w(add --skip-notifications))\n\n# Output:\n#\n#   false\n\n```\n\n#### Note\n\nOptions can have a note that will be printed in the help output.\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--to GROUP', note: 'needs to be a group'\nend\n\nCl.new('owners').run(%w(add --help))\n\n# Output:\n#\n#   Usage: owners add [options]\n#\n#   Options:\n#\n#     --to GROUP      type: string, note: needs to be a group\n#     --help          Get help on this command\n\n```\n\n#### Secret\n\nOptions can be declared as secret.\n\nThis makes it possible for client code to inspect if a given option is secret.\nAlso, option values given by the user will be tainted, so client code can rely\non this in order to, for example, obfuscate values from log output.\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--pass PASS', secret: true\n\n  def run\n    p(\n      secret?: self.class.opts[:pass].secret?,\n      tainted?: pass.tainted?\n    )\n  end\nend\n\nCl.new('owners').run(%w(add --pass pass))\n\n# Output:\n#\n#   {:secret?=\u003etrue, :tainted?=\u003etrue}\n\n```\n\n#### See Also\n\nOptions can refer to documentation using the `see` option. This will be printed\nin the help output.\n\nFor example:\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--to GROUP', see: 'https://docs.io/cli/owners/add'\nend\n\nCl.new('owners').run(%w(add --help))\n\n# Output:\n#\n#   Usage: owners add [options]\n#\n#   Options:\n#\n#     --to GROUP      type: string, see: https://docs.io/cli/owners/add\n#     --help          Get help on this command\n\n```\n\n#### Types\n\nOptions can have a type. Known types are: `:array`, `:string`, `:integer`,\n`:float`, `:boolean`.\n\nThe type `:array` allows an option to be given multiple times, and makes sure\nthe value accessible on the `Cmd` instance is a Ruby Array. (This currently\nonly supports arrays of strings).\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--to GROUP', type: :array\n\n  def run\n    p to\n  end\nend\n\nCl.new('owners').run(%w(add --to one --to two))\n\n# Output:\n#\n#   [\"one\", \"two\"]\n\n```\n\nOther types cast the given value to the expected Ruby type.\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--active BOOL', type: :boolean\n  opt '--retries INT', type: :integer\n  opt '--sleep FLOAT', type: :float\n\n  def run\n    p active: active.class, retries: retries.class, sleep: sleep.class\n  end\nend\n\nCl.new('owners').run(%w(add --active yes --retries 1 --sleep 0.1))\n\n# Output:\n#\n#   {:active=\u003eTrueClass, :retries=\u003eInteger, :sleep=\u003eFloat}\n\n```\n\n#### Required Options\n\nThere are three ways options can be required:\n\n* using `required: true` on the option: the option itself is required to be given\n* using `requires: :other`on the option: the option requires another option to be given\n* using `required :one, [:two, :three]` on the class: either `one` or both `two` and `three` must be given\n\nFor example, this simply requires the option `--to`:\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--to GROUP', required: true\n\n  def run\n    p to: to\n  end\nend\n\nCl.new('owners').run(%w(add --to one))\n\n# Output:\n#\n#   {:to=\u003e\"one\"}\n\nCl.new('owners').run(%w(add))\n\n# Output:\n#\n#   Missing required option: to\n#\n#   Usage: owners add [options]\n#\n#   Options:\n#\n#     --to GROUP      type: string, required\n#     --help          Get help on this command\n\n```\n\nThis will make the option `--retries` depend on the option `--to`:\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  opt '--to GROUP'\n  opt '--other GROUP', requires: :to\n\n  def run\n    p to: to, other: other\n  end\nend\n\nCl.new('owners').run(%w(add --to one --other two))\n\n# Output:\n#\n#   {:to=\u003e\"one\", :other=\u003e\"two\"}\n\nCl.new('owners').run(%w(add --other two))\n\n# Output:\n#\n#   Missing option: to (required by other)\n#\n#   Usage: owners add [options]\n#\n#   Options:\n#\n#     --to GROUP         type: string\n#     --other GROUP      type: string, requires: to\n#     --help             Get help on this command\n\n```\n\nThis requires either the option `--api_key` or both options `--username` and\n`--password` to be given:\n\n```ruby\nclass Add \u003c Cl::Cmd\n  register :add\n\n  # read DNF, i.e. \"token OR user AND pass\n  required :token, [:user, :pass]\n\n  opt '--token TOKEN'\n  opt '--user NAME'\n  opt '--pass PASS'\n\n  def run\n    p token: token, user: user, pass: pass\n  end\nend\n\nCl.new('owners').run(%w(add --token token))\n\n# Output:\n#\n#   {:token=\u003e\"token\", :user=\u003enil, :pass=\u003enil}\n\nCl.new('owners').run(%w(add --user user --pass pass))\n\n# Output:\n#\n#   {:token=\u003enil, :user=\u003e\"user\", :pass=\u003e\"pass\"}\n\nCl.new('owners').run(%w(add))\n\n# Output:\n#\n#   Missing options: token, or user and pass\n#\n#   Usage: owners add [options]\n#\n#   Options:\n#\n#     Either token, or user and pass are required.\n#\n#     --token TOKEN      type: string\n#     --user NAME        type: string\n#     --pass PASS        type: string\n#     --help             Get help on this command\n\n```\n\n### Config Files\n\nCl automatically reads config files that match the given executable name (inspired by\n[gem-release](https://github.com/svenfuchs/gem-release)), stored either in the\nuser directory or the current working directory.\n\nFor example:\n\n```ruby\nmodule Api\n  class Login \u003c Cl::Cmd\n    opt '--username USER'\n    opt '--password PASS'\n  end\nend\n\n# bin/api\nCL.new('api').run(ARGV)\n\n# ~/api.yml\nlogin:\n  username: 'someone'\n  password: 'password'\n\n# ./api.yml\nlogin:\n  username: 'someone else'\n```\n\nthen running\n\n```\n$ bin/api login\n```\n\ninstantiates `Api::Login`, and passes the hash\n\n```ruby\n{ username: 'someone else', password: 'password' }\n```\n\nas `opts`.\n\nOptions passed by the user take precedence over defaults defined in config\nfiles.\n\n### Environment Variables\n\nCl automatically defaults options to environment variables that are prefixed\nwith the given executable name (inspired by [gem-release](https://github.com/svenfuchs/gem-release)).\n\n```ruby\nmodule Api\n  class Login \u003c Cl::Cmd\n    opt '--username USER'\n    opt '--password PASS'\n  end\nend\n\n# bin/api\nCL.new('api').run(ARGV)\n```\n\nthen running\n\n```\n$ API_USERNAME=someone API_PASSWORD=password bin/api login\n```\n\ninstantiates `Api::Login`, and passes the hash\n\n```ruby\n{ username: 'someone', password: 'password' }\n```\n\nOptions passed by the user take precedence over defaults given as environment\nvariables, and environment variables take precedence over defaults defined in\nconfig files.\n","funding_links":[],"categories":["Ruby"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsvenfuchs%2Fcl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsvenfuchs%2Fcl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsvenfuchs%2Fcl/lists"}