diff --git a/lib/optparse.rb b/lib/optparse.rb index 2ee592954c78ea..08abca220f14e2 100644 --- a/lib/optparse.rb +++ b/lib/optparse.rb @@ -1654,21 +1654,21 @@ def separator(string) # # 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) { @@ -1679,7 +1679,7 @@ def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc: opt, rest = $1, $2 opt.tr!('_', '-') begin - if require_exact + if exact sw, = search(:long, opt) else sw, = complete(:long, opt, true) @@ -1714,7 +1714,7 @@ def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc: 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) @@ -1779,18 +1779,18 @@ def callback!(cb, max_arity, *args) # :nodoc: # []= 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 @@ -1802,20 +1802,20 @@ def permute!(argv = default_argv, into: nil) # values are stored there via []= 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 @@ -1838,7 +1838,7 @@ def parse!(argv = default_argv, into: nil) # # 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 @@ -1866,7 +1866,7 @@ def getopts(*args, symbolize_names: false) end end - parse_in_order(argv, result.method(:[]=)) + parse_in_order(argv, result.method(:[]=), **keywords) symbolize_names ? result.transform_keys(&:to_sym) : result end @@ -1979,10 +1979,10 @@ def candidate(word) # 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 @@ -1994,11 +1994,11 @@ def load(filename = nil, into: nil) '~/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 @@ -2011,10 +2011,10 @@ def load(filename = nil, into: nil) # # +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 # @@ -2331,19 +2331,19 @@ def options # 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 @@ -2356,8 +2356,8 @@ def parse!() options.parse!(self) 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 # diff --git a/test/optparse/test_optparse.rb b/test/optparse/test_optparse.rb index 3b9ccc756ace06..393b0334b136df 100644 --- a/test/optparse/test_optparse.rb +++ b/test/optparse/test_optparse.rb @@ -116,6 +116,35 @@ def test_require_exact assert_equal(false, @foo) end + def test_exact_option + @opt.def_option('-F', '--zrs=IRS', 'zrs') + %w(--zrs --zr --z -zfoo -z -F -Ffoo).each do |arg| + result = {} + @opt.parse([arg, 'foo'], into: result) + assert_equal({zrs: 'foo'}, result) + end + + [%w(--zrs foo), %w(--zrs=foo), %w(-F foo), %w(-Ffoo)].each do |args| + result = {} + @opt.parse(args, into: result, exact: true) + assert_equal({zrs: 'foo'}, result) + end + + assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(--zr foo), exact: true)} + assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(--z foo), exact: true)} + assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(-zrs foo), exact: true)} + assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(-zr foo), exact: true)} + assert_raise(OptionParser::InvalidOption) {@opt.parse(%w(-z foo), exact: true)} + + @opt.def_option('-f', '--[no-]foo', 'foo') {|arg| @foo = arg} + @opt.parse(%w[-f], exact: true) + assert_equal(true, @foo) + @opt.parse(%w[--foo], exact: true) + assert_equal(true, @foo) + @opt.parse(%w[--no-foo], exact: true) + assert_equal(false, @foo) + end + def test_raise_unknown @opt.def_option('--my-foo [ARG]') {|arg| @foo = arg} assert @opt.raise_unknown