From 050a87d02915400ee67ca6650dbaa131732f7c73 Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 10 Mar 2025 13:12:34 +0900 Subject: [PATCH 1/2] Add post-check of value Fix #80 --- lib/optparse.rb | 38 +++++++++++++++++++++++++++------- test/optparse/test_placearg.rb | 9 +++++++- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/lib/optparse.rb b/lib/optparse.rb index a45d99d..731d9bd 100644 --- a/lib/optparse.rb +++ b/lib/optparse.rb @@ -461,6 +461,10 @@ def self.candidate(key, icase = false, pat = nil, &block) candidates end + def self.completable?(key) + String.try_convert(key) or defined?(key.id2name) + end + def candidate(key, icase = false, pat = nil, &_) Completion.candidate(key, icase, pat, &method(:each)) end @@ -544,11 +548,11 @@ def self.pattern def initialize(pattern = nil, conv = nil, short = nil, long = nil, arg = nil, - desc = ([] if short or long), block = nil, &_block) + desc = ([] if short or long), block = nil, values = nil, &_block) raise if Array === pattern block ||= _block - @pattern, @conv, @short, @long, @arg, @desc, @block = - pattern, conv, short, long, arg, desc, block + @pattern, @conv, @short, @long, @arg, @desc, @block, @values = + pattern, conv, short, long, arg, desc, block, values end # @@ -581,11 +585,15 @@ def parse_arg(arg) # :nodoc: # exception. # def conv_arg(arg, val = []) # :nodoc: + v, = *val if conv val = conv.call(*val) else val = proc {|v| v}.call(*val) end + if @values + @values.include?(val) or raise InvalidArgument, v + end return arg, block, val end private :conv_arg @@ -780,7 +788,7 @@ class PlacedArgument < self # Returns nil if argument is not present or begins with '-' and is not '-'. # def parse(arg, argv, &error) - if !(val = arg) and !(val = argv[0])&.match?(/\A(?!-.)/) + if !(val = arg) and (argv.empty? or /\A-./ =~ (val = argv[0])) return nil, block end opt = (val = parse_arg(val, &error))[1] @@ -1464,6 +1472,7 @@ def make_switch(opts, block = nil) klass = nil q, a = nil has_arg = false + values = nil opts.each do |o| # argument class @@ -1477,7 +1486,7 @@ def make_switch(opts, block = nil) end # directly specified pattern(any object possible to match) - if (!(String === o || Symbol === o)) and o.respond_to?(:match) + if !Completion.completable?(o) and o.respond_to?(:match) pattern = notwice(o, pattern, 'pattern') if pattern.respond_to?(:convert) conv = pattern.method(:convert).to_proc @@ -1492,6 +1501,11 @@ def make_switch(opts, block = nil) when Proc, Method block = notwice(o, block, 'block') when Array, Hash + if Array === o + o, v = o.partition {|v| Completion.completable?(v)} + values = notwice(v, values, 'values') unless v.empty? + next if o.empty? + end case pattern when CompletingHash when nil @@ -1500,7 +1514,9 @@ def make_switch(opts, block = nil) else raise ArgumentError, "argument pattern given twice" end - o.each {|pat, *v| pattern[pat.to_s] = v.fetch(0) {pat}} + o.each {|pat, *v| pattern[pat] = v.fetch(0) {pat}} + when Range + values = notwice(o, values, 'values') when Module raise ArgumentError, "unsupported argument type: #{o}", ParseError.filter_backtrace(caller(4)) when *ArgumentStyle.keys @@ -1568,12 +1584,18 @@ def make_switch(opts, block = nil) end default_pattern, conv = search(:atype, default_style.pattern) unless default_pattern + if Range === values and klass + unless (!values.begin or klass === values.begin) and + (!values.end or klass === values.end) + raise ArgumentError, "range does not match class" + end + end if !(short.empty? and long.empty?) if has_arg and default_style == Switch::NoArgument default_style = Switch::RequiredArgument end s = (style || default_style).new(pattern || default_pattern, - conv, sdesc, ldesc, arg, desc, block) + conv, sdesc, ldesc, arg, desc, block, values) elsif !block if style or pattern raise ArgumentError, "no switch given", ParseError.filter_backtrace(caller) @@ -1582,7 +1604,7 @@ def make_switch(opts, block = nil) else short << pattern s = (style || default_style).new(pattern, - conv, nil, nil, arg, desc, block) + conv, nil, nil, arg, desc, block, values) end return s, short, long, (not_style.new(not_pattern, not_conv, sdesc, ldesc, nil, desc, block) if not_style), diff --git a/test/optparse/test_placearg.rb b/test/optparse/test_placearg.rb index 511541f..36edf40 100644 --- a/test/optparse/test_placearg.rb +++ b/test/optparse/test_placearg.rb @@ -8,7 +8,8 @@ def setup @opt.def_option("--option [VAL]") {|x| @flag = x} @opt.def_option("-T [level]", /^[0-4]$/, Integer) {|x| @topt = x} @opt.def_option("--enum [VAL]", [:Alpha, :Bravo, :Charlie]) {|x| @enum = x} - @opt.def_option("--integer [VAL]", [1, 2, 3]) {|x| @integer = x} + @opt.def_option("--integer [VAL]", Integer, [1, 2, 3]) {|x| @integer = x} + @opt.def_option("--range [VAL]", Integer, 1..3) {|x| @range = x} @topt = nil @opt.def_option("-n") {} @opt.def_option("--regexp [REGEXP]", Regexp) {|x| @reopt = x} @@ -105,4 +106,10 @@ def test_enum_conversion assert_equal([], no_error {@opt.parse!(%w"--integer=1")}) assert_equal(1, @integer) end + + def test_enum_range + assert_equal([], no_error {@opt.parse!(%w"--range=1")}) + assert_equal(1, @range) + assert_raise(OptionParser::InvalidArgument) {@opt.parse!(%w"--range=4")} + end end From e1957d7392bfe9083ca6c0c1779e4f3074859f5b Mon Sep 17 00:00:00 2001 From: Nobuyoshi Nakada Date: Mon, 10 Mar 2025 16:53:16 +0900 Subject: [PATCH 2/2] [DOC] Mention about post-check --- doc/optparse/option_params.rdoc | 12 +++++++++++- doc/optparse/ruby/matched_values.rb | 6 ++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/doc/optparse/option_params.rdoc b/doc/optparse/option_params.rdoc index 48de12e..575ee66 100644 --- a/doc/optparse/option_params.rdoc +++ b/doc/optparse/option_params.rdoc @@ -396,12 +396,22 @@ Executions: $ ruby matched_values.rb --help Usage: matched_values [options] --xxx XXX Matched values + --yyy YYY Check by range + --zzz ZZZ Check by list $ ruby matched_values.rb --xxx foo ["--xxx", "foo"] $ ruby matched_values.rb --xxx FOO ["--xxx", "FOO"] $ ruby matched_values.rb --xxx bar - matched_values.rb:6:in '
': invalid argument: --xxx bar (OptionParser::InvalidArgument) + matched_values.rb:12:in '
': invalid argument: --xxx bar (OptionParser::InvalidArgument) + $ ruby matched_values.rb --yyy 1 + ["--yyy", 1] + $ ruby matched_values.rb --yyy 4 + matched_values.rb:12:in '
': invalid argument: --yyy 4 (OptionParser::InvalidArgument) + $ ruby matched_values.rb --zzz 1 + ["--zzz", 1] + $ ruby matched_values.rb --zzz 2 + matched_values.rb:12:in '
': invalid argument: --zzz 2 (OptionParser::InvalidArgument) === Argument Converters diff --git a/doc/optparse/ruby/matched_values.rb b/doc/optparse/ruby/matched_values.rb index f184ca8..a1aba14 100644 --- a/doc/optparse/ruby/matched_values.rb +++ b/doc/optparse/ruby/matched_values.rb @@ -3,4 +3,10 @@ parser.on('--xxx XXX', /foo/i, 'Matched values') do |value| p ['--xxx', value] end +parser.on('--yyy YYY', Integer, 'Check by range', 1..3) do |value| + p ['--yyy', value] +end +parser.on('--zzz ZZZ', Integer, 'Check by list', [1, 3, 4]) do |value| + p ['--zzz', value] +end parser.parse!