Optparse [feature request: subcommand support]

After 2 1/2 years, it looks as if I'll finally get off of my old CVS
snapshot of ruby 1.7.2 (phew!). As a result, I can now look at using a
lot of the huge new standard library.

optparse is one I'm particularly interested in. Earlier this year I saw
it, and since I couldn't use it, I copied some of its features as it
seems to be much better than getoptlong. I'm missing some of them which
I want, however (type coercion and keyword completion principally). I'd
like to convert my stuff over to the OptionParser when 1.8.1 gets rolled out.

I do have one feature that optparse appears to lack, and I'm just
wondering if there is any interest in it (here's the feature request).

The feature is the ability to do what I call CVS-style subcommands, i.e.:

vor-lord:scaf_flow/scaf_flow> cvs help
Unknown command: `help'

CVS commands are:
add Add a new file/directory to the repository
admin Administration front end for rcs
annotate Show last revision where each line was modified
checkout Checkout sources for editing
commit Check files into the repository
....

I then want to be able to have options that are local to a subcommand,
as well as global options, i.e.:

Usage: cvs [cvs-options] command [command-options-and-arguments]

The implementation of this should be quite trivial. (In my own
implementation I just have a hash of objects, one of which is a default
object, which is operated on when no name is given. That said, I don't
know much about the structure of OptionParser and it may not
lend itself to an easy modification such as this).

For example, one could do something like:

opts.on('-r', '--recursive', 'operate recursively') do
   ...
end
opts.on_cmd('admin', '-r', '--remove', 'removes something') do |arg|
   ...
end

As such things can even be reused (as in the above example, -r can be
global or local, or both):

foo -r admin -r '*Helper*'

There would need to be a little bit of smarts in the usage string generator, but by and large it doesn't seem to be too difficult.

Does this sound reasonable? Is there anyone else who'd like this kind of
thing? Or am I on my own?

Thanks for reading this far. I didn't find anything related to this in a search of the archives (hopefully I didn't miss anything).

Brett Williams said:
[...]

The feature is the ability to do what I call CVS-style subcommands, i.e.:

[...]

Does this sound reasonable? Is there anyone else who'd like this kind of
thing? Or am I on my own?

RubyGems uses CVS style commands, so it would have been useful there.

One question: Is it worth loading up optparse with this functionality, or
does it make sense to put it in a different object?

···

--
-- Jim Weirich jim@weirichhouse.org http://onestepback.org
-----------------------------------------------------------------
"Beware of bugs in the above code; I have only proved it correct,
not tried it." -- Donald Knuth (in a memo to Peter van Emde Boas)

Jim Weirich wrote:

Brett Williams said:
[...]

The feature is the ability to do what I call CVS-style subcommands, i.e.:

[...]

Does this sound reasonable? Is there anyone else who'd like this kind of
thing? Or am I on my own?

RubyGems uses CVS style commands, so it would have been useful there.

One question: Is it worth loading up optparse with this functionality, or
does it make sense to put it in a different object?

It should be easy to make as a wrapper around OptionParser objects (the same way my current implementation is a wrapper around Getoptlong objects). It seems rather simple to be yet another library dependency to add, but perhaps it is not of wide enough interest to justify integrating in to the standard.

Different class, IMO. I think the RubyGems implementation is quite
good, with the Command class. Each command (admin, checkout, ...) is
a subclass of Command and specifies its own optparse options. In
this way optparse and Command work together nicely.

In CVS there are some options shared across commands, so you can do
this, for instance:

  module LocalRecursiveOptions
    def add_local_recursive_options
      add_option('-L', 'Local command only, no recursion') do
        options[:domain] = :local
      end
      add_option('-r', 'Run command on all subdirectories recursively') do
        options[:domain] = :recursive
      end
    end
  end

  ...

  class UpdateCommand < Command
    include LocalRecursiveOptions
    def initialize
      add_option('-P', 'Prune empty directories') do ... end
      ...
      add_local_recursive_options # <--------------
    end
  end

  class LogCommand < Command
    include LocalRecursiveOptions
    def initialize
      add_option('-h', 'Only print header') do ... end
      ...
      add_local_recursive_options # <--------------
    end
  end

I plan to put this command framework into its own library and include
it in addlib -- sometime in the next 12 months. If others are
interested in this approach or any other, please go ahead.

Cheers,
Gavin

···

On Tuesday, August 24, 2004, 7:24:19 AM, Jim wrote:

Brett Williams said:
[...]

The feature is the ability to do what I call CVS-style subcommands, i.e.:

[...]

Does this sound reasonable? Is there anyone else who'd like this kind of
thing? Or am I on my own?

RubyGems uses CVS style commands, so it would have been useful there.

One question: Is it worth loading up optparse with this functionality, or
does it make sense to put it in a different object?