Skip to content

Commit 0207c0a

Browse files
committed
[WIP] subcommand parser
1 parent 51f7e06 commit 0207c0a

File tree

2 files changed

+53
-4
lines changed

2 files changed

+53
-4
lines changed

lib/optparse.rb

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1148,6 +1148,7 @@ def initialize(banner = nil, width = 32, indent = ' ' * 4)
11481148
@summary_indent = indent
11491149
@default_argv = ARGV
11501150
@require_exact = false
1151+
@subparsers = nil
11511152
add_officious
11521153
yield self if block_given?
11531154
end
@@ -1170,6 +1171,12 @@ def self.terminate(arg = nil)
11701171
throw :terminate, arg
11711172
end
11721173

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

@@ -1622,12 +1629,17 @@ def order(*argv, into: nil, &nonopt)
16221629
# Non-option arguments remain in +argv+.
16231630
#
16241631
def order!(argv = default_argv, into: nil, &nonopt)
1625-
setter = ->(name, val) {into[name.to_sym] = val} if into
1632+
setter = into.extend(SymSetter).method(:sym_set) if into
16261633
parse_in_order(argv, setter, &nonopt)
16271634
end
16281635

1636+
module SymSetter
1637+
def sym_set(name, val)
1638+
self[name.to_sym] = val
1639+
end
1640+
end
16291641
def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc:
1630-
opt, arg, val, rest = nil
1642+
opt, arg, val, rest, sub = nil
16311643
nonopt ||= proc {|a| throw :terminate, a}
16321644
argv.unshift(arg) if arg = catch(:terminate) {
16331645
while arg = argv.shift
@@ -1692,6 +1704,17 @@ def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc:
16921704

16931705
# non-option argument
16941706
else
1707+
# sub-command
1708+
if (key, (sub, block) = @subparsers&.complete(arg))
1709+
block.call if block
1710+
if setter
1711+
into = setter.receiver.class.new.extend(SymSetter)
1712+
setter.call(key, into)
1713+
setter = into.method(:sym_set)
1714+
end
1715+
return sub.parse_in_order(argv, setter, &nonopt)
1716+
end
1717+
16951718
catch(:prune) do
16961719
visit(:each_option) do |sw0|
16971720
sw = sw0
@@ -1709,7 +1732,7 @@ def parse_in_order(argv = default_argv, setter = nil, &nonopt) # :nodoc:
17091732

17101733
argv
17111734
end
1712-
private :parse_in_order
1735+
protected :parse_in_order
17131736

17141737
#
17151738
# Parses command line arguments +argv+ in permutation mode and returns
@@ -1728,6 +1751,9 @@ def permute(*argv, into: nil)
17281751
# Non-option arguments remain in +argv+.
17291752
#
17301753
def permute!(argv = default_argv, into: nil)
1754+
if @subparsers
1755+
raise "cannot parse in permutation mode with subparsers"
1756+
end
17311757
nonopts = []
17321758
order!(argv, into: into, &nonopts.method(:<<))
17331759
argv[0, 0] = nonopts
@@ -1751,7 +1777,7 @@ def parse(*argv, into: nil)
17511777
# Non-option arguments remain in +argv+.
17521778
#
17531779
def parse!(argv = default_argv, into: nil)
1754-
if ENV.include?('POSIXLY_CORRECT')
1780+
if @subparsers or ENV.include?('POSIXLY_CORRECT')
17551781
order!(argv, into: into)
17561782
else
17571783
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)