2010-04-28 12 views
22

Me gustaría ser capaz de utilizar OptionParser de rubí para analizar los sub-comandos de la formaUsando OptionParser de rubí para analizar los sub-comandos

COMMAND [GLOBAL FLAGS] [SUB-COMMAND [SUB-COMMAND FLAGS]] 

como:

git branch -a 
gem list foo 

Sé que podría cambiar a una biblioteca de analizador de opciones diferente (como Trollop), pero estoy interesado en aprender cómo hacerlo desde OptionParser, ya que me gustaría aprender mejor la biblioteca.

¿Algún consejo?

+0

Sin consejos, aparte de una sugerencia para permanecer abierto a las direcciones de cambio. En mi experiencia, 'OptionParser' ha sido frustrante de usar por varias razones, una de ellas es la documentación deficiente, de ahí su pregunta. William Morgan, el autor de Trollop, no muestra piedad en su crítica (por ejemplo, ver http://stackoverflow.com/questions/897630/really-cheap-command-line-option-parsing-in-ruby y http://trollop.rubyforge.org). No puedo disputar lo que dice. – FMc

+1

@FM: Bueno, al igual que el autor de esa pregunta, estoy atrapado en una máquina donde importar bibliotecas es un PITA, así que estoy tratando de conformarme con las librerías estándar, como 'optparse'. – rampion

Respuesta

35

Lo descubrí. Necesito usar OptionParser#order!. Analizará todas las opciones desde el inicio de ARGV hasta que encuentre una no opción (que no es un argumento de opción), eliminando todo lo que procesa de ARGV, y luego se cerrará.

Así que sólo hay que hacer algo como:

global = OptionParser.new do |opts| 
    # ... 
end 
subcommands = { 
    'foo' => OptionParser.new do |opts| 
    # ... 
    end, 
    # ... 
    'baz' => OptionParser.new do |opts| 
    # ... 
    end 
} 

global.order! 
subcommands[ARGV.shift].order! 
+2

Como referencia, un ejemplo más completo es este [Gist] (https://gist.github.com/rkumar/445735). – sschuberth

+0

¿Qué pasa si 'foo' y' baz' comparten muchas opciones comunes? ¿Cómo evitar la repetición? –

+0

Fernando Á: simple, simplemente abstrae las opciones comunes de un método. 'def common_options (&blk) ; OptionParser.new {| opts | opts.on (...); ...; blk.call (opts)}; end', luego llama a ese método con un bloque para opciones específicas de subcomandos más tarde - 'subcommands = {'foo' => common_options {| opts | ...}, 'baz' => opciones comunes {| opts | ...}, ...}' – rampion

0

También hay otras joyas que usted puede mirar como main.

+0

¿Tiene algún documento? – rampion

+0

@rampion: Puede ver las muestras, por ejemplo, http://codeforpeople.com/lib/ruby/main/main-2.8.3/samples/f.rb –

1

Parece que la sintaxis OptionParser ha cambiado un poco. Tuve que usar lo siguiente para que la matriz de argumentos tuviera todas las opciones no analizadas por el objeto opts.

begin 
    opts.order!(arguments) 
rescue OptionParser::InvalidOption => io 
    # Prepend the invalid option onto the arguments array 
    arguments = io.recover(arguments) 
rescue => e 
    raise "Argument parsing failed: #{e.to_s()}" 
end 
0

GLI es el camino a seguir, https://github.com/davetron5000/gli. Un extracto de un tutorial:

#!/usr/bin/env ruby 
require 'gli' 
require 'hacer' 

include GLI::App 

program_desc 'A simple todo list' 

flag [:t,:tasklist], :default_value => File.join(ENV['HOME'],'.todolist') 

pre do |global_options,command,options,args| 
    $todo_list = Hacer::Todolist.new(global_options[:tasklist]) 
end 

command :add do |c| 
    c.action do |global_options,options,args| 
    $todo_list.create(args) 
    end 
end 

command :list do |c| 
    c.action do 
    $todo_list.list.each do |todo| 
     printf("%5d - %s\n",todo.todo_id,todo.text) 
    end 
    end 
end 

command :done do |c| 
    c.action do |global_options,options,args| 
    id = args.shift.to_i 
    $todo_list.list.each do |todo| 
     $todo_list.complete(todo) if todo.todo_id == id 
    end 
    end 
end 

exit run(ARGV) 

Usted puede encontrar el tutorial en http://davetron5000.github.io/gli/.

Cuestiones relacionadas