Ruby optparse странное поведение с обработкой исключений

Я работал над тем, чтобы сделать инструмент CLI ruby ​​более надежным с обработкой ошибок компонента CLI этого инструмента. Я использую optparse, и документация показывает, что флаги могут иметь обязательные и необязательные аргументы. Я просто вижу странное / раздражающее поведение.

В одном случае, когда аргумент флага является обязательным (с использованием знака равенства), я пытался заставить его потерпеть неудачу, но он все еще работал, он просто взял следующий флаг ('-u') в качестве аргумента этого флага, который приводит к тому, что остальная часть синтаксического анализа теряет смысл. Я думаю, что это нормально, так как "-...." может быть допустимым, но, поскольку я использую знак равенства для установки значения переключателя, я предполагаю, что назначение стиля "пробел" не будет работать,

op = OptionParser.new do |x|
  x.on("-u", "--user=USER", "user flag") do |user|  options[:user] = user end
  x.on("-d", "--db=DATABASE", "database flag") do |db|  options[:db] = db end
end

Если я передам следующий ввод CLI:

myprog -u -d mydb positionalarg

Затем во время синтаксического анализа он задает для options[:user] значение -d, а для options[:db] значение nil, поскольку он не встречается, и вместо 1 имеется 2 позиционных аргумента. Очевидно, что это ошибка пользователя, но я хочу отловить это и отображает реальную ошибку, а не просто возвращает ошибку (в случае инструмента), что только один позиционный аргумент должен быть передан из следующего списка:... Истинная проблема в том, что флаг -u отсутствует, это обязательный аргумент так что, учитывая, что у optparse есть исключение ArgumentMissing, я предполагаю, что это то, что.parse! бросил бы.

Однако с флагом, имеющим необязательный аргумент (с использованием знака равенства), он фактически необязателен, если вы выполните: -a -b= что-то, но это не работает: -a что-то -b = что-то еще. Единственный способ форсировать значение - использовать знак равенства: -a= что-то -b = что-то еще. Учитывая предыдущее поведение с обязательным аргументом со знаком равенства, я бы не подумал, что так и будет. Пример:

op = OptionParser.new do |x|
  x.on("-u", "--user[=USER]", "user flag") do |user|  options[:user] = user unless user.nil? end
  x.on("-d", "--db=DATABASE", "database flag") do |db|  options[:db] = db end
end

Итак, с разбором:

myprog -u -d mydb positionalarg

Тогда options[:user] равен nil (ок), а options[:db] равен mydb, и остается один позиционный аргумент.

С этим разбором:

myprog -u myuser -d mydb positionalarg

Тогда options[:user] равен nil (не в порядке), а options[:db] равен mydb, и остаются два позиционных аргумента: myuser и positionalarg (не в порядке). Моя проверка ошибок, опять же, barfs с позиционным счетчиком arg. Кажется, что если с обязательными аргументами флага, что и пробел, и = работает, то для необязательных аргументов флага это должно быть то же самое, но это не так.

Другая проблема заключается в том, что флаги с необязательными аргументами (с использованием пробела) хороши, за исключением случаев, когда они находятся в конце команды, а затем принимают позиционный аргумент в качестве аргумента flags.

Пример:

op = OptionParser.new do |x|
  x.on("-u", "--user [USER]", "user flag") do |user|  options[:user] = user unless user.nil? end
  x.on("-d", "--db=DATABASE", "database flag") do |db|  options[:db] = db end
end

Итак, с разбором:

myprog -d mydb -u positionalarg

Тогда options[:db] - это mydb (ok), а options[:user] - positionalarg, и позиционных аргументов не осталось. Обратите внимание, что -d mydb работает с пробелом, даже если я указываю его со знаком равенства.

Похоже, что многие люди делают CLI с ruby, делая optparse .parse! и взять оставшиеся записи в ARGV в качестве позиционных аргументов, но я думаю, что было бы лучше убрать позиционные аргументы с дальнего конца, прежде чем передавать ARGV в optparse (за исключением того, что происходит сбой в случае переменного числа позиционных аргументов.

Я уверен, что могу программировать вокруг всего этого, но я бы предпочел не делать этого, если есть способы сделать это в optparse, о котором я не знаю.

Возможно, лучше всего было бы избегать флагов с необязательными аргументами:), но любой совет был бы оценен.

1 ответ

Почему вы используете = в определении вариантов? Пример в документации не использует их.

Если я определю это MWE как test.rb:

require 'optparse'

puts "\n=Call with #{ARGV.inspect}"
options = {}

op = OptionParser.new do |x|
  x.on("-u", "--user [USER]", "user flag") do |user|  options[:user] = user unless user.nil? end
  x.on("-d", "--db DATABASE", "database flag") do |db|  options[:db] = db end
end

op.parse!

puts "Options: #{options.inspect}"
puts "ARGV     #{ARGV.inspect}"

И назовите его этим batfile (Windows, удалите @echo off для Linux):

@echo off
test.rb -h
test.rb -u -d mydb positionalarg
test.rb -u myuser -d mydb positionalarg
test.rb --user=myuser -d mydb positionalarg

Я получил:

=Call with ["-h"]
Usage: test [options]
    -u, --user [USER]                user flag
    -d, --db DATABASE                database flag

=Call with ["-u", "-d", "mydb", "positionalarg"]
Options: {:db=>"mydb"}
ARGV     ["positionalarg"]

=Call with ["-u", "myuser", "-d", "mydb", "positionalarg"]
Options: {:user=>"myuser", :db=>"mydb"}
ARGV     ["positionalarg"]

=Call with ["--user=myuser", "-d", "mydb", "positionalarg"]
Options: {:user=>"myuser", :db=>"mydb"}
ARGV     ["positionalarg"]
Другие вопросы по тегам