lib/optparse.rb in optparse-0.4.0 vs lib/optparse.rb in optparse-0.5.0
- old
+ new
@@ -6,11 +6,10 @@
# Documentation:: Nobu Nakada and Gavin Sinclair.
#
# See OptionParser for documentation.
#
-
#--
# == Developer Documentation (not for RDoc output)
#
# === Class tree
#
@@ -423,11 +422,12 @@
# {Tutorial}[optparse/tutorial.rdoc],
# should be enough to learn how to use this class.
# If you have any questions, file a ticket at http://bugs.ruby-lang.org.
#
class OptionParser
- OptionParser::Version = "0.4.0"
+ # The version string
+ OptionParser::Version = "0.5.0"
# :stopdoc:
NoArgument = [NO_ARGUMENT = :NONE, nil].freeze
RequiredArgument = [REQUIRED_ARGUMENT = :REQUIRED, true].freeze
OptionalArgument = [OPTIONAL_ARGUMENT = :OPTIONAL, false].freeze
@@ -436,10 +436,12 @@
#
# Keyword completion module. This allows partial arguments to be specified
# and resolved against a list of acceptable values.
#
module Completion
+ # :nodoc:
+
def self.regexp(key, icase)
Regexp.new('\A' + Regexp.quote(key).gsub(/\w+\b/, '\&\w*'), icase)
end
def self.candidate(key, icase = false, pat = nil, &block)
@@ -457,11 +459,11 @@
candidates << [k, v, kn]
end
candidates
end
- def candidate(key, icase = false, pat = nil)
+ def candidate(key, icase = false, pat = nil, &_)
Completion.candidate(key, icase, pat, &method(:each))
end
public
def complete(key, icase = false, pat = nil)
@@ -508,10 +510,12 @@
#
# Defined within Switch are several Switch-derived classes: NoArgument,
# RequiredArgument, etc.
#
class Switch
+ # :nodoc:
+
attr_reader :pattern, :conv, :short, :long, :arg, :desc, :block
#
# Guesses argument style from +arg+. Returns corresponding
# OptionParser::Switch class (OptionalArgument, etc.).
@@ -695,10 +699,15 @@
def pretty_print(q) # :nodoc:
q.object_group(self) {pretty_print_contents(q)}
end
+ def omitted_argument(val) # :nodoc:
+ val.pop if val.size == 3 and val.last.nil?
+ val
+ end
+
#
# Switch that takes no arguments.
#
class NoArgument < self
@@ -708,14 +717,14 @@
def parse(arg, argv)
yield(NeedlessArgument, arg) if arg
conv_arg(arg)
end
- def self.incompatible_argument_styles(*)
+ def self.incompatible_argument_styles(*) # :nodoc:
end
- def self.pattern
+ def self.pattern # :nodoc:
Object
end
def pretty_head # :nodoc:
"NoArgument"
@@ -728,11 +737,11 @@
class RequiredArgument < self
#
# Raises an exception if argument is not present.
#
- def parse(arg, argv)
+ def parse(arg, argv, &_)
unless arg
raise MissingArgument if argv.empty?
arg = argv.shift
end
conv_arg(*parse_arg(arg, &method(:raise)))
@@ -753,11 +762,11 @@
#
def parse(arg, argv, &error)
if arg
conv_arg(*parse_arg(arg, &error))
else
- conv_arg(arg)
+ omitted_argument conv_arg(arg)
end
end
def pretty_head # :nodoc:
"Optional"
@@ -772,17 +781,18 @@
#
# Returns nil if argument is not present or begins with '-' and is not '-'.
#
def parse(arg, argv, &error)
if !(val = arg) and (argv.empty? or /\A-./ =~ (val = argv[0]))
- return nil, block, nil
+ return nil, block
end
opt = (val = parse_arg(val, &error))[1]
val = conv_arg(*val)
if opt and !arg
argv.shift
else
+ omitted_argument val
val[0] = nil
end
val
end
@@ -796,10 +806,12 @@
# Simple option list providing mapping from short and/or long option
# string to OptionParser::Switch and mapping from acceptable argument to
# matching pattern and converter pair. Also provides summary feature.
#
class List
+ # :nodoc:
+
# Map from acceptable argument types to pattern and converter pairs.
attr_reader :atype
# Map from short style option switches to actual switch objects.
attr_reader :short
@@ -835,11 +847,11 @@
# See OptionParser.accept.
#
def accept(t, pat = /.*/m, &block)
if pat
pat.respond_to?(:match) or
- raise TypeError, "has no `match'", ParseError.filter_backtrace(caller(2))
+ raise TypeError, "has no 'match'", ParseError.filter_backtrace(caller(2))
else
pat = t if t.respond_to?(:match)
end
unless block
block = pat.method(:convert).to_proc if pat.respond_to?(:convert)
@@ -1031,15 +1043,35 @@
def compsys(to, name = File.basename($0)) # :nodoc:
to << "#compdef #{name}\n"
to << COMPSYS_HEADER
visit(:compsys, {}, {}) {|o, d|
- to << %Q[ "#{o}[#{d.gsub(/[\"\[\]]/, '\\\\\&')}]" \\\n]
+ to << %Q[ "#{o}[#{d.gsub(/[\\\"\[\]]/, '\\\\\&')}]" \\\n]
}
to << " '*:file:_files' && return 0\n"
end
+ def help_exit
+ if STDOUT.tty? && (pager = ENV.values_at(*%w[RUBY_PAGER PAGER]).find {|e| e && !e.empty?})
+ less = ENV["LESS"]
+ args = [{"LESS" => "#{!less || less.empty? ? '-' : less}Fe"}, pager, "w"]
+ print = proc do |f|
+ f.puts help
+ rescue Errno::EPIPE
+ # pager terminated
+ end
+ if Process.respond_to?(:fork) and false
+ IO.popen("-") {|f| f ? Process.exec(*args, in: f) : print.call(STDOUT)}
+ # unreachable
+ end
+ IO.popen(*args, &print)
+ else
+ puts help
+ end
+ exit
+ end
+
#
# Default options for ARGV, which never appear in option summary.
#
Officious = {}
@@ -1047,12 +1079,11 @@
# --help
# Shows option summary.
#
Officious['help'] = proc do |parser|
Switch::NoArgument.new do |arg|
- puts parser.help
- exit
+ parser.help_exit
end
end
#
# --*-completion-bash=WORD
@@ -1127,10 +1158,14 @@
arg.nonzero?
when nil
default.to_i + 1
end
end
+
+ #
+ # See self.inc
+ #
def inc(*args)
self.class.inc(*args)
end
#
@@ -1165,15 +1200,23 @@
# back to be the first non-option argument.
#
def terminate(arg = nil)
self.class.terminate(arg)
end
+ #
+ # See #terminate.
+ #
def self.terminate(arg = nil)
throw :terminate, arg
end
@stack = [DefaultList]
+ #
+ # Returns the global top option list.
+ #
+ # Do not use directly.
+ #
def self.top() DefaultList end
#
# Directs to accept specified class +t+. The argument string is passed to
# the block in which it should be converted to the desired class.
@@ -1190,13 +1233,13 @@
def self.accept(*args, &blk) top.accept(*args, &blk) end
#
# Directs to reject specified class argument.
#
- # +t+:: Argument class specifier, any object including Class.
+ # +type+:: Argument class specifier, any object including Class.
#
- # reject(t)
+ # reject(type)
#
def reject(*args, &blk) top.reject(*args, &blk) end
#
# See #reject.
#
@@ -1282,14 +1325,28 @@
str << " (#{v})" if v = release
str
end
end
+ #
+ # Shows warning message with the program name
+ #
+ # +mesg+:: Message, defaulted to +$!+.
+ #
+ # See Kernel#warn.
+ #
def warn(mesg = $!)
super("#{program_name}: #{mesg}")
end
+ #
+ # Shows message with the program name then aborts.
+ #
+ # +mesg+:: Message, defaulted to +$!+.
+ #
+ # See Kernel#abort.
+ #
def abort(mesg = $!)
super("#{program_name}: #{mesg}")
end
#
@@ -1307,10 +1364,13 @@
end
#
# Pushes a new List.
#
+ # If a block is given, yields +self+ and returns the result of the
+ # block, otherwise returns +self+.
+ #
def new
@stack.push(List.new)
if block_given?
yield self
else
@@ -1530,10 +1590,16 @@
return s, short, long,
(not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style),
nolong
end
+ # ----
+ # Option definition phase methods
+ #
+ # These methods are used to define options, or to construct an
+ # OptionParser instance in other words.
+
# :call-seq:
# define(*params, &block)
#
# :include: ../doc/optparse/creates_option.rdoc
#
@@ -1605,57 +1671,69 @@
#
def separator(string)
top.append(string, nil, nil)
end
+ # ----
+ # Arguments parse phase methods
#
+ # These methods parse +argv+, convert, and store the results by
+ # calling handlers. As these methods do not modify +self+, +self+
+ # can be frozen.
+
+ #
# Parses command line arguments +argv+ in order. When a block is given,
# each non-option argument is yielded. When optional +into+ keyword
# argument is provided, the parsed option values are stored there via
# <code>[]=</code> method (so it can be Hash, or OpenStruct, or other
# similar object).
#
# Returns the rest of +argv+ left unparsed.
#
- def order(*argv, into: nil, &nonopt)
+ def order(*argv, **keywords, &nonopt)
argv = argv[0].dup if argv.size == 1 and Array === argv[0]
- order!(argv, into: into, &nonopt)
+ order!(argv, **keywords, &nonopt)
end
#
# Same as #order, but removes switches destructively.
# Non-option arguments remain in +argv+.
#
- def order!(argv = default_argv, into: nil, &nonopt)
+ def order!(argv = default_argv, into: nil, **keywords, &nonopt)
setter = ->(name, val) {into[name.to_sym] = val} if into
- parse_in_order(argv, setter, &nonopt)
+ parse_in_order(argv, setter, **keywords, &nonopt)
end
- def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc:
+ def parse_in_order(argv = default_argv, setter = nil, exact: require_exact, **, &nonopt) # :nodoc:
opt, arg, val, rest = nil
nonopt ||= proc {|a| throw :terminate, a}
argv.unshift(arg) if arg = catch(:terminate) {
while arg = argv.shift
case arg
# long option
when /\A--([^=]*)(?:=(.*))?/m
opt, rest = $1, $2
opt.tr!('_', '-')
begin
- sw, = complete(:long, opt, true)
- if require_exact && !sw.long.include?(arg)
- throw :terminate, arg unless raise_unknown
- raise InvalidOption, arg
+ if exact
+ sw, = search(:long, opt)
+ else
+ sw, = complete(:long, opt, true)
end
rescue ParseError
throw :terminate, arg unless raise_unknown
raise $!.set_option(arg, true)
+ else
+ unless sw
+ throw :terminate, arg unless raise_unknown
+ raise InvalidOption, arg
+ end
end
begin
- opt, cb, val = sw.parse(rest, argv) {|*exc| raise(*exc)}
- val = cb.call(val) if cb
- setter.call(sw.switch_name, val) if setter
+ opt, cb, *val = sw.parse(rest, argv) {|*exc| raise(*exc)}
+ val = callback!(cb, 1, *val) if cb
+ callback!(setter, 2, sw.switch_name, *val) if setter
rescue ParseError
raise $!.set_option(arg, rest)
end
# short option
@@ -1669,11 +1747,11 @@
sw, = complete(:short, opt)
# short option matched.
val = arg.delete_prefix('-')
has_arg = true
rescue InvalidOption
- raise if require_exact
+ raise if exact
# if no short options match, try completion with long
# options.
sw, = complete(:long, opt)
eq ||= !rest
end
@@ -1681,20 +1759,20 @@
rescue ParseError
throw :terminate, arg unless raise_unknown
raise $!.set_option(arg, true)
end
begin
- opt, cb, val = sw.parse(val, argv) {|*exc| raise(*exc) if eq}
+ opt, cb, *val = sw.parse(val, argv) {|*exc| raise(*exc) if eq}
rescue ParseError
raise $!.set_option(arg, arg.length > 2)
else
raise InvalidOption, arg if has_arg and !eq and arg == "-#{opt}"
end
begin
argv.unshift(opt) if opt and (!rest or (opt = opt.sub(/\A-*/, '-')) != '-')
- val = cb.call(val) if cb
- setter.call(sw.switch_name, val) if setter
+ val = callback!(cb, 1, *val) if cb
+ callback!(setter, 2, sw.switch_name, *val) if setter
rescue ParseError
raise $!.set_option(arg, arg.length > 2)
end
# non-option argument
@@ -1716,29 +1794,40 @@
argv
end
private :parse_in_order
+ # Calls callback with _val_.
+ def callback!(cb, max_arity, *args) # :nodoc:
+ if (size = args.size) < max_arity and cb.to_proc.lambda?
+ (arity = cb.arity) < 0 and arity = (1-arity)
+ arity = max_arity if arity > max_arity
+ args[arity - 1] = nil if arity > size
+ end
+ cb.call(*args)
+ end
+ private :callback!
+
#
# Parses command line arguments +argv+ in permutation mode and returns
# list of non-option arguments. When optional +into+ keyword
# argument is provided, the parsed option values are stored there via
# <code>[]=</code> method (so it can be Hash, or OpenStruct, or other
# similar object).
#
- def permute(*argv, into: nil)
+ def permute(*argv, **keywords)
argv = argv[0].dup if argv.size == 1 and Array === argv[0]
- permute!(argv, into: into)
+ permute!(argv, **keywords)
end
#
# Same as #permute, but removes switches destructively.
# Non-option arguments remain in +argv+.
#
- def permute!(argv = default_argv, into: nil)
+ def permute!(argv = default_argv, **keywords)
nonopts = []
- order!(argv, into: into, &nonopts.method(:<<))
+ order!(argv, **keywords, &nonopts.method(:<<))
argv[0, 0] = nonopts
argv
end
#
@@ -1746,24 +1835,24 @@
# POSIXLY_CORRECT is set, and in permutation mode otherwise.
# When optional +into+ keyword argument is provided, the parsed option
# values are stored there via <code>[]=</code> method (so it can be Hash,
# or OpenStruct, or other similar object).
#
- def parse(*argv, into: nil)
+ def parse(*argv, **keywords)
argv = argv[0].dup if argv.size == 1 and Array === argv[0]
- parse!(argv, into: into)
+ parse!(argv, **keywords)
end
#
# Same as #parse, but removes switches destructively.
# Non-option arguments remain in +argv+.
#
- def parse!(argv = default_argv, into: nil)
+ def parse!(argv = default_argv, **keywords)
if ENV.include?('POSIXLY_CORRECT')
- order!(argv, into: into)
+ order!(argv, **keywords)
else
- permute!(argv, into: into)
+ permute!(argv, **keywords)
end
end
#
# Wrapper method for getopts.rb.
@@ -1782,11 +1871,11 @@
# # params[:b] = "1" # -b1
# # params[:foo] = "1" # --foo
# # params[:bar] = "x" # --bar x
# # params[:zot] = "z" # --zot Z
#
- def getopts(*args, symbolize_names: false)
+ def getopts(*args, symbolize_names: false, **keywords)
argv = Array === args.first ? args.shift : default_argv
single_options, *long_options = *args
result = {}
@@ -1810,11 +1899,11 @@
result[opt] = false
define("--#{opt}", *[desc].compact)
end
end
- parse_in_order(argv, result.method(:[]=))
+ parse_in_order(argv, result.method(:[]=), **keywords)
symbolize_names ? result.transform_keys(&:to_sym) : result
end
#
# See #getopts.
@@ -1879,10 +1968,13 @@
all_candidates.select! {|cand| cand.is_a?(String) }
checker = DidYouMean::SpellChecker.new(dictionary: all_candidates)
DidYouMean.formatter.message_for(all_candidates & checker.correct(opt))
end
+ #
+ # Return candidates for +word+.
+ #
def candidate(word)
list = []
case word
when '-'
long = short = true
@@ -1920,14 +2012,14 @@
# under XDG and Haiku standard places.
#
# The optional +into+ keyword argument works exactly like that accepted in
# method #parse.
#
- def load(filename = nil, into: nil)
+ def load(filename = nil, **keywords)
unless filename
basename = File.basename($0, '.*')
- return true if load(File.expand_path(basename, '~/.options'), into: into) rescue nil
+ return true if load(File.expand_path(basename, '~/.options'), **keywords) rescue nil
basename << ".options"
return [
# XDG
ENV['XDG_CONFIG_HOME'],
'~/.config',
@@ -1935,15 +2027,15 @@
# Haiku
'~/config/settings',
].any? {|dir|
next if !dir or dir.empty?
- load(File.expand_path(basename, dir), into: into) rescue nil
+ load(File.expand_path(basename, dir), **keywords) rescue nil
}
end
begin
- parse(*File.readlines(filename, chomp: true), into: into)
+ parse(*File.readlines(filename, chomp: true), **keywords)
true
rescue Errno::ENOENT, Errno::ENOTDIR
false
end
end
@@ -1952,14 +2044,14 @@
# Parses environment variable +env+ or its uppercase with splitting like a
# shell.
#
# +env+ defaults to the basename of the program.
#
- def environment(env = File.basename($0, '.*'))
+ def environment(env = File.basename($0, '.*'), **keywords)
env = ENV[env] || ENV[env.upcase] or return
require 'shellwords'
- parse(*Shellwords.shellwords(env))
+ parse(*Shellwords.shellwords(env), **keywords)
end
#
# Acceptable argument classes
#
@@ -2121,10 +2213,11 @@
#
class ParseError < RuntimeError
# Reason which caused the error.
Reason = 'parse error'
+ # :nodoc:
def initialize(*args, additional: nil)
@additional = additional
@arg0, = args
@args = args
@reason = nil
@@ -2271,23 +2364,23 @@
#
# Parses +self+ destructively in order and returns +self+ containing the
# rest arguments left unparsed.
#
- def order!(&blk) options.order!(self, &blk) end
+ def order!(**keywords, &blk) options.order!(self, **keywords, &blk) end
#
# Parses +self+ destructively in permutation mode and returns +self+
# containing the rest arguments left unparsed.
#
- def permute!() options.permute!(self) end
+ def permute!(**keywords) options.permute!(self, **keywords) end
#
# Parses +self+ destructively and returns +self+ containing the
# rest arguments left unparsed.
#
- def parse!() options.parse!(self) end
+ def parse!(**keywords) options.parse!(self, **keywords) end
#
# Substitution of getopts is possible as follows. Also see
# OptionParser#getopts.
#
@@ -2296,21 +2389,22 @@
# eval "$OPT_#{opt.gsub(/[^A-Za-z0-9_]/, '_')} = val"
# end
# rescue OptionParser::ParseError
# end
#
- def getopts(*args, symbolize_names: false)
- options.getopts(self, *args, symbolize_names: symbolize_names)
+ def getopts(*args, symbolize_names: false, **keywords)
+ options.getopts(self, *args, symbolize_names: symbolize_names, **keywords)
end
#
# Initializes instance variable.
#
def self.extend_object(obj)
super
obj.instance_eval {@optparse = nil}
end
- def initialize(*args)
+
+ def initialize(*args) # :nodoc:
super
@optparse = nil
end
end