clamp-1.3.3/ 0000755 0000041 0000041 00000000000 15045240442 012642 5 ustar www-data www-data clamp-1.3.3/.autotest 0000644 0000041 0000041 00000000361 15045240442 014513 0 ustar www-data www-data require "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
clamp-1.3.3/.editorconfig 0000644 0000041 0000041 00000000251 15045240442 015315 0 ustar www-data www-data root = 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
clamp-1.3.3/.gitignore 0000644 0000041 0000041 00000000121 15045240442 014624 0 ustar www-data www-data *.gem
.bundle
.markdownlint*
.rvmrc
.ruby-version
.yardoc
doc
pkg/*
Gemfile.lock
clamp-1.3.3/CHANGES.md 0000644 0000041 0000041 00000003665 15045240442 014246 0 ustar www-data www-data # Changelog
## 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.
clamp-1.3.3/clamp.gemspec 0000644 0000041 0000041 00000001505 15045240442 015304 0 ustar www-data www-data # frozen_string_literal: true
$LOAD_PATH.push File.expand_path("lib", __dir__)
require "clamp/version"
Gem::Specification.new do |s|
s.name = "clamp"
s.version = Clamp::VERSION.dup
s.platform = Gem::Platform::RUBY
s.authors = ["Mike Williams"]
s.email = "mdub@dogbiscuit.org"
s.homepage = "https://github.com/mdub/clamp"
s.license = "MIT"
s.summary = "a minimal framework for command-line utilities"
s.description = <<-TEXT.gsub(/^\s+/, "")
Clamp provides an object-model for command-line utilities.
It handles parsing of command-line options, and generation of usage help.
TEXT
s.files = `git ls-files`.split("\n")
s.require_paths = ["lib"]
s.required_ruby_version = ">= 2.5", "< 4"
s.metadata["rubygems_mfa_required"] = "true"
end
clamp-1.3.3/Guardfile 0000644 0000041 0000041 00000002767 15045240442 014503 0 ustar www-data www-data # 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
clamp-1.3.3/lib/ 0000755 0000041 0000041 00000000000 15045240442 013410 5 ustar www-data www-data clamp-1.3.3/lib/clamp/ 0000755 0000041 0000041 00000000000 15045240442 014504 5 ustar www-data www-data clamp-1.3.3/lib/clamp/messages.rb 0000644 0000041 0000041 00000002575 15045240442 016651 0 ustar www-data www-data # frozen_string_literal: true
module Clamp # :nodoc:
# Message lookup, to allow localization.
#
module Messages
def messages=(new_messages)
messages.merge!(new_messages)
end
def message(key, options = {})
string = messages.fetch(key)
return string if options.empty?
format string, options
end
def clear_messages!
init_default_messages
end
DEFAULTS = {
too_many_arguments: "too many arguments",
option_required: "option '%s' is required",
option_or_env_required: "option '% s' (or env %s) is required",
option_argument_error: "option '%s': %s",
parameter_argument_error: "parameter '% s': %s",
env_argument_error: "$%s: %s",
unrecognised_option: "Unrecognised option '%s'",
no_such_subcommand: "No such sub-command '%s'",
no_value_provided: "no value provided",
default: "default",
or: "or",
required: "required",
usage_heading: "Usage",
parameters_heading: "Parameters",
subcommands_heading: "Subcommands",
options_heading: "Options"
}.freeze
private
def messages
init_default_messages unless defined?(@messages)
@messages
end
def init_default_messages
@messages = DEFAULTS.dup
end
end
extend Messages
end
clamp-1.3.3/lib/clamp/truthy.rb 0000644 0000041 0000041 00000000273 15045240442 016372 0 ustar www-data www-data # frozen_string_literal: true
module Clamp # :nodoc:
TRUTHY_VALUES = %w[1 yes enable on true].freeze
def self.truthy?(arg)
TRUTHY_VALUES.include?(arg.to_s.downcase)
end
end
clamp-1.3.3/lib/clamp/subcommand/ 0000755 0000041 0000041 00000000000 15045240442 016634 5 ustar www-data www-data clamp-1.3.3/lib/clamp/subcommand/parsing.rb 0000644 0000041 0000041 00000000676 15045240442 020635 0 ustar www-data www-data # frozen_string_literal: true
require "clamp/subcommand/execution"
module Clamp
module Subcommand
# Subcommand parsing methods.
#
module Parsing
protected
def parse_subcommand
return false unless self.class.has_subcommands?
extend(Subcommand::Execution)
end
private
def default_subcommand_name
self.class.default_subcommand || request_help
end
end
end
end
clamp-1.3.3/lib/clamp/subcommand/definition.rb 0000644 0000041 0000041 00000000622 15045240442 021311 0 ustar www-data www-data # frozen_string_literal: true
module Clamp
module Subcommand
Definition = Struct.new(:names, :description, :subcommand_class) do
def initialize(names, description, subcommand_class)
names = Array(names)
super
end
def is_called?(name)
names.member?(name)
end
def help
[names.join(", "), description]
end
end
end
end
clamp-1.3.3/lib/clamp/subcommand/execution.rb 0000644 0000041 0000041 00000002530 15045240442 021164 0 ustar www-data www-data # frozen_string_literal: true
module Clamp
module Subcommand
# Support for subcommand execution.
#
# This module is mixed into command instances that have subcommands, overriding
# default behaviour in {Clamp::Command}.
#
module Execution
# override default Command behaviour
def execute
# delegate to subcommand
subcommand = instantiate_subcommand(subcommand_name)
subcommand.run(subcommand_arguments)
end
private
def instantiate_subcommand(name)
subcommand_class = find_subcommand_class(name)
subcommand = subcommand_class.new(invocation_path_for(name), context)
self.class.inheritable_attributes.each do |attribute|
next unless attribute.of(self).defined?
attribute.of(subcommand).set(attribute.of(self).get)
end
subcommand
end
def invocation_path_for(name)
param_names = self.class.parameters.select(&:inheritable?).map(&:name)
[invocation_path, *param_names, name].join(" ")
end
def find_subcommand_class(name)
subcommand_def = self.class.find_subcommand(name)
return subcommand_def.subcommand_class if subcommand_def
subcommand_missing(name)
end
def verify_required_options_are_set
# not required
end
end
end
end
clamp-1.3.3/lib/clamp/subcommand/declaration.rb 0000644 0000041 0000041 00000004633 15045240442 021454 0 ustar www-data www-data # frozen_string_literal: true
require "clamp/errors"
require "clamp/subcommand/definition"
module Clamp
module Subcommand
# Subcommand declaration methods.
#
module Declaration
def recognised_subcommands
@recognised_subcommands ||= []
end
def subcommand(name, description, subcommand_class = self, &block)
subcommand_class = Class.new(subcommand_class, &block) if block
declare_subcommand_parameters unless has_subcommands?
recognised_subcommands << Subcommand::Definition.new(name, description, subcommand_class)
end
def has_subcommands?
!recognised_subcommands.empty?
end
def find_subcommand(name)
recognised_subcommands.find { |sc| sc.is_called?(name) }
end
def find_subcommand_class(*names)
names.inject(self) do |command_class, name|
return nil unless command_class
subcommand = command_class.find_subcommand(name)
subcommand&.subcommand_class
end
end
def inheritable_attributes
recognised_options + inheritable_parameters
end
def default_subcommand=(name)
raise Clamp::DeclarationError, "default_subcommand must be defined before subcommands" if has_subcommands?
@default_subcommand = name
end
def default_subcommand(*args, &block)
if args.empty?
@default_subcommand ||= false
else
$stderr.puts "WARNING: Clamp default_subcommand syntax has changed; check the README."
$stderr.puts " (from #{caller(1..1).first})"
self.default_subcommand = args.first
subcommand(*args, &block)
end
end
private
def declare_subcommand_parameters
if default_subcommand
parameter "[SUBCOMMAND]", "subcommand",
attribute_name: :subcommand_name,
default: default_subcommand,
inheritable: false
else
parameter "SUBCOMMAND", "subcommand",
attribute_name: :subcommand_name,
required: false,
inheritable: false
end
remove_method :default_subcommand_name if method_defined?(:default_subcommand_name)
parameter "[ARG] ...", "subcommand arguments",
attribute_name: :subcommand_arguments,
inheritable: false
end
end
end
end
clamp-1.3.3/lib/clamp/option/ 0000755 0000041 0000041 00000000000 15045240442 016014 5 ustar www-data www-data clamp-1.3.3/lib/clamp/option/parsing.rb 0000644 0000041 0000041 00000004520 15045240442 020005 0 ustar www-data www-data # frozen_string_literal: true
module Clamp # :nodoc:
class << self
attr_accessor :allow_options_after_parameters
end
module Option
# Option parsing methods.
#
module Parsing
protected
def parse_options
set_options_from_command_line
default_options_from_environment
end
private
def set_options_from_command_line
argument_buffer = []
argument_buffer_limit = self.class.parameter_buffer_limit
until remaining_arguments.empty?
unless remaining_arguments.first.start_with?("-")
break unless argument_buffer.size < argument_buffer_limit
argument_buffer << remaining_arguments.shift
next
end
switch = remaining_arguments.shift
break if switch == "--"
handle_switch(switch)
end
remaining_arguments.unshift(*argument_buffer)
end
def handle_switch(switch)
switch = split_trailing_switches(switch)
option = find_option(switch)
begin
value = option.extract_value(switch, remaining_arguments)
option.of(self).take(value)
rescue ArgumentError => e
signal_usage_error Clamp.message(:option_argument_error, switch: switch, message: e.message)
end
end
def split_trailing_switches(switch)
case switch
when /\A(-\w)(.+)\z/m # combined short options
switch = Regexp.last_match(1)
if find_option(switch).flag?
remaining_arguments.unshift("-#{Regexp.last_match(2)}")
else
remaining_arguments.unshift(Regexp.last_match(2))
end
when /\A(--[^=]+)=(.*)\z/m
switch = Regexp.last_match(1)
remaining_arguments.unshift(Regexp.last_match(2))
end
switch
end
def default_options_from_environment
self.class.recognised_options.each do |option|
option.of(self).default_from_environment
end
end
def verify_required_options_are_set
self.class.recognised_options.each do |option|
option.of(self).verify_not_missing
end
end
def find_option(switch)
self.class.find_option(switch) ||
signal_usage_error(Clamp.message(:unrecognised_option, switch: switch))
end
end
end
end
clamp-1.3.3/lib/clamp/option/definition.rb 0000644 0000041 0000041 00000004606 15045240442 020477 0 ustar www-data www-data # frozen_string_literal: true
require "clamp/attribute/definition"
require "clamp/truthy"
module Clamp
module Option
# Represents an option of a Clamp::Command class.
#
class Definition < Attribute::Definition
def initialize(switches, type, description, options = {})
@switches = Array(switches)
@type = type
@description = description
super(options)
@multivalued = options[:multivalued]
return unless options.key?(:required)
@required = options[:required]
# Do some light validation for conflicting settings.
raise ArgumentError, "Specifying a :default value with :required doesn't make sense" if options.key?(:default)
raise ArgumentError, "A required flag (boolean) doesn't make sense." if type == :flag
end
attr_reader :switches, :type
def long_switch
switches.find { |switch| switch =~ /^--/ }
end
def handles?(switch)
recognised_switches.member?(switch)
end
def flag?
@type == :flag
end
def flag_set?(switch)
!(switch =~ /^--no-(.*)/ && switches.member?("--[no-]#{Regexp.last_match(1)}"))
end
def read_method
if flag?
"#{super}?"
else
super
end
end
def extract_value(switch, arguments)
if flag?
flag_set?(switch)
else
raise ArgumentError, Clamp.message(:no_value_provided) if arguments.empty?
arguments.shift
end
end
def default_conversion_block
Clamp.method(:truthy?) if flag?
end
def help_lhs
lhs = switches.join(", ")
lhs += " #{type}" unless flag?
lhs
end
private
def recognised_switches
switches.map do |switch|
if switch =~ /^--\[no-\](.*)/
["--#{Regexp.last_match(1)}", "--no-#{Regexp.last_match(1)}"]
else
switch
end
end.flatten
end
def infer_attribute_name
raise Clamp::DeclarationError, "You must specify either a long-switch or an :attribute_value" unless long_switch
inferred_name = long_switch.sub(/^--(\[no-\])?/, "").tr("-", "_")
inferred_name += "_list" if multivalued?
inferred_name
end
def required_indicator
Clamp.message(:required) if required?
end
end
end
end
clamp-1.3.3/lib/clamp/option/declaration.rb 0000644 0000041 0000041 00000003117 15045240442 020630 0 ustar www-data www-data # frozen_string_literal: true
require "clamp/attribute/declaration"
require "clamp/option/definition"
module Clamp
module Option
# Option declaration methods.
#
module Declaration
include Clamp::Attribute::Declaration
def option(switches, type, description, opts = {}, &block)
Option::Definition.new(switches, type, description, opts).tap do |option|
block ||= option.default_conversion_block
define_accessors_for(option, &block)
declared_options << option
end
end
def find_option(switch)
recognised_options.find { |o| o.handles?(switch) }
end
def declared_options
@declared_options ||= []
end
def recognised_options
unless @implicit_options_declared ||= false
declare_implicit_help_option
@implicit_options_declared = true
end
effective_options
end
private
def declare_implicit_help_option
return false if effective_options.find { |o| o.handles?("--help") }
help_switches = ["--help"]
help_switches.unshift("-h") unless effective_options.find { |o| o.handles?("-h") }
option help_switches, :flag, "print help" do
request_help
end
end
def effective_options
ancestors.inject([]) do |options, ancestor|
options + options_declared_on(ancestor)
end
end
def options_declared_on(ancestor)
return [] unless ancestor.is_a?(Clamp::Option::Declaration)
ancestor.declared_options
end
end
end
end
clamp-1.3.3/lib/clamp/command.rb 0000644 0000041 0000041 00000010351 15045240442 016447 0 ustar www-data www-data # frozen_string_literal: true
require "clamp/messages"
require "clamp/errors"
require "clamp/help"
require "clamp/option/declaration"
require "clamp/option/parsing"
require "clamp/parameter/declaration"
require "clamp/parameter/parsing"
require "clamp/subcommand/declaration"
require "clamp/subcommand/parsing"
module Clamp
# {Command} models a shell command. Each command invocation is a new object.
# Command options and parameters are represented as attributes
# (see {Command::Declaration}).
#
# The main entry-point is {#run}, which uses {#parse} to populate attributes based
# on an array of command-line arguments, then calls {#execute} (which you provide)
# to make it go.
#
class Command
# Create a command execution.
#
# @param [String] invocation_path the path used to invoke the command
# @param [Hash] context additional data the command may need
#
def initialize(invocation_path, context = {})
@invocation_path = invocation_path
@context = context
end
# @return [String] the path used to invoke this command
#
attr_reader :invocation_path
# @return [Array] unconsumed command-line arguments
#
attr_reader :remaining_arguments
# Parse command-line arguments.
#
# @param [Array] arguments command-line arguments
# @return [Array] unconsumed arguments
#
def parse(arguments)
@remaining_arguments = arguments.dup
parse_options
parse_parameters
parse_subcommand
verify_required_options_are_set
handle_remaining_arguments
end
# Run the command, with the specified arguments.
#
# This calls {#parse} to process the command-line arguments,
# then delegates to {#execute}.
#
# @param [Array] arguments command-line arguments
#
def run(arguments)
parse(arguments)
execute
end
# Execute the command (assuming that all options/parameters have been set).
#
# This method is designed to be overridden in sub-classes.
#
def execute
raise "you need to define #execute"
end
# @return [String] usage documentation for this command
#
def help
self.class.help(invocation_path)
end
# Abort with subcommand missing usage error
#
# @ param [String] name subcommand_name
def subcommand_missing(name)
signal_usage_error(Clamp.message(:no_such_subcommand, name: name))
end
include Clamp::Option::Parsing
include Clamp::Parameter::Parsing
include Clamp::Subcommand::Parsing
protected
attr_accessor :context
def handle_remaining_arguments
signal_usage_error Clamp.message(:too_many_arguments) unless remaining_arguments.empty?
end
private
def signal_usage_error(message)
e = UsageError.new(message, self)
e.set_backtrace(caller)
raise e
end
def signal_error(message, options = {})
status = options.fetch(:status, 1)
e = ExecutionError.new(message, self, status)
e.set_backtrace(caller)
raise e
end
def request_help
raise HelpWanted, self
end
class << self
include Clamp::Option::Declaration
include Clamp::Parameter::Declaration
include Clamp::Subcommand::Declaration
include Help
# An alternative to "def execute"
def execute(&block)
define_method(:execute, &block)
end
# Create an instance of this command class, and run it.
#
# @param [String] invocation_path the path used to invoke the command
# @param [Array] arguments command-line arguments
# @param [Hash] context additional data the command may need
#
def run(invocation_path = File.basename($PROGRAM_NAME), arguments = ARGV, context = {})
new(invocation_path, context).run(arguments)
rescue Clamp::UsageError => e
$stderr.puts "ERROR: #{e.message}"
$stderr.puts ""
$stderr.puts "See: '#{e.command.invocation_path} --help'"
exit(1)
rescue Clamp::HelpWanted => e
puts e.command.help
rescue Clamp::ExecutionError => e
$stderr.puts "ERROR: #{e.message}"
exit(e.status)
rescue SignalException => e
exit(128 + e.signo)
end
end
end
end
clamp-1.3.3/lib/clamp/help.rb 0000644 0000041 0000041 00000005351 15045240442 015765 0 ustar www-data www-data # frozen_string_literal: true
require "stringio"
require "clamp/messages"
module Clamp
# Command help generation.
#
module Help
def usage(usage)
@declared_usage_descriptions ||= []
@declared_usage_descriptions << usage
end
attr_reader :declared_usage_descriptions, :description
def description=(description)
@description = description.dup
if @description =~ /^\A\n*( +)/
indent = Regexp.last_match(1)
@description.gsub!(/^#{indent}/, "")
end
@description.strip!
end
def banner(description)
self.description = description
end
def derived_usage_description
parts = ["[OPTIONS]"]
parts += parameters.map(&:name)
parts.join(" ")
end
def usage_descriptions
declared_usage_descriptions || [derived_usage_description]
end
def help(invocation_path, builder = Builder.new)
help = builder
help.add_usage(invocation_path, usage_descriptions)
help.add_description(description)
help.add_list(Clamp.message(:parameters_heading), parameters) if has_parameters?
help.add_list(Clamp.message(:subcommands_heading), recognised_subcommands) if has_subcommands?
help.add_list(Clamp.message(:options_heading), recognised_options)
help.string
end
# A builder for auto-generated help.
#
class Builder
def initialize
@lines = []
end
def string
left_column_width = lines.grep(Array).map(&:first).map(&:size).max
StringIO.new.tap do |out|
lines.each do |line|
case line
when Array
line[0] = line[0].ljust(left_column_width)
line.unshift("")
out.puts(line.join(" "))
else
out.puts(line)
end
end
end.string
end
def line(text = "")
@lines << text
end
def row(lhs, rhs)
@lines << [lhs, rhs]
end
def add_usage(invocation_path, usage_descriptions)
line "#{Clamp.message(:usage_heading)}:"
usage_descriptions.each do |usage|
line " #{invocation_path} #{usage}".rstrip
end
end
def add_description(description)
return unless description
line
line description.gsub(/^/, " ")
end
DETAIL_FORMAT = " %-29s %s"
def add_list(heading, items)
line
line "#{heading}:"
items.reject { |i| i.respond_to?(:hidden?) && i.hidden? }.each do |item|
label, description = item.help
description.each_line do |line|
row(label, line)
label = ""
end
end
end
private
attr_accessor :lines
end
end
end
clamp-1.3.3/lib/clamp/parameter/ 0000755 0000041 0000041 00000000000 15045240442 016464 5 ustar www-data www-data clamp-1.3.3/lib/clamp/parameter/parsing.rb 0000644 0000041 0000041 00000001527 15045240442 020461 0 ustar www-data www-data # frozen_string_literal: true
module Clamp
module Parameter
# Parameter parsing methods.
#
module Parsing
protected
def parse_parameters
set_parameters_from_command_line
default_parameters_from_environment
end
private
def set_parameters_from_command_line
self.class.parameters.each do |parameter|
parameter.consume(remaining_arguments).each do |value|
parameter.of(self).take(value)
end
rescue ArgumentError => e
signal_usage_error Clamp.message(:parameter_argument_error, param: parameter.name, message: e.message)
end
end
def default_parameters_from_environment
self.class.parameters.each do |parameter|
parameter.of(self).default_from_environment
end
end
end
end
end
clamp-1.3.3/lib/clamp/parameter/definition.rb 0000644 0000041 0000041 00000002647 15045240442 021152 0 ustar www-data www-data # frozen_string_literal: true
require "clamp/attribute/definition"
module Clamp
module Parameter
# Represents an parameter of a Clamp::Command class.
#
class Definition < Attribute::Definition
def initialize(name, description, options = {})
@name = name
@description = description
super(options)
@multivalued = (@name =~ ELLIPSIS_SUFFIX)
@required = options.fetch(:required) do
(@name !~ OPTIONAL)
end
@inheritable = options.fetch(:inheritable, true)
end
attr_reader :name
def inheritable?
@inheritable
end
def help_lhs
name
end
def consume(arguments)
raise ArgumentError, Clamp.message(:no_value_provided) if required? && arguments.empty?
arguments.shift(multivalued? ? arguments.length : 1)
end
ELLIPSIS_SUFFIX = / \.\.\.$/.freeze
OPTIONAL = /^\[(.*)\]/.freeze
VALID_ATTRIBUTE_NAME = /^[a-z0-9_]+$/.freeze
private
def infer_attribute_name
inferred_name = name.downcase.tr("-", "_").sub(ELLIPSIS_SUFFIX, "").sub(OPTIONAL) { Regexp.last_match(1) }
raise "cannot infer attribute_name from #{name.inspect}" unless inferred_name.match? VALID_ATTRIBUTE_NAME
inferred_name += "_list" if multivalued?
inferred_name
end
def required_indicator
# implied by LHS
end
end
end
end
clamp-1.3.3/lib/clamp/parameter/declaration.rb 0000644 0000041 0000041 00000002232 15045240442 021275 0 ustar www-data www-data # frozen_string_literal: true
require "clamp/attribute/declaration"
require "clamp/parameter/definition"
module Clamp
module Parameter
# Parameter declaration methods.
#
module Declaration
include Clamp::Attribute::Declaration
def parameters
@parameters ||= []
end
def has_parameters?
!parameters.empty?
end
def parameter(name, description, options = {}, &block)
Parameter::Definition.new(name, description, options).tap do |parameter|
define_accessors_for(parameter, &block)
parameters << parameter
end
end
def inheritable_parameters
superclass_inheritable_parameters + parameters.select(&:inheritable?)
end
def parameter_buffer_limit
return 0 unless Clamp.allow_options_after_parameters
return Float::INFINITY if inheritable_parameters.any?(&:multivalued?)
inheritable_parameters.size
end
private
def superclass_inheritable_parameters
return [] unless superclass.respond_to?(:inheritable_parameters, true)
superclass.inheritable_parameters
end
end
end
end
clamp-1.3.3/lib/clamp/version.rb 0000644 0000041 0000041 00000000104 15045240442 016511 0 ustar www-data www-data # frozen_string_literal: true
module Clamp
VERSION = "1.3.3"
end
clamp-1.3.3/lib/clamp/attribute/ 0000755 0000041 0000041 00000000000 15045240442 016507 5 ustar www-data www-data clamp-1.3.3/lib/clamp/attribute/definition.rb 0000644 0000041 0000041 00000004556 15045240442 021176 0 ustar www-data www-data # frozen_string_literal: true
require "clamp/attribute/instance"
module Clamp
module Attribute
# Represents an attribute of a Clamp::Command class.
#
class Definition
def initialize(options)
@attribute_name = options[:attribute_name].to_s if options.key?(:attribute_name)
@default_value = options[:default] if options.key?(:default)
@environment_variable = options[:environment_variable] if options.key?(:environment_variable)
@hidden = options[:hidden] if options.key?(:hidden)
end
attr_reader :description, :environment_variable
def help_rhs
rhs = description
comments = [required_indicator, default_description].compact
rhs += " (#{comments.join(', ')})" unless comments.empty?
rhs
end
def help
[help_lhs, help_rhs]
end
def ivar_name
"@#{attribute_name}"
end
def read_method
attribute_name
end
def default_method
"default_#{read_method}"
end
def write_method
"#{attribute_name}="
end
def append_method
"append_to_#{attribute_name}" if multivalued?
end
def multivalued?
@multivalued
end
def required?
@required ||= false
end
def hidden?
@hidden ||= false
end
def attribute_name
@attribute_name ||= infer_attribute_name
end
def default_value
if defined?(@default_value)
@default_value
elsif multivalued?
[]
end
end
def of(command)
Attribute::Instance.new(self, command)
end
def option_missing_message
if environment_variable
Clamp.message(:option_or_env_required,
option: switches.first,
env: environment_variable)
else
Clamp.message(:option_required,
option: switches.first)
end
end
private
def default_description
default_sources = [
("$#{@environment_variable}" if defined?(@environment_variable)),
(@default_value.inspect if defined?(@default_value))
].compact
return nil if default_sources.empty?
"#{Clamp.message(:default)}: " + default_sources.join(", #{Clamp.message(:or)} ")
end
end
end
end
clamp-1.3.3/lib/clamp/attribute/instance.rb 0000644 0000041 0000041 00000004630 15045240442 020643 0 ustar www-data www-data # frozen_string_literal: true
module Clamp
module Attribute
# Represents an attribute of a Clamp::Command instance.
#
class Instance
def initialize(attribute, command)
@attribute = attribute
@command = command
end
attr_reader :attribute, :command
def defined?
command.instance_variable_defined?(attribute.ivar_name)
end
# get value directly
def get
command.instance_variable_get(attribute.ivar_name)
end
# set value directly
def set(value)
command.instance_variable_set(attribute.ivar_name, value)
end
def default
command.send(attribute.default_method) if command.respond_to?(attribute.default_method, true)
end
# default implementation of read_method
def _read
set(default) unless self.defined?
get
end
# default implementation of append_method
def _append(value)
current_values = get || []
set(current_values + [value])
end
# default implementation of write_method for multi-valued attributes
def _replace(values)
set([])
Array(values).each { |value| take(value) }
end
def read
command.send(attribute.read_method)
end
def take(value)
if attribute.multivalued?
command.send(attribute.append_method, value)
else
command.send(attribute.write_method, value)
end
end
def signal_usage_error(*args)
command.send(:signal_usage_error, *args)
end
def default_from_environment
return if self.defined?
return if attribute.environment_variable.nil?
return unless ENV.key?(attribute.environment_variable)
# Set the parameter value if it's environment variable is present
value = ENV.fetch(attribute.environment_variable, nil)
begin
take(value)
rescue ArgumentError => e
signal_usage_error Clamp.message(:env_argument_error, env: attribute.environment_variable, message: e.message)
end
end
def unset?
if attribute.multivalued?
read.empty?
else
read.nil?
end
end
def missing?
attribute.required? && unset?
end
def verify_not_missing
signal_usage_error attribute.option_missing_message if missing?
end
end
end
end
clamp-1.3.3/lib/clamp/attribute/declaration.rb 0000644 0000041 0000041 00000002752 15045240442 021327 0 ustar www-data www-data # frozen_string_literal: true
module Clamp
module Attribute
# Methods to generate attribute accessors.
#
module Declaration
protected
def define_accessors_for(attribute, &block)
define_reader_for(attribute)
define_default_for(attribute)
if attribute.multivalued?
define_appender_for(attribute, &block)
define_multi_writer_for(attribute)
else
define_simple_writer_for(attribute, &block)
end
end
private
def define_reader_for(attribute)
define_method(attribute.read_method) do
attribute.of(self)._read
end
end
def define_default_for(attribute)
return false if attribute.default_value.nil?
define_method(attribute.default_method) do
attribute.default_value
end
end
def define_simple_writer_for(attribute, &block)
define_method(attribute.write_method) do |value|
value = instance_exec(value, &block) if block
attribute.of(self).set(value)
end
end
def define_appender_for(attribute, &block)
define_method(attribute.append_method) do |value|
value = instance_exec(value, &block) if block
attribute.of(self)._append(value)
end
end
def define_multi_writer_for(attribute)
define_method(attribute.write_method) do |values|
attribute.of(self)._replace(values)
end
end
end
end
end
clamp-1.3.3/lib/clamp/errors.rb 0000644 0000041 0000041 00000001456 15045240442 016353 0 ustar www-data www-data # frozen_string_literal: true
module Clamp
# raised to indicate invalid option/parameter declaration
class DeclarationError < StandardError
end
# abstract command runtime error
class RuntimeError < StandardError
def initialize(message, command)
super(message)
@command = command
end
attr_reader :command
end
# raised to signal incorrect command usage
class UsageError < RuntimeError; end
# raised to request usage help
class HelpWanted < RuntimeError
def initialize(command)
super("I need help", command)
end
end
# raised to signal error during execution
class ExecutionError < RuntimeError
def initialize(message, command, status = 1)
super(message, command)
@status = status
end
attr_reader :status
end
end
clamp-1.3.3/lib/clamp.rb 0000644 0000041 0000041 00000000263 15045240442 015032 0 ustar www-data www-data # frozen_string_literal: true
require "clamp/version"
require "clamp/command"
def Clamp(&block) # rubocop:disable Naming/MethodName
Class.new(Clamp::Command, &block).run
end
clamp-1.3.3/spec/ 0000755 0000041 0000041 00000000000 15045240442 013574 5 ustar www-data www-data clamp-1.3.3/spec/spec_helper.rb 0000644 0000041 0000041 00000001616 15045240442 016416 0 ustar www-data www-data # frozen_string_literal: true
require "rspec"
require "clamp"
require "stringio"
require "pry-byebug"
RSpec.configure do |config|
config.around do |example|
example.run
rescue SystemExit => e
raise "Unexpected exit with status #{e.status}"
end
end
module OutputCapture
def self.included(target)
target.before do
$stdout = @out = StringIO.new
$stderr = @err = StringIO.new
end
target.after do
$stdout = STDOUT
$stderr = STDERR
end
end
def stdout
@out.string
end
def stderr
@err.string
end
end
module CommandFactory
def given_command(name, &block)
let(:command_class) do
Class.new(Clamp::Command, &block)
end
let(:command) do
command_class.new(name)
end
end
end
module SetEnv
def set_env(name, value)
if value
ENV[name] = value
else
ENV.delete(name)
end
end
end
clamp-1.3.3/spec/clamp/ 0000755 0000041 0000041 00000000000 15045240442 014670 5 ustar www-data www-data clamp-1.3.3/spec/clamp/help/ 0000755 0000041 0000041 00000000000 15045240442 015620 5 ustar www-data www-data clamp-1.3.3/spec/clamp/help/builder_spec.rb 0000644 0000041 0000041 00000002603 15045240442 020606 0 ustar www-data www-data # frozen_string_literal: true
require "spec_helper"
describe Clamp::Help::Builder do
subject(:builder) { described_class.new }
def output
builder.string
end
describe "#line" do
it "adds a line of text" do
builder.line("blah")
expect(output).to eq("blah\n")
end
end
describe "#row" do
it "adds two strings separated by spaces" do
builder.row("LHS", "RHS")
expect(output).to eq(" LHS RHS\n")
end
end
context "with multiple rows" do
before do
builder.row("foo", "bar")
builder.row("flibble", "blurk")
builder.row("x", "y")
end
let(:expected_output) do
[
" foo bar\n",
" flibble blurk\n",
" x y\n"
]
end
it "arranges them in two columns" do
expect(output.lines).to eq expected_output
end
end
context "with a mixture of lines and rows" do
before do
builder.line("ABCDEFGHIJKLMNOP")
builder.row("flibble", "blurk")
builder.line("Another section heading")
builder.row("x", "y")
end
let(:expected_output) do
[
"ABCDEFGHIJKLMNOP\n",
" flibble blurk\n",
"Another section heading\n",
" x y\n"
]
end
it "still arranges them in two columns" do
expect(output.lines).to eq expected_output
end
end
end
clamp-1.3.3/spec/clamp/command_group_spec.rb 0000644 0000041 0000041 00000021327 15045240442 021066 0 ustar www-data www-data # frozen_string_literal: true
require "spec_helper"
describe Clamp::Command do
extend CommandFactory
include OutputCapture
context "with subcommands" do
given_command "flipflop" do
def execute
puts message
end
subcommand "flip", "flip it" do
def message
"FLIPPED"
end
end
subcommand "flop", "flop it\nfor extra flop" do
def message
"FLOPPED"
end
end
end
describe "flip command" do
before do
command.run(["flip"])
end
it "delegates to sub-commands" do
expect(stdout).to match(/FLIPPED/)
end
end
describe "flop command" do
before do
command.run(["flop"])
end
it "delegates to sub-commands" do
expect(stdout).to match(/FLOPPED/)
end
end
context "when executed with no subcommand" do
it "triggers help" do
expect do
command.run([])
end.to raise_error(Clamp::HelpWanted)
end
end
describe "#help" do
it "shows subcommand parameters in usage" do
expect(command.help).to include("flipflop [OPTIONS] SUBCOMMAND [ARG] ...")
end
it "lists subcommands" do
expect(command.help).to match(/Subcommands:\n +flip +flip it\n +flop +flop it/)
end
it "handles new lines in subcommand descriptions" do
expect(command.help).to match(/flop +flop it\n +for extra flop/)
end
end
describe ".find_subcommand_class" do
it "finds subcommand classes" do
flip_class = command_class.find_subcommand_class("flip")
expect(flip_class.new("xx").message).to eq("FLIPPED")
end
end
end
context "with an aliased subcommand" do
given_command "blah" do
subcommand ["say", "talk"], "Say something" do
parameter "WORD ...", "stuff to say"
def execute
puts word_list
end
end
end
describe "the first alias" do
before do
command.run(["say", "boo"])
end
it "responds to it" do
expect(stdout).to match(/boo/)
end
end
describe "the second alias" do
before do
command.run(["talk", "jive"])
end
it "responds to it" do
expect(stdout).to match(/jive/)
end
end
describe "#help" do
it "lists all aliases" do
help = command.help
expect(help).to match(/say, talk .* Say something/)
end
end
end
context "with nested subcommands" do
given_command "fubar" do
subcommand "foo", "Foo!" do
subcommand "bar", "Baaaa!" do
def self.this_is_bar; end
def execute
puts "FUBAR"
end
end
end
end
it "delegates multiple levels" do
command.run(["foo", "bar"])
expect(stdout).to match(/FUBAR/)
end
describe ".find_subcommand_class" do
it "finds nested subcommands" do
expect(command_class.find_subcommand_class("foo", "bar")).to respond_to(:this_is_bar)
end
end
end
context "with a default subcommand" do
given_command "admin" do
self.default_subcommand = "status"
subcommand "status", "Show status" do
def execute
puts "All good!"
end
end
end
context "when executed with no subcommand" do
it "invokes the default subcommand" do
command.run([])
expect(stdout).to match(/All good/)
end
end
end
context "with a default subcommand, declared the old way" do
given_command "admin" do
default_subcommand "status", "Show status" do
def execute
puts "All good!"
end
end
end
context "when executed with no subcommand" do
it "invokes the default subcommand" do
command.run([])
expect(stdout).to match(/All good/)
end
end
end
context "when declaring a default subcommand after subcommands" do
let(:command) do
Class.new(Clamp::Command) do
subcommand "status", "Show status" do
def execute
puts "All good!"
end
end
end
end
it "is not supported" do
expect do
command.default_subcommand = "status"
end.to raise_error(/default_subcommand must be defined before subcommands/)
end
end
context "with subcommands, declared after a parameter" do
given_command "with" do
parameter "THING", "the thing"
subcommand "spit", "spit it" do
def execute
puts "spat the #{thing}"
end
end
subcommand "say", "say it" do
subcommand "loud", "yell it" do
def execute
puts thing.upcase
end
end
end
end
it "allows the parameter to be specified first" do
command.run(["dummy", "spit"])
expect(stdout.strip).to eq "spat the dummy"
end
it "passes the parameter down the stack" do
command.run(["money", "say", "loud"])
expect(stdout.strip).to eq "MONEY"
end
it "shows parameter in usage help" do
command.run(["stuff", "say", "loud", "--help"])
rescue Clamp::HelpWanted => e
expect(e.command.invocation_path).to eq "with THING say loud"
end
end
describe "each subcommand" do
let(:command_class) do
speed_options = Module.new do
extend Clamp::Option::Declaration
option "--speed", "SPEED", "how fast", default: "slowly"
end
Class.new(Clamp::Command) do
option "--direction", "DIR", "which way", default: "home"
include speed_options
subcommand "move", "move in the appointed direction" do
def execute
motion = context[:motion] || "walking"
puts "#{motion} #{direction} #{speed}"
end
end
end
end
let(:command) do
command_class.new("go")
end
it "accepts options defined in superclass (specified after the subcommand)" do
command.run(["move", "--direction", "north"])
expect(stdout).to match(/walking north/)
end
it "accepts options defined in superclass (specified before the subcommand)" do
command.run(["--direction", "north", "move"])
expect(stdout).to match(/walking north/)
end
it "accepts options defined in included modules" do
command.run(["move", "--speed", "very quickly"])
expect(stdout).to match(/walking home very quickly/)
end
it "has access to command context" do
command = command_class.new("go", motion: "wandering")
command.run(["move"])
expect(stdout).to match(/wandering home/)
end
end
context "with a subcommand, with options" do
given_command "weeheehee" do
option "--json", "JSON", "a json blob" do |option|
print "parsing!"
option
end
subcommand "woohoohoo", "like weeheehee but with more o" do
def execute; end
end
end
it "only parses options once" do
command.run(["--json", '{"a":"b"}', "woohoohoo"])
expect(stdout).to eq "parsing!"
end
end
context "with an unknown subcommand" do
let(:subcommand_missing) do
Module.new do
def subcommand_missing(_name)
abort "there is no such thing"
end
end
end
let(:subcommand_missing_with_return) do
Module.new do
def subcommand_missing(_name)
self.class.recognised_subcommands.first.subcommand_class
end
end
end
let(:command_class) do
Class.new(Clamp::Command) do
subcommand "test", "test subcommand" do
def execute
puts "known subcommand"
end
end
def execute; end
end
end
let(:command) do
command_class.new("foo")
end
it "signals no such subcommand usage error" do
expect { command.run(["foo"]) }.to raise_error(Clamp::UsageError, "No such sub-command 'foo'")
end
it "executes the subcommand missing method" do
command.extend subcommand_missing
expect { command.run(["foo"]) }.to raise_error(SystemExit, /there is no such thing/)
end
it "uses the subcommand class returned from subcommand_missing" do
command.extend subcommand_missing_with_return
command.run(["foo"])
expect(stdout).to match(/known subcommand/)
end
end
context "with a subcommand and required options" do
given_command "movements" do
option "--direction", "N|S|E|W", "bearing", required: true
subcommand "hop", "Hop" do
def execute
puts "Hopping #{direction}"
end
end
end
it "allows options after the subcommand" do
command.run(%w[hop --direction south])
expect(stdout).to eq "Hopping south\n"
end
end
end
clamp-1.3.3/spec/clamp/option/ 0000755 0000041 0000041 00000000000 15045240442 016200 5 ustar www-data www-data clamp-1.3.3/spec/clamp/option/definition_spec.rb 0000644 0000041 0000041 00000017101 15045240442 021667 0 ustar www-data www-data # frozen_string_literal: true
require "spec_helper"
describe Clamp::Option::Definition do
context "with String argument" do
let(:option) do
described_class.new("--key-file", "FILE", "SSH identity")
end
it "has a long_switch" do
expect(option.long_switch).to eq "--key-file"
end
it "has a type" do
expect(option.type).to eq "FILE"
end
it "has a description" do
expect(option.description).to eq "SSH identity"
end
describe "#attribute_name" do
it "is derived from the (long) switch" do
expect(option.attribute_name).to eq "key_file"
end
it "can be overridden" do
option = described_class.new("--key-file", "FILE", "SSH identity", attribute_name: "ssh_identity")
expect(option.attribute_name).to eq "ssh_identity"
end
end
describe "#write_method" do
it "is derived from the attribute_name" do
expect(option.write_method).to eq "key_file="
end
end
describe "#default_value" do
it "defaults to nil" do
option = described_class.new("-n", "N", "iterations")
expect(option.default_value).to be_nil
end
it "can be overridden" do
option = described_class.new("-n", "N", "iterations", default: 1)
expect(option.default_value).to eq 1
end
end
describe "#help" do
it "combines switch, type and description" do
expect(option.help).to eq ["--key-file FILE", "SSH identity"]
end
end
end
context "when flag" do
let(:option) do
described_class.new("--verbose", :flag, "Blah blah blah")
end
describe "#default_conversion_block" do
context "with 'true' value" do
it "converts to true" do
expect(option.default_conversion_block.call("true")).to be true
end
end
context "with 'yes' value" do
it "converts to true" do
expect(option.default_conversion_block.call("yes")).to be true
end
end
context "with 'false' value" do
it "converts to false" do
expect(option.default_conversion_block.call("false")).to be false
end
end
context "with 'no' value" do
it "converts to false" do
expect(option.default_conversion_block.call("no")).to be false
end
end
end
describe "#help" do
it "excludes option argument" do
expect(option.help).to eq ["--verbose", "Blah blah blah"]
end
end
end
context "when negatable flag" do
let(:option) do
described_class.new("--[no-]force", :flag, "Force installation")
end
describe "positive form" do
it "handles this" do
expect(option.handles?("--force")).to be true
end
end
describe "negative form" do
it "handles this" do
expect(option.handles?("--no-force")).to be true
end
end
describe "#flag_set?" do
describe "positive variant" do
it "returns true" do
expect(option.flag_set?("--force")).to be true
end
end
describe "negative variant" do
it "returns false" do
expect(option.flag_set?("--no-force")).to be false
end
end
end
describe "#attribute_name" do
it "is derived from the (long) switch" do
expect(option.attribute_name).to eq "force"
end
end
end
context "with both short and long switches" do
let(:option) do
described_class.new(["-k", "--key-file"], "FILE", "SSH identity")
end
describe "long switch" do
it "handles this" do
expect(option.handles?("--key-file")).to be true
end
end
describe "short switch" do
it "handles this" do
expect(option.handles?("-k")).to be true
end
end
describe "#help" do
it "includes both switches" do
expect(option.help).to eq ["-k, --key-file FILE", "SSH identity"]
end
end
end
context "with an associated environment variable" do
let(:option) do
described_class.new("-x", "X", "mystery option", environment_variable: "APP_X")
end
describe "#help" do
it "describes environment variable" do
expect(option.help).to eq ["-x X", "mystery option (default: $APP_X)"]
end
end
context "with a default value" do
let(:option) do
described_class.new("-x", "X", "mystery option", environment_variable: "APP_X", default: "xyz")
end
describe "#help" do
it "describes both environment variable and default" do
expect(option.help).to eq ["-x X", %{mystery option (default: $APP_X, or "xyz")}]
end
end
end
context "when it is required" do
let(:option) do
described_class.new("-x", "X", "mystery option", environment_variable: "APP_X", required: true)
end
describe "#help" do
it "describes the environment variable as the default" do
expect(option.help).to eql ["-x X", %{mystery option (required, default: $APP_X)}]
end
end
end
end
context "when multivalued" do
let(:option) do
described_class.new(["-H", "--header"], "HEADER", "extra header", multivalued: true)
end
it "is multivalued" do
expect(option).to be_multivalued
end
describe "#default_value" do
it "defaults to an empty Array" do
expect(option.default_value).to be_empty
end
it "can be overridden" do
option = described_class.new("-H", "HEADER", "extra header", multivalued: true, default: [1, 2, 3])
expect(option.default_value).to eq [1, 2, 3]
end
end
describe "#attribute_name" do
it "gets a _list suffix" do
expect(option.attribute_name).to eq "header_list"
end
end
describe "#append_method" do
it "is derived from the attribute_name" do
expect(option.append_method).to eq "append_to_header_list"
end
end
end
describe "in subcommand" do
let(:command_class) do
Class.new(Clamp::Command) do
subcommand "foo", "FOO!" do
option "--bar", "BAR", "Bars foo."
end
end
end
describe "Command#help" do
it "includes help for each option exactly once" do
subcommand = command_class.send(:find_subcommand, "foo")
subcommand_help = subcommand.subcommand_class.help("")
expect(subcommand_help.lines.grep(/--bar BAR/).count).to eq 1
end
end
end
describe "a required option" do
it "rejects :default" do
expect do
described_class.new("--key-file", "FILE", "SSH identity",
required: true, default: "hello")
end.to raise_error(ArgumentError)
end
it "rejects :flag options" do
expect do
described_class.new("--awesome", :flag, "Be awesome?", required: true)
end.to raise_error(ArgumentError)
end
end
describe "a hidden option" do
let(:option) { described_class.new("--unseen", :flag, "Something", hidden: true) }
it "is hidden" do
expect(option).to be_hidden
end
end
describe "a hidden option in a command" do
let(:command_class) do
Class.new(Clamp::Command) do
option "--unseen", :flag, "Something", hidden: true
def execute
# this space intentionally left blank
end
end
end
it "is not shown in the help" do
expect(command_class.help("foo")).not_to match(/^ +--unseen +Something$/)
end
it "sets the expected accessor" do
command = command_class.new("foo")
command.run(["--unseen"])
expect(command).to be_unseen
end
end
end
clamp-1.3.3/spec/clamp/command_option_module_spec.rb 0000644 0000041 0000041 00000001210 15045240442 022574 0 ustar www-data www-data # frozen_string_literal: true
require "spec_helper"
describe Clamp::Command do
include OutputCapture
context "with included module" do
let(:command) do
shared_options = Module.new do
extend Clamp::Option::Declaration
option "--size", "SIZE", default: 4
end
command_class = Class.new(Clamp::Command) do
include shared_options
def execute
puts "size = #{size}"
end
end
command_class.new("foo")
end
it "accepts options from included module" do
command.run(["--size", "42"])
expect(stdout).to eq "size = 42\n"
end
end
end
clamp-1.3.3/spec/clamp/messages_spec.rb 0000644 0000041 0000041 00000002062 15045240442 020036 0 ustar www-data www-data # frozen_string_literal: true
require "spec_helper"
describe Clamp::Messages do
describe "message" do
before do
Clamp.messages = {
too_many_arguments: "Way too many!",
custom_message: "Say %s to %s"
}
end
after do
Clamp.clear_messages!
end
it "allows setting custom messages" do
expect(Clamp.message(:too_many_arguments)).to eq "Way too many!"
end
it "fallbacks to a default message" do
expect(Clamp.message(:no_value_provided)).to eq "no value provided"
end
it "formats the message" do
expect(Clamp.message(:custom_message, what: "hello", whom: "Clamp")).to eq "Say hello to Clamp"
end
end
describe "clear_messages!" do
let!(:default_msg) { Clamp.message(:too_many_arguments).clone }
before do
Clamp.messages = {
too_many_arguments: "Way too many!"
}
Clamp.clear_messages!
end
it "clears messages to the defualt state" do
expect(Clamp.message(:too_many_arguments)).to eq default_msg
end
end
end
clamp-1.3.3/spec/clamp/command_spec.rb 0000644 0000041 0000041 00000066216 15045240442 017660 0 ustar www-data www-data # frozen_string_literal: true
require "spec_helper"
describe Clamp::Command do
extend CommandFactory
include OutputCapture
include SetEnv
given_command("cmd") do
def execute
puts "Hello, world"
end
end
describe "#help" do
it "describes usage" do
expect(command.help).to match(/^Usage:\n cmd.*\n/)
end
end
describe "#run" do
before do
command.run([])
end
it "executes the #execute method" do
expect(stdout).not_to be_empty
end
end
describe ".option" do
context "when regular" do
before do
command.class.option "--flavour", "FLAVOUR", "Flavour of the month"
end
context "when not set" do
it "equals nil" do
expect(command.flavour).to be_nil
end
end
context "when set" do
before do
command.flavour = "chocolate"
end
it "equals the value" do
expect(command.flavour).to eq "chocolate"
end
end
end
context "with type :flag" do
before do
command.class.option "--verbose", :flag, "Be heartier"
end
describe "predicate-style reader" do
it "exists" do
expect(command).to respond_to(:verbose?)
end
end
describe "regular reader" do
it "does not exist" do
expect(command).not_to respond_to(:verbose)
end
end
end
context "with explicit :attribute_name" do
before do
command.class.option "--foo", "FOO", "A foo", attribute_name: :bar
end
it "uses the specified attribute_name name to name accessors" do
command.bar = "chocolate"
expect(command.bar).to eq "chocolate"
end
describe "default reader" do
it "does not exist" do
expect(command).not_to respond_to(:foo)
end
end
describe "default writer" do
it "does not exist" do
expect(command).not_to respond_to(:foo=)
end
end
end
context "with default method" do
before do
command.class.option "--port", "PORT", "port"
command.class.class_eval do
def default_port
4321
end
end
end
it "sets the specified default value" do
expect(command.port).to eq 4321
end
end
context "with :default value" do
before do
command.class.option "--port", "PORT", "port to listen on", default: 4321
end
it "declares default method" do
expect(command.default_port).to eq 4321
end
describe "#help" do
it "describes the default value" do
expect(command.help).to include("port to listen on (default: 4321)")
end
end
end
context "without :default value" do
before do
command.class.option "--port", "PORT", "port to listen on"
end
it "does not declare default method" do
expect(command).not_to respond_to(:default_port)
end
end
context "with :multivalued" do
before do
command.class.option "--flavour", "FLAVOUR", "flavour(s)", multivalued: true, attribute_name: :flavours
end
it "defaults to empty array" do
expect(command.flavours).to be_empty
end
it "supports multiple values" do
command.parse(%w[--flavour chocolate --flavour vanilla])
expect(command.flavours).to eq %w[chocolate vanilla]
end
it "generates a single-value appender method" do
command.append_to_flavours("mud")
command.append_to_flavours("pie")
expect(command.flavours).to eq %w[mud pie]
end
it "generates a multi-value setter method" do
command.append_to_flavours("replaceme")
command.flavours = %w[mud pie]
expect(command.flavours).to eq %w[mud pie]
end
it "does not require a value" do
expect do
command.parse([])
end.not_to raise_error
end
end
context "with :environment_variable" do
let(:environment_value) { nil }
let(:args) { [] }
before do
command.class.option "--port", "PORT", "port to listen on",
default: 4321,
environment_variable: "PORT",
&:to_i
set_env("PORT", environment_value)
command.parse(args)
end
context "when no environment variable is present" do
it "uses the default" do
expect(command.port).to eq 4321
end
end
context "when environment variable is present" do
let(:environment_value) { "12345" }
it "uses the environment variable" do
expect(command.port).to eq 12_345
end
context "when a value is specified on the command-line" do
let(:args) { %w[--port 1500] }
it "uses command-line value" do
expect(command.port).to eq 1500
end
end
end
describe "#help" do
it "describes the default value and env usage" do
expect(command.help).to include("port to listen on (default: $PORT, or 4321)")
end
end
end
context "with :environment_variable and type :flag" do
let(:environment_value) { nil }
before do
command.class.option "--[no-]enable", :flag, "enable?", default: false, environment_variable: "ENABLE"
set_env("ENABLE", environment_value)
command.parse([])
end
context "when no environment variable is present" do
it "uses the default" do
expect(command.enable?).to be false
end
end
%w[1 yes enable on true].each do |truthy_value|
context "when environment variable is #{truthy_value.inspect}" do
let(:environment_value) { truthy_value }
it "sets the flag" do
expect(command.enable?).to be true
end
end
end
%w[0 no disable off false].each do |falsey_value|
context "when environment variable is #{falsey_value.inspect}" do
let(:environment_value) { falsey_value }
it "clears the flag" do
expect(command.enable?).to be false
end
end
end
end
context "with :required" do
before do
command.class.option "--port", "PORT", "port to listen on", required: true
end
describe "#help" do
it "marks it as required" do
expect(command.help).to include("port to listen on (required)")
end
end
context "when no value is provided" do
it "raises a UsageError" do
expect do
command.parse([])
end.to raise_error(Clamp::UsageError)
end
end
context "when a value is provided" do
it "does not raise an error" do
expect do
command.parse(["--port", "12345"])
end.not_to raise_error
end
end
end
context "with :required and :multivalued" do
before do
command.class.option "--port", "PORT", "port to listen on", required: true, multivalued: true
end
context "when no value is provided" do
it "raises a UsageError" do
expect do
command.parse([])
end.to raise_error(Clamp::UsageError)
end
end
context "when a value is provided" do
it "does not raise an error" do
expect do
command.parse(["--port", "12345"])
end.not_to raise_error
end
end
end
context "with a block" do
before do
command.class.option "--port", "PORT", "Port to listen on" do |port|
Integer(port)
end
end
context "when value is incorrect" do
it "raises an error" do
expect do
command.port = "blah"
end.to raise_error(ArgumentError)
end
end
context "when value is convertible" do
it "uses the block to validate and convert the option argument" do
command.port = "1234"
expect(command.port).to eq 1234
end
end
end
end
context "with options declared" do
before do
command.class.option ["-f", "--flavour"], "FLAVOUR", "Flavour of the month"
command.class.option ["-c", "--color"], "COLOR", "Preferred hue"
command.class.option ["--scoops"], "N", "Number of scoops",
default: 1,
environment_variable: "DEFAULT_SCOOPS" do |arg|
Integer(arg)
end
command.class.option ["-n", "--[no-]nuts"], :flag, "Nuts (or not)\nMay include nuts"
command.class.parameter "[ARG] ...", "extra arguments", attribute_name: :arguments
end
describe "#parse" do
context "with an unrecognised option" do
it "raises a UsageError" do
expect do
command.parse(%w[--foo bar])
end.to raise_error(Clamp::UsageError)
end
end
context "with options" do
before do
command.parse(%w[--flavour strawberry --nuts --color blue])
end
describe "flavour" do
it "maps the option value onto the command object" do
expect(command.flavour).to eq "strawberry"
end
end
describe "color" do
it "maps the option value onto the command object" do
expect(command.color).to eq "blue"
end
end
describe "nuts?" do
it "maps the option value onto the command object" do
expect(command.nuts?).to be true
end
end
end
context "with short options" do
before do
command.parse(%w[-f strawberry -c blue])
end
describe "flavour" do
it "recognizes short options as aliases" do
expect(command.flavour).to eq "strawberry"
end
end
describe "color" do
it "recognizes short options as aliases" do
expect(command.color).to eq "blue"
end
end
end
context "with a value appended to a short option" do
before do
command.parse(%w[-fstrawberry])
end
it "works as though the value were separated" do
expect(command.flavour).to eq "strawberry"
end
end
context "with combined short options" do
before do
command.parse(%w[-nf strawberry])
end
describe "flavour" do
it "works as though the options were separate" do
expect(command.flavour).to eq "strawberry"
end
end
describe "nuts?" do
it "works as though the options were separate" do
expect(command.nuts?).to be true
end
end
end
context "with option arguments attached using equals sign" do
before do
command.parse(%w[--flavour=strawberry --color=blue])
end
describe "flavour" do
it "maps the option value onto the command object" do
expect(command.flavour).to eq "strawberry"
end
end
describe "color" do
it "maps the option value onto the command object" do
expect(command.color).to eq "blue"
end
end
end
context "with option arguments that look like options" do
before do
command.parse(%w[--flavour=-dashing- --scoops -1])
end
describe "flavour" do
it "sets the options" do
expect(command.flavour).to eq("-dashing-")
end
end
describe "scoops" do
it "sets the options" do
expect(command.scoops).to eq(-1)
end
end
end
context "with option-like things beyond the arguments" do
it "treats them as positional arguments" do
command.parse(%w[a b c --flavour strawberry])
expect(command.arguments).to eq %w[a b c --flavour strawberry]
end
end
context "with multi-line arguments that look like options" do
before do
command.parse(["foo\n--flavour=strawberry", "bar\n-cblue"])
end
describe "arguments" do
it "treats them as positional arguments" do
expect(command.arguments).to eq ["foo\n--flavour=strawberry", "bar\n-cblue"]
end
end
describe "flavour" do
it "treats them as positional arguments" do
expect(command.flavour).to be_nil
end
end
describe "color" do
it "treats them as positional arguments" do
expect(command.color).to be_nil
end
end
end
context "with an option terminator" do
it "considers everything after the terminator to be an argument" do
command.parse(%w[--color blue -- --flavour strawberry])
expect(command.arguments).to eq %w[--flavour strawberry]
end
end
context "with --flag" do
before do
command.parse(%w[--nuts])
end
it "sets the flag" do
expect(command.nuts?).to be true
end
end
context "with --no-flag" do
before do
command.nuts = true
command.parse(%w[--no-nuts])
end
it "clears the flag" do
expect(command.nuts?).to be false
end
end
context "with --help" do
it "requests help" do
expect do
command.parse(%w[--help])
end.to raise_error(Clamp::HelpWanted)
end
end
context "with -h" do
it "requests help" do
expect do
command.parse(%w[-h])
end.to raise_error(Clamp::HelpWanted)
end
end
context "when no option value is provided" do
it "signals a UsageError" do
expect do
command.parse(%w[--flavour])
end.to raise_error(Clamp::UsageError, /^option '--flavour': no value provided/)
end
end
context "when a bad option value is specified on the command-line" do
it "signals a UsageError" do
expect do
command.parse(%w[--scoops reginald])
end.to raise_error(Clamp::UsageError, /^option '--scoops': invalid value for Integer/)
end
end
context "when a bad option value is specified in the environment" do
it "signals a UsageError" do
ENV["DEFAULT_SCOOPS"] = "marjorie"
expect do
command.parse([])
end.to raise_error(Clamp::UsageError, /^\$DEFAULT_SCOOPS: invalid value for Integer/)
end
end
end
describe "#help" do
it "indicates that there are options" do
expect(command.help).to include("cmd [OPTIONS]")
end
it "includes option details" do
flavour_help = "-f, --flavour FLAVOUR +Flavour of the month"
color_help = "-c, --color COLOR +Preferred hue"
expect(command.help).to match(/#{flavour_help}\n +#{color_help}/)
end
it "handles new lines in option descriptions" do
expect(command.help).to match(/--\[no-\]nuts +Nuts \(or not\)\n +May include nuts/)
end
end
end
context "with an explicit --help option declared" do
before do
command.class.option ["--help"], :flag, "help wanted"
end
describe "parsing" do
it "does not generate implicit help option" do
expect do
command.parse(%w[--help])
end.not_to raise_error
end
end
describe "help?" do
before do
command.parse(%w[--help])
end
it "generates custom help option" do
expect(command.help?).to be true
end
end
it "does not recognise -h" do
expect do
command.parse(%w[-h])
end.to raise_error(Clamp::UsageError)
end
end
context "with an explicit -h option declared" do
before do
command.class.option ["-h", "--humidity"], "PERCENT", "relative humidity" do |n|
Integer(n)
end
end
it "does not map -h to help" do
expect(command.help).not_to match(/-h[, ].*help/)
end
it "still recognizes --help" do
expect do
command.parse(%w[--help])
end.to raise_error(Clamp::HelpWanted)
end
end
describe ".parameter" do
context "when regular" do
before do
command.class.parameter "FLAVOUR", "flavour of the month"
end
context "when not set" do
it "equals nil" do
expect(command.flavour).to be_nil
end
end
context "when set" do
before do
command.flavour = "chocolate"
end
it "equals the value" do
expect(command.flavour).to eq "chocolate"
end
end
end
context "with explicit :attribute_name" do
before do
command.class.parameter "FOO", "a foo", attribute_name: :bar
end
it "uses the specified attribute_name name to name accessors" do
command.bar = "chocolate"
expect(command.bar).to eq "chocolate"
end
end
context "with :default value" do
before do
command.class.parameter "[ORIENTATION]", "direction", default: "west"
end
it "sets the specified default value" do
expect(command.orientation).to eq "west"
end
describe "#help" do
it "describes the default value" do
expect(command.help).to include("direction (default: \"west\")")
end
end
end
context "with a block" do
before do
command.class.parameter "PORT", "port to listen on" do |port|
Integer(port)
end
end
context "when value is incorrect" do
it "raises an error" do
expect do
command.port = "blah"
end.to raise_error(ArgumentError)
end
end
context "when value is convertible" do
it "uses the block to validate and convert the argument" do
command.port = "1234"
expect(command.port).to eq 1234
end
end
end
context "with ellipsis" do
before do
command.class.parameter "FILE ...", "files"
end
it "accepts multiple arguments" do
command.parse(%w[X Y Z])
expect(command.file_list).to eq %w[X Y Z]
end
end
context "when optional, with ellipsis" do
before do
command.class.parameter "[FILE] ...", "files"
command.parse([])
end
describe "default_file_list" do
it "defaults to an empty list" do
expect(command.default_file_list).to be_empty
end
end
describe "file_list" do
it "defaults to an empty list" do
expect(command.file_list).to be_empty
end
end
describe "mutation" do
before do
command.file_list << "treasure"
end
it "is mutable" do
expect(command.file_list).to eq ["treasure"]
end
end
end
context "with :environment_variable" do
before do
command.class.parameter "[FILE]", "a file", environment_variable: "FILE",
default: "/dev/null"
set_env("FILE", environment_value)
command.parse(args)
end
let(:args) { [] }
let(:environment_value) { nil }
context "when neither argument nor environment variable are present" do
it "uses the default" do
expect(command.file).to eq File::NULL
end
end
context "when environment variable is present" do
let(:environment_value) { "/etc/motd" }
describe "and no argument is provided" do
it "uses the environment variable" do
expect(command.file).to eq "/etc/motd"
end
end
describe "and an argument is provided" do
let(:args) { ["/dev/null"] }
it "uses the argument" do
expect(command.file).to eq File::NULL
end
end
end
describe "#help" do
it "describes the default value and env usage" do
expect(command.help).to include(%{ (default: $FILE, or "/dev/null")})
end
end
end
end
context "with no parameters declared" do
describe "#parse" do
context "with arguments" do
it "raises a UsageError" do
expect do
command.parse(["crash"])
end.to raise_error(Clamp::UsageError, "too many arguments")
end
end
end
end
context "with parameters declared" do
before do
command.class.parameter "X", "x\nxx"
command.class.parameter "Y", "y"
command.class.parameter "[Z]", "z", default: "ZZZ"
end
describe "#parse" do
context "with arguments for all parameters" do
before do
command.parse(["crash", "bang", "wallop"])
end
describe "x" do
it "maps arguments onto the command object" do
expect(command.x).to eq "crash"
end
end
describe "y" do
it "maps arguments onto the command object" do
expect(command.y).to eq "bang"
end
end
describe "z" do
it "maps arguments onto the command object" do
expect(command.z).to eq "wallop"
end
end
end
context "with insufficient arguments" do
it "raises a UsageError" do
expect do
command.parse(["crash"])
end.to raise_error(Clamp::UsageError, "parameter 'Y': no value provided")
end
end
context "with optional argument omitted" do
before do
command.parse(["crash", "bang"])
end
describe "x" do
it "defaults the optional argument" do
expect(command.x).to eq "crash"
end
end
describe "y" do
it "defaults the optional argument" do
expect(command.y).to eq "bang"
end
end
describe "z" do
it "defaults the optional argument" do
expect(command.z).to eq "ZZZ"
end
end
end
context "with multi-line arguments" do
before do
command.parse(["foo\nhi", "bar", "baz"])
end
describe "x" do
it "parses them correctly" do
expect(command.x).to eq "foo\nhi"
end
end
describe "y" do
it "parses them correctly" do
expect(command.y).to eq "bar"
end
end
describe "z" do
it "parses them correctly" do
expect(command.z).to eq "baz"
end
end
end
context "with too many arguments" do
it "raises a UsageError" do
expect do
command.parse(["crash", "bang", "wallop", "kapow"])
end.to raise_error(Clamp::UsageError, "too many arguments")
end
end
end
describe "#help" do
it "indicates that there are parameters" do
expect(command.help).to include("cmd [OPTIONS] X Y [Z]")
end
it "includes parameter details" do
expect(command.help).to match(/X +x\n[\s\S]* +Y +y\n[\s\S]* +\[Z\] +z \(default: "ZZZ"\)/)
end
it "handles new lines in option descriptions" do
expect(command.help).to match(/X +x\n +xx/)
end
end
end
describe ".execute" do
before do
command_class.class_eval do
execute do
puts "using execute DSL"
end
end
command.run([])
end
it "provides an alternative way to declare execute method" do
expect(stdout).to eq("using execute DSL\n")
end
end
context "with explicit usage" do
given_command("blah") do
usage "FOO BAR ..."
end
describe "#help" do
it "includes the explicit usage" do
expect(command.help).to include("blah FOO BAR ...\n")
end
end
end
context "with multiple usages" do
given_command("put") do
usage "THIS HERE"
usage "THAT THERE"
end
describe "#help" do
it "includes both potential usages" do
expect(command.help).to match(/put THIS HERE\n +put THAT THERE/)
end
end
end
context "with a banner" do
given_command("punt") do
banner <<-TEXT
Punt is an example command. It doesn't do much, really.
The prefix at the beginning of this description should be normalised
to two spaces.
TEXT
end
describe "#help" do
it "includes the banner" do
expect(command.help).to match(/^ Punt is an example command.+\n^ \n^ The prefix/)
end
end
end
describe ".run" do
context "when regular" do
before do
command.class.class_eval do
parameter "WORD ...", "words"
def execute
print word_list.inspect
end
end
end
it "creates a new Command instance and runs it" do
xyz = %w[x y z]
command.class.run("cmd", xyz)
expect(stdout).to eq xyz.inspect
end
end
context "when invoked with a context hash" do
before do
command.class.class_eval do
def execute
print context[:foo]
end
end
end
it "makes the context available within the command" do
command.class.run("xyz", [], foo: "bar")
expect(stdout).to eq "bar"
end
end
context "when there's a CommandError" do
before do
command.class.class_eval do
def execute
signal_error "Oh crap!", status: 456
end
end
end
it "outputs the error message and exits with the specified status" do # rubocop:disable RSpec/ExampleLength
expect { command.class.run("cmd", []) }
.to raise_error(
an_instance_of(SystemExit).and(having_attributes(status: 456))
).and output(<<~TEXT).to_stderr
ERROR: Oh crap!
TEXT
end
end
context "when there's a UsageError" do
before do
command.class.class_eval do
def execute
signal_usage_error "bad dog!"
end
end
begin
command.class.run("cmd", [])
rescue SystemExit => e
@system_exit = e
end
end
it "outputs the error message and help" do # rubocop:disable RSpec/ExampleLength
expect { command.class.run("cmd", []) }
.to raise_error(
an_instance_of(SystemExit).and(having_attributes(status: 1))
).and output(<<~TEXT).to_stderr
ERROR: bad dog!
See: 'cmd --help'
TEXT
end
end
context "when help is requested" do
it "outputs help" do
command.class.run("cmd", ["--help"])
expect(stdout).to include "Usage:"
end
end
end
describe "subclass" do
let(:command) do
parent_command_class = Class.new(Clamp::Command) do
option "--verbose", :flag, "be louder"
end
derived_command_class = Class.new(parent_command_class) do
option "--iterations", "N", "number of times to go around"
end
derived_command_class.new("cmd")
end
it "inherits options from it's superclass" do
command.parse(["--verbose"])
expect(command).to be_verbose
end
end
end
clamp-1.3.3/spec/clamp/parameter/ 0000755 0000041 0000041 00000000000 15045240442 016650 5 ustar www-data www-data clamp-1.3.3/spec/clamp/parameter/definition_spec.rb 0000644 0000041 0000041 00000013514 15045240442 022343 0 ustar www-data www-data # frozen_string_literal: true
require "spec_helper"
describe Clamp::Parameter::Definition do
context "when regular" do
let(:parameter) do
described_class.new("COLOR", "hue of choice")
end
it "has a name" do
expect(parameter.name).to eq "COLOR"
end
it "has a description" do
expect(parameter.description).to eq "hue of choice"
end
it "is single-valued" do
expect(parameter).not_to be_multivalued
end
describe "#attribute_name" do
it "is derived from the name" do
expect(parameter.attribute_name).to eq "color"
end
it "can be overridden" do
parameter = described_class.new("COLOR", "hue of choice", attribute_name: "hue")
expect(parameter.attribute_name).to eq "hue"
end
end
describe "#consume" do
subject(:consume) { parameter.consume(arguments) }
context "with arguments" do
let(:arguments) { %w[a b c] }
it "returns one consumed argument" do
expect(consume).to eq ["a"]
end
describe "arguments after consume" do
before do
consume
end
it "has only non-consumed" do
expect(arguments).to eq %w[b c]
end
end
end
context "without arguments" do
let(:arguments) { [] }
it "raises an Argument error" do
expect { consume }.to raise_error(ArgumentError)
end
end
end
end
context "when optional (name in square brackets)" do
let(:parameter) do
described_class.new("[COLOR]", "hue of choice")
end
it "is single-valued" do
expect(parameter).not_to be_multivalued
end
describe "#attribute_name" do
it "omits the brackets" do
expect(parameter.attribute_name).to eq "color"
end
end
describe "#consume" do
subject(:consume) { parameter.consume(arguments) }
context "with arguments" do
let(:arguments) { %w[a b c] }
it "returns one consumed argument" do
expect(consume).to eq ["a"]
end
describe "arguments after consume" do
before do
consume
end
it "has only non-consumed" do
expect(arguments).to eq %w[b c]
end
end
end
context "without arguments" do
let(:arguments) { [] }
it "consumes nothing" do
expect(consume).to be_empty
end
end
end
end
context "when list (name followed by ellipsis)" do
let(:parameter) do
described_class.new("FILE ...", "files to process")
end
it "is multi-valued" do
expect(parameter).to be_multivalued
end
describe "#attribute_name" do
it "gets a _list suffix" do
expect(parameter.attribute_name).to eq "file_list"
end
end
describe "#append_method" do
it "is derived from the attribute_name" do
expect(parameter.append_method).to eq "append_to_file_list"
end
end
describe "#consume" do
subject(:consume) { parameter.consume(arguments) }
context "with arguments" do
let(:arguments) { %w[a b c] }
it "returns all the consumed arguments" do
expect(consume).to eq %w[a b c]
end
describe "arguments after consume" do
before do
consume
end
it "empty" do
expect(arguments).to be_empty
end
end
end
describe "without arguments" do
let(:arguments) { [] }
it "raises an Argument error" do
expect { consume }.to raise_error(ArgumentError)
end
end
end
context "with a weird parameter name, and an explicit attribute_name" do
let(:parameter) do
described_class.new("KEY=VALUE ...", "config-settings", attribute_name: :config_settings)
end
describe "#attribute_name" do
it "is the specified one" do
expect(parameter.attribute_name).to eq "config_settings"
end
end
end
end
context "when optional list" do
let(:parameter) do
described_class.new("[FILES] ...", "files to process")
end
it "is multi-valued" do
expect(parameter).to be_multivalued
end
describe "#attribute_name" do
it "gets a _list suffix" do
expect(parameter.attribute_name).to eq "files_list"
end
end
describe "#default_value" do
it "is an empty list" do
expect(parameter.default_value).to be_empty
end
end
describe "#help" do
it "does not include default" do
expect(parameter.help_rhs).not_to include("default:")
end
end
context "with specified default value" do
let(:parameter) do
described_class.new("[FILES] ...", "files to process", default: %w[a b c])
end
describe "#default_value" do
it "is that specified" do
expect(parameter.default_value).to eq %w[a b c]
end
end
describe "#help" do
it "includes the default value" do
expect(parameter.help_rhs).to include("default:")
end
end
describe "#consume" do
subject(:consume) { parameter.consume(arguments) }
context "with arguments" do
let(:arguments) { %w[a b c] }
it "returns all the consumed arguments" do
expect(consume).to eq %w[a b c]
end
describe "arguments after consume" do
before do
consume
end
it "empty" do
expect(arguments).to be_empty
end
end
end
context "without arguments" do
let(:arguments) { [] }
it "don't override defaults" do
expect(consume).to be_empty
end
end
end
end
end
end
clamp-1.3.3/spec/clamp/command_option_reordering_spec.rb 0000644 0000041 0000041 00000002234 15045240442 023456 0 ustar www-data www-data # frozen_string_literal: true
require "spec_helper"
describe Clamp::Command do
extend CommandFactory
include OutputCapture
context "with allow_options_after_parameters enabled" do
before do
Clamp.allow_options_after_parameters = true
end
after do
Clamp.allow_options_after_parameters = false
end
given_command("cmd") do
option ["-v", "--verbose"], :flag, "Be noisy"
subcommand "say", "Say something" do
option "--loud", :flag, "say it loud"
parameter "WORDS ...", "the thing to say", attribute_name: :words
def execute
message = words.join(" ")
message = message.upcase if loud?
message *= 3 if verbose?
$stdout.puts message
end
end
end
it "still works" do
command.run(%w[say foo])
expect(stdout).to eq "foo\n"
end
it "honours options after positional arguments" do
command.run(%w[say blah --verbose])
expect(stdout).to eq "blahblahblah\n"
end
it "honours options declared on subcommands" do
command.run(%w[say --loud blah])
expect(stdout).to eq "BLAH\n"
end
end
end
clamp-1.3.3/.rspec 0000644 0000041 0000041 00000000023 15045240442 013752 0 ustar www-data www-data --color
--warnings
clamp-1.3.3/Rakefile 0000644 0000041 0000041 00000000521 15045240442 014305 0 ustar www-data www-data # frozen_string_literal: true
require "bundler"
Bundler::GemHelper.install_tasks
require "rspec/core/rake_task"
RSpec::Core::RakeTask.new do |t|
t.pattern = "spec/**/*_spec.rb"
t.rspec_opts = ["--colour", "--format", "documentation"]
end
require "rubocop/rake_task"
RuboCop::RakeTask.new
task "default" => ["spec", "rubocop"]
clamp-1.3.3/CODEOWNERS 0000644 0000041 0000041 00000000010 15045240442 014224 0 ustar www-data www-data * @mdub
clamp-1.3.3/Gemfile 0000644 0000041 0000041 00000000654 15045240442 014142 0 ustar www-data www-data # 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.79.0", require: false
gem "rubocop-rake", "~> 0.7.1", require: false
gem "rubocop-rspec", "~> 3.6.0", require: false
end
group :test do
gem "rspec", "~> 3.13"
end
clamp-1.3.3/LICENSE 0000644 0000041 0000041 00000002067 15045240442 013654 0 ustar www-data www-data Copyright (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.
clamp-1.3.3/.travis.yml 0000644 0000041 0000041 00000000064 15045240442 014753 0 ustar www-data www-data language: ruby
rvm:
- 2.5
- 2.6
- 2.7
- 3.0
clamp-1.3.3/README.md 0000644 0000041 0000041 00000033276 15045240442 014134 0 ustar www-data www-data # Clamp
[](http://badge.fury.io/rb/clamp)
[](http://travis-ci.org/mdub/clamp)
"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
```
## 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 '%s' is required"),
option_or_env_required: _("option '% s' (or env %s) is required"),
option_argument_error: _("option '%s': %s")
# ...
}
```
See [messages.rb](https://github.com/mdub/clamp/blob/master/lib/clamp/messages.rb) for full list of available messages.
## License
Copyright (C) 2011 [Mike Williams](mailto:mdub@dogbiscuit.org)
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 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.
## Contributing to Clamp
Source-code for Clamp is [on Github](https://github.com/mdub/clamp).
clamp-1.3.3/examples/ 0000755 0000041 0000041 00000000000 15045240442 014460 5 ustar www-data www-data clamp-1.3.3/examples/word 0000755 0000041 0000041 00000000611 15045240442 015357 0 ustar www-data www-data #! /usr/bin/env ruby
# frozen_string_literal: true
# Demonstrate "restful" style; parameters precede subcommands
require "clamp"
Clamp do
banner %(
Word ops.
)
parameter "WORD", "the word in question"
subcommand "say", "Say it" do
def execute
puts word
end
end
subcommand "shout", "Say it loud" do
def execute
puts word.upcase
end
end
end
clamp-1.3.3/examples/defaulted 0000755 0000041 0000041 00000001151 15045240442 016341 0 ustar www-data www-data #! /usr/bin/env ruby
# frozen_string_literal: true
# An example of default values and methods
require "clamp"
require "highline"
Clamp do
option ["-U", "--user"], "USER", "user name",
environment_variable: "THE_USER",
default: "bob"
option ["-P", "--password"], "PASSWORD", "password",
environment_variable: "THE_PASSWORD"
def execute
puts "User: #{user}, Password: #{password}"
end
private
def default_password
terminal.ask("Password [#{user}]: ") { |q| q.echo = "*" }
end
def terminal
tty = open("/dev/tty", "w+")
HighLine.new(tty, tty)
end
end
clamp-1.3.3/examples/speak 0000755 0000041 0000041 00000001005 15045240442 015505 0 ustar www-data www-data #! /usr/bin/env ruby
# frozen_string_literal: true
# A simple Clamp command, with options and parameters
require "clamp"
Clamp do
banner %(
Say something.
)
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
clamp-1.3.3/examples/admin 0000755 0000041 0000041 00000000754 15045240442 015504 0 ustar www-data www-data #! /usr/bin/env ruby
# frozen_string_literal: true
# An example of options and parameters
require "clamp"
Clamp do
option "--timeout", "SECONDS", "connection timeout", default: 5, environment_variable: "MYAPP_TIMEOUT" do |x|
Integer(x)
end
parameter "HOST", "server address"
parameter "[PORT]", "server port", default: 80, environment_variable: "MYAPP_PORT"
def execute
puts "trying to connect to #{host} on port #{port} (waiting up to #{timeout} seconds)"
end
end
clamp-1.3.3/examples/fubar 0000755 0000041 0000041 00000000413 15045240442 015503 0 ustar www-data www-data #! /usr/bin/env ruby
# frozen_string_literal: true
# An example of nested subcommands
require "clamp"
Clamp do
subcommand "foo", "Foo!" do
subcommand ["bar", "bah", "baa"], "Baaaa!" do
def execute
puts "FUBAR"
end
end
end
end
clamp-1.3.3/examples/gitdown 0000755 0000041 0000041 00000002474 15045240442 016070 0 ustar www-data www-data #! /usr/bin/env ruby
# frozen_string_literal: true
# Demonstrate how subcommands can be declared as classes
require "clamp"
module GitDown
class AbstractCommand < Clamp::Command
option ["-v", "--verbose"], :flag, "be verbose"
option "--version", :flag, "show version" do
puts "GitDown-0.0.0a"
exit(0)
end
def say(message)
message = message.upcase if verbose?
puts message
end
end
class CloneCommand < AbstractCommand
parameter "REPOSITORY", "repository to clone"
parameter "[DIR]", "working directory", default: "."
def execute
say "cloning to #{dir}"
end
end
class PullCommand < AbstractCommand
option "--[no-]commit", :flag, "Perform the merge and commit the result."
def execute
say "pulling"
end
end
class StatusCommand < AbstractCommand
option ["-s", "--short"], :flag, "Give the output in the short-format."
def execute
if short?
say "good"
else
say "it's all good ..."
end
end
end
class MainCommand < AbstractCommand
subcommand "clone", "Clone a remote repository.", CloneCommand
subcommand "pull", "Fetch and merge updates.", PullCommand
subcommand "status", "Display status of local repository.", StatusCommand
end
end
GitDown::MainCommand.run
clamp-1.3.3/examples/flipflop 0000755 0000041 0000041 00000000761 15045240442 016225 0 ustar www-data www-data #! /usr/bin/env ruby
# frozen_string_literal: true
# An example of subcommands (including a default subcommand)
require "clamp"
require "clamp/version"
Clamp do
option ["--version", "-v"], :flag, "Show version" do
puts "Powered by Clamp-#{Clamp::VERSION}"
exit(0)
end
self.default_subcommand = "flip"
subcommand "flip", "flip it" do
def execute
puts "FLIPPED"
end
end
subcommand "flop", "flop it" do
def execute
puts "FLOPPED"
end
end
end
clamp-1.3.3/examples/scoop 0000755 0000041 0000041 00000000515 15045240442 015532 0 ustar www-data www-data #! /usr/bin/env ruby
# frozen_string_literal: true
# An example of multi-valued options
require "clamp"
Clamp do
option ["-f", "--flavour"], "FLAVOUR", "flavour",
multivalued: true, default: ["chocolate"],
attribute_name: :flavours
def execute
puts "one #{flavours.join(' and ')} ice-cream"
end
end
clamp-1.3.3/examples/subcommand_missing 0000755 0000041 0000041 00000000462 15045240442 020271 0 ustar www-data www-data #! /usr/bin/env ruby
# frozen_string_literal: true
# Demonstrate subcommand_missing
require "clamp"
Clamp do
subcommand "hello", "Say hello" do
def execute
puts "Hello"
end
end
def subcommand_missing(name)
abort "Install bye plugin first" if name == "bye"
super
end
end
clamp-1.3.3/.rubocop.yml 0000644 0000041 0000041 00000001743 15045240442 015121 0 ustar www-data www-data plugins:
- rubocop-rake
- rubocop-rspec
AllCops:
TargetRubyVersion: 2.5
NewCops: enable
Layout/LineLength:
Max: 120
Layout/EmptyLinesAroundBlockBody:
Enabled: false
Layout/EmptyLinesAroundClassBody:
EnforcedStyle: empty_lines
Layout/EmptyLinesAroundModuleBody:
Enabled: false
Metrics/AbcSize:
Enabled: false
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