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"]