pax_global_header00006660000000000000000000000064151575012440014516gustar00rootroot0000000000000052 comment=00bf5894fca60ee4fe3349c02ea30f86ba5f86b7 mdub-clamp-00bf589/000077500000000000000000000000001515750124400140765ustar00rootroot00000000000000mdub-clamp-00bf589/.autotest000066400000000000000000000003611515750124400157470ustar00rootroot00000000000000require "autotest/bundler" Autotest.add_hook :initialize do |at| at.add_exception ".git" at.add_mapping(%r{^lib/(.*)\.rb$}, :prepend) do |_, match| ["spec/unit/#{match[1]}_spec.rb"] + Dir['spec/clamp/command*_spec.rb'] end end mdub-clamp-00bf589/.editorconfig000066400000000000000000000002511515750124400165510ustar00rootroot00000000000000root = true [*] indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true max_line_length = 120 mdub-clamp-00bf589/.github/000077500000000000000000000000001515750124400154365ustar00rootroot00000000000000mdub-clamp-00bf589/.github/workflows/000077500000000000000000000000001515750124400174735ustar00rootroot00000000000000mdub-clamp-00bf589/.github/workflows/ci.yml000066400000000000000000000011661515750124400206150ustar00rootroot00000000000000name: CI on: push: branches: [master] pull_request: branches: [master] jobs: test: runs-on: ubuntu-latest strategy: fail-fast: false matrix: ruby-version: - "3.1" - "3.4" - "4.0" steps: - uses: actions/checkout@v4 - name: Install fish and zsh run: sudo apt-get install -y fish zsh - name: Set up Ruby ${{ matrix.ruby-version }} uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true - name: Run tests and linter run: bundle exec rake mdub-clamp-00bf589/.gitignore000066400000000000000000000001331515750124400160630ustar00rootroot00000000000000*.gem .bundle .markdownlint* .rvmrc .ruby-version .yardoc doc pkg/* Gemfile.lock coverage/ mdub-clamp-00bf589/.rspec000066400000000000000000000000231515750124400152060ustar00rootroot00000000000000--color --warnings mdub-clamp-00bf589/.rubocop.yml000066400000000000000000000021431515750124400163500ustar00rootroot00000000000000plugins: - rubocop-rake - rubocop-rspec AllCops: TargetRubyVersion: 2.5 NewCops: enable Exclude: - "**/*.zsh" - "vendor/**/*" Layout/LineLength: Max: 120 Layout/EmptyLinesAroundBlockBody: Enabled: false Layout/EmptyLinesAroundClassBody: EnforcedStyle: empty_lines Layout/EmptyLinesAroundModuleBody: Enabled: false Metrics/AbcSize: Enabled: false Metrics/ClassLength: CountAsOne: - array Metrics/BlockLength: Exclude: - "spec/**/*" Metrics/MethodLength: Max: 30 Naming/AccessorMethodName: Enabled: false Naming/FileName: Exclude: - "bin/*" Naming/PredicatePrefix: Enabled: false Style/ClassAndModuleChildren: EnforcedStyle: nested Exclude: - "spec/**/*" Style/Documentation: Exclude: - "lib/**/version.rb" - "examples/*" - "spec/**/*" Style/Encoding: Enabled: true Style/Lambda: Enabled: false Style/NumericLiterals: Enabled: false Style/StderrPuts: Enabled: false Style/StringLiterals: EnforcedStyle: double_quotes Style/WordArray: Enabled: false RSpec/NestedGroups: Enabled: false RSpec/Output: Enabled: false mdub-clamp-00bf589/CHANGES.md000066400000000000000000000043021515750124400154670ustar00rootroot00000000000000# Changelog ## 1.5.1 (2026-03-11) * Fix shell completion scripts: required parameters, subcommand aliases, and option value handling. ## 1.5.0 (2026-03-03) * Add `--shell-completions` support. ## 1.4.0 (2026-02-09) * Add support for Ruby 4.0. * Set up GitHub Actions for CI. ## 1.3.3 (2025-07-31) * Raise an error if no value is provided for a non-flag switch. ## 1.3.2 (2020-08-20) * Fix Ruby warnings. ## 1.3.1 (2019-07-11) * Choose a sensible column width in generated help, based on content. * Fix issue#99: extraneous parameter names in subcommand help. ## 1.3.0 (2018-06-17) * Add `.execute` DSL method. * Append '(required)' to the description of required options. * Fix issue#75: don't generate `default_XXX` method unless a default is specified. * Fix issue#90: allow required options to be provided after subcommands. ## 1.2.0 (2018-02-12) * Add option to `Clamp.allow_options_after_parameters`. ## 1.1.2 (2017-02-12) * Improve usage help for commands with both parameters and subcommands. ## 1.1.1 (2016-10-19) * Rename `.declare_attribute` back to `.define_accessors_for`. ## 1.1.0 (2016-10-17) * Add `#subcommand_missing`. * Fix issue#66: pass parameter values down to nested subcommands. * Drop support for Ruby 1.9 and 2.0. ## 1.0.1 (2016-10-01) * Minor bug-fixes. ## 1.0.0 (2015-06-08) * Allow options to be `:hidden`. * I18N support. ## 0.6.5 (2015-05-02) * Catch signals and exit appropriately. ## 0.6.4 (2015-02-26) * Ensure computed defaults are only computed once. ## 0.6.3 (2013-11-14) * Specify (MIT) license. ## 0.6.2 (2013-11-06) * Refactoring around multi-valued attributes. * Allow injection of a custom help-builder. ## 0.6.1 (2013-05-07) * Signal a usage error when an environment_variable fails validation. * Refactor setting, defaulting and inheritance of attributes. ## 0.6.0 (2013-04-28) * Introduce "banner" to describe a command (replacing "self.description="). * Introduce "Clamp do ... end" syntax sugar. * Allow parameters to be specified before a subcommand. * Add support for :multivalued options. * Multi valued options and parameters get an "#append_to_foo_list" method, rather than "#foo_list=". * default_subcommand must be specified before any subcommands. mdub-clamp-00bf589/CODEOWNERS000066400000000000000000000000101515750124400154600ustar00rootroot00000000000000* @mdub mdub-clamp-00bf589/Gemfile000066400000000000000000000007161515750124400153750ustar00rootroot00000000000000# frozen_string_literal: true source "https://rubygems.org" gemspec group :development do gem "guard-rspec", "~> 4.7", require: false gem "highline" gem "listen", "~> 3.9" gem "pry-byebug", "~> 3.11" gem "rake", "~> 13.3" gem "rubocop", "~> 1.84.1", require: false gem "rubocop-rake", "~> 0.7.1", require: false gem "rubocop-rspec", "~> 3.9.0", require: false end group :test do gem "rspec", "~> 3.13" gem "simplecov", require: false end mdub-clamp-00bf589/Guardfile000066400000000000000000000027671515750124400157370ustar00rootroot00000000000000# frozen_string_literal: true # A sample Guardfile # More info at https://github.com/guard/guard#readme ## Uncomment and set this to only include directories you want to watch # directories %w(app lib config test spec features) \ # .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")} ## NOTE: if you are using the `directories` clause above and you are not ## watching the project directory ('.'), then you will want to move ## the Guardfile to a watched dir and symlink it back, e.g. # # $ mkdir config # $ mv Guardfile config/ # $ ln -s config/Guardfile . # # and, you'll have to watch "config/Guardfile" instead of "Guardfile" # NOTE: The cmd option is now required due to the increasing number of ways # rspec may be run, below are examples of the most common uses. # * bundler: 'bundle exec rspec' # * bundler binstubs: 'bin/rspec' # * spring: 'bin/rspec' (This will use spring if running and you have # installed the spring binstubs per the docs) # * zeus: 'zeus rspec' (requires the server to be started separately) # * 'just' rspec: 'rspec' guard :rspec, cmd: "bundle exec rspec" do require "guard/rspec/dsl" dsl = Guard::RSpec::Dsl.new(self) # Feel free to open issues for suggestions and improvements # RSpec files rspec = dsl.rspec watch(rspec.spec_helper) { rspec.spec_dir } watch(rspec.spec_support) { rspec.spec_dir } watch(rspec.spec_files) # Ruby files ruby = dsl.ruby dsl.watch_spec_files_for(ruby.lib_files) end mdub-clamp-00bf589/LICENSE000066400000000000000000000020671515750124400151100ustar00rootroot00000000000000Copyright (c) 2010 Mike Williams Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. mdub-clamp-00bf589/README.md000066400000000000000000000351411515750124400153610ustar00rootroot00000000000000# Clamp [![Gem Version](https://badge.fury.io/rb/clamp.png)](http://badge.fury.io/rb/clamp) [![CI](https://github.com/mdub/clamp/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/mdub/clamp/actions/workflows/ci.yml) "Clamp" is a minimal framework for command-line utilities. It handles boring stuff like parsing the command-line, and generating help, so you can get on with making your command actually do stuff. ## Not another one! Yeah, sorry. There are a bunch of existing command-line parsing libraries out there, and Clamp draws inspiration from a variety of sources, including [Thor], [optparse], and [Clip]. In the end, though, I wanted a slightly rounder wheel. (Although, Clamp has a _lot_ in common with Ara T. Howard's [main.rb]. Had I been aware of that project at the time, I might not have written Clamp.) [optparse]: http://ruby-doc.org/stdlib/libdoc/optparse/rdoc/index.html [Thor]: http://github.com/wycats/thor [Clip]: http://clip.rubyforge.org/ [main.rb]: https://github.com/ahoward/main ## Quick Start A typical Clamp script looks like this: ```ruby require 'clamp' Clamp do option "--loud", :flag, "say it loud" option ["-n", "--iterations"], "N", "say it N times", default: 1 do |s| Integer(s) end parameter "WORDS ...", "the thing to say", attribute_name: :words def execute the_truth = words.join(" ") the_truth.upcase! if loud? iterations.times do puts the_truth end end end ``` Internally, Clamp models a command as a Ruby class (a subclass of `Clamp::Command`), and a command execution as an instance of that class. The example above is really just syntax-sugar for: ```ruby require 'clamp' class SpeakCommand < Clamp::Command option "--loud", :flag, "say it loud" option ["-n", "--iterations"], "N", "say it N times", default: 1 do |s| Integer(s) end parameter "WORDS ...", "the thing to say", attribute_name: :words def execute the_truth = words.join(" ") the_truth.upcase! if loud? iterations.times do puts the_truth end end end SpeakCommand.run ``` Class-level methods like `option` and `parameter` declare attributes, in a similar way to `attr_accessor`, and arrange for them to be populated automatically based on command-line arguments. They are also used to generate `help` documentation. There are more examples demonstrating various features of Clamp [on Github][examples]. [examples]: https://github.com/mdub/clamp/tree/master/examples ## Declaring options Options are declared using the `option` method. The three required arguments are: 1. the option switch (or switches), 2. an option argument name 3. a short description For example: ```ruby option "--flavour", "FLAVOUR", "ice-cream flavour" ``` It works a little like `attr_accessor`, defining reader and writer methods on the command class. The attribute name is inferred from the switch (in this case, "`flavour`"). When you pass options to your command, Clamp will populate the attributes, which are then available for use in your `#execute` method. ```ruby def execute puts "You chose #{flavour}. Excellent choice!" end ``` If you don't like the inferred attribute name, you can override it: ```ruby option "--type", "TYPE", "type of widget", attribute_name: :widget_type # to avoid clobbering Object#type ``` ### Short/long option switches The first argument to `option` can be an array, rather than a single string, in which case all the switches are treated as aliases: ```ruby option ["-s", "--subject"], "SUBJECT", "email subject line" ``` ### Flag options Some options are just boolean flags. Pass "`:flag`" as the second parameter to tell Clamp not to expect an option argument: ```ruby option "--verbose", :flag, "be chatty" ``` For flag options, Clamp appends "`?`" to the generated reader method; ie. you get a method called "`#verbose?`", rather than just "`#verbose`". Negatable flags are easy to generate, too: ```ruby option "--[no-]force", :flag, "be forceful (or not)" ``` Clamp will handle both "`--force`" and "`--no-force`" options, setting the value of "`#force?`" appropriately. ### Required options Although "required option" is an oxymoron, Clamp lets you mark an option as required, and will verify that a value is provided: ```ruby option "--password", "PASSWORD", "the secret password", required: true ``` Note that it makes no sense to mark a `:flag` option, or one with a `:default`, as `:required`. ### Multivalued options Declaring an option "`:multivalued`" allows it to be specified multiple times on the command line. ```ruby option "--format", "FORMAT", "output format", multivalued: true ``` The underlying attribute becomes an Array, and the suffix "`_list`" is appended to the default attribute name. In this case, an attribute called "`format_list`" would be generated (unless you override the default by specifying an `:attribute_name`). ### Hidden options Declaring an option "`:hidden`" will cause it to be hidden from `--help` output. ```ruby option "--some-option", "VALUE", "Just a little option", hidden: true ``` ### Version option A common idiom is to have an option `--version` that outputs the command version and doesn't run any subcommands. This can be achieved by: ```ruby option "--version", :flag, "Show version" do puts MyGem::VERSION exit(0) end ``` ## Declaring parameters Positional parameters can be declared using `parameter`, specifying 1. the parameter name, and 2. a short description For example: ```ruby parameter "SRC", "source file" ``` Like options, parameters are implemented as attributes of the command, with the default attribute name derived from the parameter name (in this case, "`src`"). By convention, parameter names are specified in uppercase, to make them obvious in usage help. ### Optional parameters Wrapping a parameter name in square brackets indicates that it's optional, e.g. ```ruby parameter "[TARGET_DIR]", "target directory" ``` ### Multivalued (aka "greedy") parameters Three dots at the end of a parameter name makes it "greedy" - it will consume all remaining command-line arguments. For example: ```ruby parameter "FILE ...", "input files", attribute_name: :files ``` Like multivalued options, greedy parameters are backed by an Array attribute (named with a "`_list`" suffix, by default). ## Parsing and validation of options and parameters When you `#run` a command, it will first attempt to `#parse` command-line arguments, and map them onto the declared options and parameters, before invoking your `#execute` method. Clamp will verify that all required (ie. non-optional) parameters are present, and signal a error if they aren't. ### Validation Both `option` and `parameter` accept an optional block. If present, the block will be called with the raw string argument, and is expected to validate it. The value returned by the block will be assigned to the underlying attribute, so it's also a good place to coerce the String to a different type, if appropriate. For example: ```ruby option "--port", "PORT", "port to listen on" do |s| Integer(s) end ``` If the block raises an ArgumentError, Clamp will catch it, and report that the value was bad: ```ruby !!!plain ERROR: option '--port': invalid value for Integer: "blah" ``` For multivalued options and parameters, the validation block will be called for each value specified. More complex validation, e.g. those involving multiple options/parameters, should be performed within the `#execute` method. Use `#signal_usage_error` to tell the user what they did wrong, e.g. ```ruby def execute if port < 1024 && user != 'root' signal_usage_error "port restricted for non-root users" end # ... carry on ... end ``` ### Advanced option/parameter handling While Clamp provides an attribute-writer method for each declared option or parameter, you always have the option of overriding it to provide custom argument-handling logic, e.g. ```ruby parameter "SERVER", "location of server" def server=(server) @server_address, @server_port = server.split(":") end ``` ### Default values Default values can be specified for options, and optional parameters: ```ruby option "--flavour", "FLAVOUR", "ice-cream flavour", default: "chocolate" parameter "[HOST]", "server host", default: "localhost" ``` For more advanced cases, you can also specify default values by defining a method called "`default_#{attribute_name}`": ```ruby option "--http-port", "PORT", "web-server port", default: 9000 option "--admin-port", "PORT", "admin port" def default_admin_port http_port + 1 end ``` ### Environment variable support Options (and optional parameters) can also be associated with environment variables: ```ruby option "--port", "PORT", "the port to listen on", environment_variable: "MYAPP_PORT" do |val| val.to_i end parameter "[HOST]", "server address", environment_variable: "MYAPP_HOST" ``` Clamp will check the specified envariables in the absence of values supplied on the command line, before looking for a default value. ### Allowing options after parameters By default, Clamp only recognises options _before_ positional parameters. Some other option-parsing libraries - notably [GNU `getopt(3)`](https://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html) - allow option and parameter arguments to appear in any order on the command-line, e.g. foobar --foo=bar something --fnord=snuffle another-thing If you want Clamp to allow options and parameters to be "interspersed" in this way, set: ```ruby Clamp.allow_options_after_parameters = true ``` ## Declaring Subcommands Subcommand support helps you wrap a number of related commands into a single script (ala tools like "`git`"). Clamp will inspect the first command-line argument (after options are parsed), and delegate to the named subcommand. Unsuprisingly, subcommands are declared using the `subcommand` method. e.g. ```ruby Clamp do subcommand "init", "Initialize the repository" do def execute # ... end end end ``` Clamp generates an anonymous subclass of the current class, to represent the subcommand. Alternatively, you can provide an explicit subcommand class: ```ruby class MainCommand < Clamp::Command subcommand "init", "Initialize the repository", InitCommand end class InitCommand < Clamp::Command def execute # ... end end ``` Like options, subcommands may have aliases: ```ruby Clamp do subcommand ["initialize", "init"], "Initialize the repository" do # ... end end ``` ### Default subcommand You can set a default subcommand, at the class level, as follows: ```ruby Clamp do self.default_subcommand = "status" subcommand "status", "Display current status" do def execute # ... end end end ``` Then, if when no SUBCOMMAND argument is provided, the default will be selected. ### Subcommand options and parameters Options are inheritable, so any options declared for a command are supported by it's sub-classes (e.g. those created using the block form of `subcommand`). Parameters, on the other hand, are not inherited - each subcommand must declare it's own parameter list. Note that, if a subcommand accepts options, they must be specified on the command-line _after_ the subcommand name. You can define a `subcommand_missing` method that is called when user tries to run an unknown subcommand: ```ruby Clamp do def subcommand_missing(name) if name == "foo" return Object.const_get(:FooPlugin) if Object.const_defined?(:FooPlugin) abort "Subcommand 'foo' requires plugin X" end end end ``` ## Getting help All Clamp commands support a "`--help`" option, which outputs brief usage documentation, based on those seemingly useless extra parameters that you had to pass to `option` and `parameter`. ```sh $ speak --help Usage: speak [OPTIONS] WORDS ... Arguments: WORDS ... the thing to say Options: --loud say it loud -n, --iterations N say it N times (default: 1) -h, --help print help ``` ## Shell completion Clamp can generate shell completion scripts for bash, zsh, and fish. This is an opt-in feature: ```ruby require 'clamp/completion' ``` This adds a hidden `--shell-completions` option to all commands. Use it to generate a completion script: ```sh $ myapp --shell-completions bash # or: zsh, fish ``` ### Activating completions For **bash**, add to your `~/.bashrc`: ```sh eval "$(myapp --shell-completions bash)" ``` For **zsh**, add to your `~/.zshrc`: ```sh eval "$(myapp --shell-completions zsh)" ``` For **fish**, add to your `~/.config/fish/config.fish`: ```sh myapp --shell-completions fish | source ``` ### Programmatic API You can also generate completion scripts programmatically: ```ruby script = MyCommand.generate_completion(:fish, "myapp") ``` This returns the completion script as a string, which you can write to a file or use however you like. ## Localization Clamp comes with support for overriding strings with custom translations. You can use localization library of your choice and override the strings at startup. Example usage: ```ruby require 'gettext' Clamp.messages = { too_many_arguments: _("too many arguments"), option_required: _("option '%