我需要一个解决方案,它不会抛出OptionParser :: InvalidOption
异常,并且在当前的答案中找不到优雅的解决方案。这个猴子补丁基于一个其他答案,但是将其整理并使其更像当前的order!
语义。但是,请参见下面关于多次选项解析固有问题的未解决问题。
class OptionParser
def order_recognized!(args)
extra_opts = []
begin
order!(args) { |a| extra_opts << a }
rescue OptionParser::InvalidOption => e
extra_opts << e.args[0]
retry
end
args[0, 0] = extra_opts
end
end
这个功能与order!
类似,唯一的区别是如果出现无法识别的开关,则会将其保留在ARGV
中,而不是抛出InvalidOption
异常。
RSpec测试:
describe OptionParser do
before(:each) do
@parser = OptionParser.new do |opts|
opts.on('--foo=BAR', OptionParser::DecimalInteger) { |f| @found << f }
end
@found = []
end
describe 'order_recognized!' do
it 'finds good switches using equals (--foo=3)' do
argv = %w(one two --foo=3 three)
@parser.order_recognized!(argv)
expect(@found).to eq([3])
expect(argv).to eq(%w(one two three))
end
it 'leaves unknown switches alone' do
argv = %w(one --bar=2 two three)
@parser.order_recognized!(argv)
expect(@found).to eq([])
expect(argv).to eq(%w(one --bar=2 two three))
end
it 'leaves unknown single-dash switches alone' do
argv = %w(one -bar=2 two three)
@parser.order_recognized!(argv)
expect(@found).to eq([])
expect(argv).to eq(%w(one -bar=2 two three))
end
it 'finds good switches using space (--foo 3)' do
argv = %w(one --bar=2 two --foo 3 three)
@parser.order_recognized!(argv)
expect(@found).to eq([3])
expect(argv).to eq(%w(one --bar=2 two three))
end
it 'finds repeated args' do
argv = %w(one --foo=1 two --foo=3 three)
@parser.order_recognized!(argv)
expect(@found).to eq([1, 3])
expect(argv).to eq(%w(one two three))
end
it 'maintains repeated non-switches' do
argv = %w(one --foo=1 one --foo=3 three)
@parser.order_recognized!(argv)
expect(@found).to eq([1, 3])
expect(argv).to eq(%w(one one three))
end
it 'maintains repeated unrecognized switches' do
argv = %w(one --bar=1 one --bar=3 three)
@parser.order_recognized!(argv)
expect(@found).to eq([])
expect(argv).to eq(%w(one --bar=1 one --bar=3 three))
end
it 'still raises InvalidArgument' do
argv = %w(one --foo=bar)
expect { @parser.order_recognized!(argv) }.to raise_error(OptionParser::InvalidArgument)
end
it 'still raises MissingArgument' do
argv = %w(one --foo)
expect { @parser.order_recognized!(argv) }.to raise_error(OptionParser::MissingArgument)
end
end
end
问题:通常情况下,OptionParser允许使用缩写选项,只要有足够的字符来唯一识别所需选项即可。但是,多阶段解析选项会破坏这种功能,因为OptionParser在第一次解析时无法看到所有可能的参数。例如:
describe OptionParser do
context 'one parser with similar prefixed options' do
before(:each) do
@parser1 = OptionParser.new do |opts|
opts.on('--foobar=BAR', OptionParser::DecimalInteger) { |f| @found_foobar << f }
opts.on('--foo=BAR', OptionParser::DecimalInteger) { |f| @found_foo << f }
end
@found_foobar = []
@found_foo = []
end
it 'distinguishes similar prefixed switches' do
argv = %w(--foo=3 --foobar=4)
@parser1.order_recognized!(argv)
expect(@found_foobar).to eq([4])
expect(@found_foo).to eq([3])
end
end
context 'two parsers in separate passes' do
before(:each) do
@parser1 = OptionParser.new do |opts|
opts.on('--foobar=BAR', OptionParser::DecimalInteger) { |f| @found_foobar << f }
end
@parser2 = OptionParser.new do |opts|
opts.on('--foo=BAR', OptionParser::DecimalInteger) { |f| @found_foo << f }
end
@found_foobar = []
@found_foo = []
end
it 'confuses similar prefixed switches' do
argv = %w(--foo=3 --foobar=4)
@parser1.order_recognized!(argv)
@parser2.order_recognized!(argv)
expect(@found_foobar).to eq([3, 4])
expect(@found_foo).to eq([])
end
end
end