clean up class method declaration slightly using class << self syntax
This commit is contained in:
parent
6cf70753f4
commit
e852d2755c
317
lib/yawpa.rb
317
lib/yawpa.rb
@ -13,179 +13,190 @@ require "yawpa/version"
|
||||
# - Options can be defined with a range specifying the allowed number of
|
||||
# parameters
|
||||
module Yawpa
|
||||
|
||||
# Exception class raised when an unknown option is observed.
|
||||
class ArgumentParsingException < Exception; end
|
||||
|
||||
# Parse input parameters looking for options according to rules given in
|
||||
# flags.
|
||||
# Syntax:
|
||||
# opts, args = parse(params, options, flags = {})
|
||||
#
|
||||
# An ArgumentParsingException will be raised if an unknown option is observed
|
||||
# or insufficient arguments are present for an option.
|
||||
#
|
||||
# Example +options+:
|
||||
#
|
||||
# {
|
||||
# version: nil,
|
||||
# verbose: {short: 'v'},
|
||||
# server: {nargs: (1..2)},
|
||||
# username: {nargs: 1},
|
||||
# password: {nargs: 1},
|
||||
# color: :boolean,
|
||||
# }
|
||||
#
|
||||
# The keys of the +options+ Hash can be either strings or symbols.
|
||||
#
|
||||
#
|
||||
# @param params [Array]
|
||||
# List of program parameters to parse.
|
||||
# @param options [Hash]
|
||||
# Hash containing the long option names as keys, and values containing
|
||||
# special flags for the options as values (examples above).
|
||||
# Possible values:
|
||||
# +nil+:: No special flags for this option (equivalent to +{}+)
|
||||
# +:boolean+::
|
||||
# The option is a toggleable boolean option (equivalent to
|
||||
# +{boolean: true}+)
|
||||
# Hash::
|
||||
# Possible option flags:
|
||||
# - +:short+: specify a short option letter to associate with the long option
|
||||
# - +:nargs+: specify an exact number or range of possible numbers of
|
||||
# arguments to the option
|
||||
# - +:boolean+: if true, specify that the option is a toggleable boolean
|
||||
# option and allow a prefix of "no" to turn it off.
|
||||
# @param flags [Hash]
|
||||
# Optional flags dictating how {.parse} should do its job.
|
||||
# @option flags [Boolean] :posix_order
|
||||
# Stop processing parameters when a non-option argument is seen.
|
||||
# Set this to +true+ if you want to implement subcommands.
|
||||
#
|
||||
# @return [Array]
|
||||
# Two-element array containing +opts+ and +args+ return values.
|
||||
# +opts+::
|
||||
# The returned +opts+ value will be a Hash with the observed
|
||||
# options as keys and any option arguments as values.
|
||||
# +args+::
|
||||
# The returned +args+ will be an Array of the unprocessed
|
||||
# parameters (if +:posix_order+ was passed in +flags+, this array might
|
||||
# contain further options that were not processed after observing a
|
||||
# non-option parameters).
|
||||
def self.parse(params, options, flags = {})
|
||||
options = _massage_options(options)
|
||||
opts = {}
|
||||
args = []
|
||||
i = 0
|
||||
while i < params.length
|
||||
param = params[i]
|
||||
if param =~ /^--([^=]+)(?:=(.+))?$/
|
||||
param_name, val = $1, $2
|
||||
bool_val = true
|
||||
if options[param_name].nil?
|
||||
if param_name =~ /^no(.*)$/
|
||||
test_param_name = $1
|
||||
if options[test_param_name]
|
||||
param_name = test_param_name
|
||||
bool_val = false
|
||||
class << self
|
||||
|
||||
# Parse input parameters looking for options according to rules given in
|
||||
# flags.
|
||||
# Syntax:
|
||||
# opts, args = parse(params, options, flags = {})
|
||||
#
|
||||
# An ArgumentParsingException will be raised if an unknown option is
|
||||
# observed or insufficient arguments are present for an option.
|
||||
#
|
||||
# Example +options+:
|
||||
#
|
||||
# {
|
||||
# version: nil,
|
||||
# verbose: {short: 'v'},
|
||||
# server: {nargs: (1..2)},
|
||||
# username: {nargs: 1},
|
||||
# password: {nargs: 1},
|
||||
# color: :boolean,
|
||||
# }
|
||||
#
|
||||
# The keys of the +options+ Hash can be either strings or symbols.
|
||||
#
|
||||
#
|
||||
# @param params [Array]
|
||||
# List of program parameters to parse.
|
||||
# @param options [Hash]
|
||||
# Hash containing the long option names as keys, and values containing
|
||||
# special flags for the options as values (examples above).
|
||||
# Possible values:
|
||||
# +nil+:: No special flags for this option (equivalent to +{}+)
|
||||
# +:boolean+::
|
||||
# The option is a toggleable boolean option (equivalent to
|
||||
# +{boolean: true}+)
|
||||
# Hash::
|
||||
# Possible option flags:
|
||||
# - +:short+: specify a short option letter to associate with the long
|
||||
# option
|
||||
# - +:nargs+: specify an exact number or range of possible numbers of
|
||||
# arguments to the option
|
||||
# - +:boolean+: if true, specify that the option is a toggleable
|
||||
# boolean option and allow a prefix of "no" to turn it off.
|
||||
# @param flags [Hash]
|
||||
# Optional flags dictating how {.parse} should do its job.
|
||||
# @option flags [Boolean] :posix_order
|
||||
# Stop processing parameters when a non-option argument is seen.
|
||||
# Set this to +true+ if you want to implement subcommands.
|
||||
#
|
||||
# @return [Array]
|
||||
# Two-element array containing +opts+ and +args+ return values.
|
||||
# +opts+::
|
||||
# The returned +opts+ value will be a Hash with the observed
|
||||
# options as keys and any option arguments as values.
|
||||
# +args+::
|
||||
# The returned +args+ will be an Array of the unprocessed
|
||||
# parameters (if +:posix_order+ was passed in +flags+, this array might
|
||||
# contain further options that were not processed after observing a
|
||||
# non-option parameters).
|
||||
def parse(params, options, flags = {})
|
||||
options = _massage_options(options)
|
||||
opts = {}
|
||||
args = []
|
||||
i = 0
|
||||
while i < params.length
|
||||
param = params[i]
|
||||
if param =~ /^--([^=]+)(?:=(.+))?$/
|
||||
param_name, val = $1, $2
|
||||
bool_val = true
|
||||
if options[param_name].nil?
|
||||
if param_name =~ /^no(.*)$/
|
||||
test_param_name = $1
|
||||
if options[test_param_name]
|
||||
param_name = test_param_name
|
||||
bool_val = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
opt_config = options[param_name]
|
||||
raise ArgumentParsingException.new("Unknown option '#{param_name}'") unless opt_config
|
||||
param_key = opt_config[:key]
|
||||
if opt_config[:boolean]
|
||||
opts[param_key] = bool_val
|
||||
elsif opt_config[:nargs].last == 0
|
||||
opts[param_key] = true
|
||||
else
|
||||
opts[param_key] = []
|
||||
i += _gather(opt_config[:nargs], i + 1, params, val, param_key, opts[param_key])
|
||||
end
|
||||
elsif param =~ /^-(.+)$/
|
||||
short_flags = $1
|
||||
short_idx = 0
|
||||
while short_idx < short_flags.length
|
||||
opt_config = _find_opt_config_by_short_name(options, short_flags[short_idx])
|
||||
if opt_config.nil?
|
||||
raise ArgumentParsingException.new("Unknown option '-#{short_flags[short_idx]}'")
|
||||
end
|
||||
opt_config = options[param_name]
|
||||
raise ArgumentParsingException.new("Unknown option '#{param_name}'") unless opt_config
|
||||
param_key = opt_config[:key]
|
||||
if opt_config[:nargs].last == 0
|
||||
if opt_config[:boolean]
|
||||
opts[param_key] = bool_val
|
||||
elsif opt_config[:nargs].last == 0
|
||||
opts[param_key] = true
|
||||
else
|
||||
opts[param_key] = []
|
||||
i += _gather(opt_config[:nargs], i + 1, params, short_flags[short_idx + 1, short_flags.length], param_key, opts[param_key])
|
||||
break
|
||||
i += _gather(opt_config[:nargs], i + 1, params, val, param_key, opts[param_key])
|
||||
end
|
||||
short_idx += 1
|
||||
elsif param =~ /^-(.+)$/
|
||||
short_flags = $1
|
||||
short_idx = 0
|
||||
while short_idx < short_flags.length
|
||||
opt_config = _find_opt_config_by_short_name(options, short_flags[short_idx])
|
||||
if opt_config.nil?
|
||||
raise ArgumentParsingException.new("Unknown option '-#{short_flags[short_idx]}'")
|
||||
end
|
||||
param_key = opt_config[:key]
|
||||
if opt_config[:nargs].last == 0
|
||||
opts[param_key] = true
|
||||
else
|
||||
opts[param_key] = []
|
||||
i += _gather(opt_config[:nargs],
|
||||
i + 1,
|
||||
params,
|
||||
short_flags[short_idx + 1, short_flags.length],
|
||||
param_key,
|
||||
opts[param_key])
|
||||
break
|
||||
end
|
||||
short_idx += 1
|
||||
end
|
||||
elsif flags[:posix_order]
|
||||
args = params[i, params.length]
|
||||
break
|
||||
else
|
||||
args << params[i]
|
||||
end
|
||||
elsif flags[:posix_order]
|
||||
args = params[i, params.length]
|
||||
break
|
||||
else
|
||||
args << params[i]
|
||||
i += 1
|
||||
end
|
||||
i += 1
|
||||
|
||||
# Condense 1-element arrays of option values to just the element itself
|
||||
opts.each_key do |k|
|
||||
if opts[k].is_a?(Array) and opts[k].length == 1
|
||||
opts[k] = opts[k].first
|
||||
end
|
||||
end
|
||||
|
||||
return [opts, args]
|
||||
end
|
||||
|
||||
# Condense 1-element arrays of option values to just the element itself
|
||||
opts.each_key do |k|
|
||||
if opts[k].is_a?(Array) and opts[k].length == 1
|
||||
opts[k] = opts[k].first
|
||||
private
|
||||
|
||||
# Internal helper method to gather arguments for an option
|
||||
def _gather(nargs, start_idx, params, initial, param_key, result)
|
||||
n_gathered = 0
|
||||
if initial and initial != ''
|
||||
result << initial
|
||||
n_gathered += 1
|
||||
end
|
||||
num_indices_used = 0
|
||||
index = start_idx
|
||||
while n_gathered < nargs.last and
|
||||
index < params.length and
|
||||
params[index][0] != '-' do
|
||||
result << params[index]
|
||||
index += 1
|
||||
num_indices_used += 1
|
||||
n_gathered += 1
|
||||
end
|
||||
if n_gathered < nargs.first
|
||||
raise ArgumentParsingException.new("Not enough arguments supplied for option '#{param_key}'")
|
||||
end
|
||||
num_indices_used
|
||||
end
|
||||
|
||||
# Internal helper method to format the options in a consistent format
|
||||
def _massage_options(options)
|
||||
{}.tap do |newopts|
|
||||
options.each_pair do |k, v|
|
||||
v = {} if v.nil?
|
||||
v = {boolean: true} if v == :boolean
|
||||
newkey = k.to_s
|
||||
newopts[newkey] = {key: k}
|
||||
nargs = v[:nargs] || 0
|
||||
nargs = (nargs..nargs) if nargs.is_a?(Fixnum)
|
||||
newopts[newkey][:nargs] = nargs
|
||||
newopts[newkey][:short] = v[:short] || ''
|
||||
newopts[newkey][:boolean] = v[:boolean]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return [opts, args]
|
||||
end
|
||||
|
||||
# Internal helper method to gather arguments for an option
|
||||
def self._gather(nargs, start_idx, params, initial, param_key, result)
|
||||
n_gathered = 0
|
||||
if initial and initial != ''
|
||||
result << initial
|
||||
n_gathered += 1
|
||||
end
|
||||
num_indices_used = 0
|
||||
index = start_idx
|
||||
while n_gathered < nargs.last and
|
||||
index < params.length and
|
||||
params[index][0] != '-' do
|
||||
result << params[index]
|
||||
index += 1
|
||||
num_indices_used += 1
|
||||
n_gathered += 1
|
||||
end
|
||||
if n_gathered < nargs.first
|
||||
raise ArgumentParsingException.new("Not enough arguments supplied for option '#{param_key}'")
|
||||
end
|
||||
num_indices_used
|
||||
end
|
||||
private_class_method :_gather
|
||||
|
||||
# Internal helper method to format the options in a consistent format
|
||||
def self._massage_options(options)
|
||||
{}.tap do |newopts|
|
||||
# Internal helper method to find an option configuration by short name
|
||||
def _find_opt_config_by_short_name(options, short_name)
|
||||
options.each_pair do |k, v|
|
||||
v = {} if v.nil?
|
||||
v = {boolean: true} if v == :boolean
|
||||
newkey = k.to_s
|
||||
newopts[newkey] = {key: k}
|
||||
nargs = v[:nargs] || 0
|
||||
nargs = (nargs..nargs) if nargs.is_a?(Fixnum)
|
||||
newopts[newkey][:nargs] = nargs
|
||||
newopts[newkey][:short] = v[:short] || ''
|
||||
newopts[newkey][:boolean] = v[:boolean]
|
||||
return v if v[:short] == short_name
|
||||
end
|
||||
nil
|
||||
end
|
||||
end
|
||||
private_class_method :_massage_options
|
||||
|
||||
# Internal helper method to find an option configuration by short name
|
||||
def self._find_opt_config_by_short_name(options, short_name)
|
||||
options.each_pair do |k, v|
|
||||
return v if v[:short] == short_name
|
||||
end
|
||||
nil
|
||||
end
|
||||
private_class_method :_find_opt_config_by_short_name
|
||||
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user