Skip to content

Commit 99af0a0

Browse files
committed
[WIP] subcommand parser
1 parent 7366189 commit 99af0a0

File tree

2 files changed

+54
-4
lines changed

2 files changed

+54
-4
lines changed

lib/optparse.rb

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1149,6 +1149,7 @@ def initialize(banner = nil, width = 32, indent = ' ' * 4)
11491149
@default_argv = ARGV
11501150
@require_exact = false
11511151
@raise_unknown = true
1152+
@subparsers = nil
11521153
add_officious
11531154
yield self if block_given?
11541155
end
@@ -1171,6 +1172,12 @@ def self.terminate(arg = nil)
11711172
throw :terminate, arg
11721173
end
11731174

1175+
def subparser(name, *rest, &block)
1176+
parser = self.class.new(*rest)
1177+
(@subparsers ||= CompletingHash.new)[name] = [parser, block]
1178+
parser
1179+
end
1180+
11741181
@stack = [DefaultList]
11751182
def self.top() DefaultList end
11761183

@@ -1626,12 +1633,18 @@ def order(*argv, into: nil, &nonopt)
16261633
# Non-option arguments remain in +argv+.
16271634
#
16281635
def order!(argv = default_argv, into: nil, &nonopt)
1629-
setter = ->(name, val) {into[name.to_sym] = val} if into
1636+
setter = into.extend(SymSetter).method(:sym_set) if into
16301637
parse_in_order(argv, setter, &nonopt)
16311638
end
16321639

1640+
module SymSetter
1641+
def sym_set(name, val)
1642+
self[name.to_sym] = val
1643+
end
1644+
end
1645+
16331646
def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc:
1634-
opt, arg, val, rest = nil
1647+
opt, arg, val, rest, sub = nil
16351648
nonopt ||= proc {|a| throw :terminate, a}
16361649
argv.unshift(arg) if arg = catch(:terminate) {
16371650
while arg = argv.shift
@@ -1699,6 +1712,17 @@ def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc:
16991712

17001713
# non-option argument
17011714
else
1715+
# sub-command
1716+
if (key, (sub, block) = @subparsers&.complete(arg))
1717+
block.call if block
1718+
if setter
1719+
into = setter.receiver.class.new.extend(SymSetter)
1720+
setter.call(key, into)
1721+
setter = into.method(:sym_set)
1722+
end
1723+
return sub.parse_in_order(argv, setter, &nonopt)
1724+
end
1725+
17021726
catch(:prune) do
17031727
visit(:each_option) do |sw0|
17041728
sw = sw0
@@ -1716,7 +1740,7 @@ def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc:
17161740

17171741
argv
17181742
end
1719-
private :parse_in_order
1743+
protected :parse_in_order
17201744

17211745
#
17221746
# Parses command line arguments +argv+ in permutation mode and returns
@@ -1735,6 +1759,9 @@ def permute(*argv, into: nil)
17351759
# Non-option arguments remain in +argv+.
17361760
#
17371761
def permute!(argv = default_argv, into: nil)
1762+
if @subparsers
1763+
raise "cannot parse in permutation mode with subparsers"
1764+
end
17381765
nonopts = []
17391766
order!(argv, into: into, &nonopts.method(:<<))
17401767
argv[0, 0] = nonopts
@@ -1758,7 +1785,7 @@ def parse(*argv, into: nil)
17581785
# Non-option arguments remain in +argv+.
17591786
#
17601787
def parse!(argv = default_argv, into: nil)
1761-
if ENV.include?('POSIXLY_CORRECT')
1788+
if @subparsers or ENV.include?('POSIXLY_CORRECT')
17621789
order!(argv, into: into)
17631790
else
17641791
permute!(argv, into: into)

sample/optparse/subcommand.rb

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#! /usr/bin/ruby
2+
# contributed by Minero Aoki.
3+
4+
require 'optparse'
5+
6+
opts = {}
7+
parser = OptionParser.new
8+
parser.on('-i') { opts["i"] = true }
9+
parser.on('-o') { puts["o"] = true }
10+
11+
parser.subparser('add') {opts[:add] = {}}
12+
.on('-i') { opts[:add]["i"] = true }
13+
parser.subparser('del') {opts[:del] = {}}.then do |sub|
14+
sub.on('-i') { opts[:del]["i"] = true }
15+
end
16+
parser.subparser('list') {opts[:list] = {}}.then do |sub|
17+
sub.on('-iN', Integer) {|i| opts[:list]["i"] = i }
18+
end
19+
20+
h = {}
21+
p parser.parse!(ARGV, into: h)
22+
p h
23+
p opts

0 commit comments

Comments
 (0)