optparse2 0.6.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/optparse2/context.rb +51 -0
- data/lib/optparse2/switch-helpers.rb +171 -0
- data/lib/optparse2/version.rb +1 -1
- data/lib/optparse2.rb +132 -85
- metadata +3 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 3ccf871ad692772fb4e40ece995b2a812812d3416c51ead37d0fceea9a4b959f
|
|
4
|
+
data.tar.gz: eb74c3439fea8f47b70497d2f78e74849aa0cdc15ee2d25ec123b38710d7dea0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a01534549e3a919e0fa7d5c89d41ceb334d1d375d1a9b436a1e25efdb61c5aaf568ec46ee74e549607280cf6bfef12e40824b5c52eceae9a54138c565f7a8106
|
|
7
|
+
data.tar.gz: 211b3b24d8888ac8c802d2fe7aaf3dfbe134e9adf3fa2bdcb86a3c5c78adf8cef18f0c40b2ef45bdce4db48cd4efe0e55d7c0faa478bcd5fb1e5e10b077c2ae4
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
class OptParse2
|
|
2
|
+
# A Context is in charge of all option parsing
|
|
3
|
+
class Context
|
|
4
|
+
class << self
|
|
5
|
+
attr_accessor :current
|
|
6
|
+
|
|
7
|
+
def with_context(...)
|
|
8
|
+
context = new(...)
|
|
9
|
+
old, self.current = current, context
|
|
10
|
+
yield context
|
|
11
|
+
ensure
|
|
12
|
+
self.current = old
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
attr_reader :non_options, :deferred_options, :already_parsed_options
|
|
17
|
+
|
|
18
|
+
def initialize(into:, nonopt:)
|
|
19
|
+
@into = into
|
|
20
|
+
@nonopt = nonopt
|
|
21
|
+
|
|
22
|
+
@already_parsed_options = {}
|
|
23
|
+
@non_options = []
|
|
24
|
+
@deferred_options = {}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def add_non_option(non_option)
|
|
28
|
+
@non_options << non_option
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def handle_deferred!
|
|
32
|
+
@deferred_options.each do |key, deferred|
|
|
33
|
+
self[key] = deferred[:proc].call(deferred[:data]) if deferred[:proc]
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def key?(key)
|
|
38
|
+
@already_parsed_options.key?(key)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Directly assigns `value` to `key`, both in the internal list of parsed
|
|
42
|
+
# options, as well as in the `into:` option (if one is provided).
|
|
43
|
+
#
|
|
44
|
+
# Note that if `value` is `DONT_ASSIGN` nothing happens
|
|
45
|
+
def []=(key, value)
|
|
46
|
+
return if DONT_ASSIGN.equal?(value)
|
|
47
|
+
@already_parsed_options[key] = value
|
|
48
|
+
@into[key] = value if @into
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
class OptParse2
|
|
2
|
+
## Helpers is a mixin that contains methods to modify how the original `Switch` works
|
|
3
|
+
|
|
4
|
+
module Helpers
|
|
5
|
+
# Mark the switch as hidden (so it won't show up in the usage)
|
|
6
|
+
def set_hidden
|
|
7
|
+
def self.summarize(*) end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# The name of the switch; this is the key that's used when assigning options into an `into:`.
|
|
11
|
+
# It normally corresponds to the flag name (`--foo-bar` -> `foo-bar`), but can be overwritten
|
|
12
|
+
# as needed.
|
|
13
|
+
attr_writer :switch_name
|
|
14
|
+
def switch_name
|
|
15
|
+
defined?(@switch_name) ? @switch_name : super
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def set_multiple(multiple)
|
|
19
|
+
old_block = @block
|
|
20
|
+
sw = switch_name.to_sym
|
|
21
|
+
|
|
22
|
+
case multiple
|
|
23
|
+
in :first!
|
|
24
|
+
@block = ->(arg, **nil) do
|
|
25
|
+
ctx = OptParse2::Context.current
|
|
26
|
+
if ctx.deferred_options.key? sw
|
|
27
|
+
OptParse2::DONT_ASSIGN
|
|
28
|
+
else
|
|
29
|
+
ctx.deferred_options[sw] = {}
|
|
30
|
+
old_block ? old_block.call(arg) : arg
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
in :first
|
|
34
|
+
@block = ->(arg, **nil) do
|
|
35
|
+
ctx = OptParse2::Context.current
|
|
36
|
+
if ctx.deferred_options.key? sw
|
|
37
|
+
OptParse2::DONT_ASSIGN
|
|
38
|
+
else
|
|
39
|
+
ctx.deferred_options[sw] = {
|
|
40
|
+
proc: old_block ? proc{ old_block.call(arg) } : proc { arg }
|
|
41
|
+
}
|
|
42
|
+
OptParse2::DONT_ASSIGN
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
in :last!, nil
|
|
46
|
+
# don't do anything, this is the default behaviour
|
|
47
|
+
in :last
|
|
48
|
+
@block = ->(arg, **nil) do
|
|
49
|
+
ctx = OptParse2::Context.current
|
|
50
|
+
ctx.deferred_options[sw] = {
|
|
51
|
+
proc: old_block ? proc { old_block.call(arg) } : proc { arg }
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
OptParse2::DONT_ASSIGN
|
|
55
|
+
end
|
|
56
|
+
in :raise
|
|
57
|
+
@block = ->(arg) do
|
|
58
|
+
ctx = OptParse2::Context.current
|
|
59
|
+
if ctx.already_parsed_options.key? sw or ctx.deferred_options.key? sw
|
|
60
|
+
raise OptParse2::ParseError, "encountered repeated option"
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
old_block ? old_block.call(arg) : arg
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
in :count
|
|
67
|
+
@block = ->(amnt, **nil) do
|
|
68
|
+
ctx = OptParse2::Context.current
|
|
69
|
+
|
|
70
|
+
ctx.deferred_options[sw] ||= {
|
|
71
|
+
proc: old_block ? proc { |data| old_block.call(data) } : proc { |data| data },
|
|
72
|
+
data: 0
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if amnt == true || amnt.nil?
|
|
76
|
+
ctx.deferred_options[sw][:data] += 1
|
|
77
|
+
else
|
|
78
|
+
ctx.deferred_options[sw][:data] = amnt
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
OptParse2::DONT_ASSIGN
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
in :count!
|
|
85
|
+
@block = ->(amnt, *a, **k, &b) do
|
|
86
|
+
ctx = OptParse2::Context.current
|
|
87
|
+
|
|
88
|
+
ctx.deferred_options[sw] ||= { data: 0 }
|
|
89
|
+
|
|
90
|
+
if amnt == true || amnt.nil?
|
|
91
|
+
ctx.deferred_options[sw][:data] += 1
|
|
92
|
+
else
|
|
93
|
+
ctx.deferred_options[sw][:data] = amnt
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
new_amnt = old_block ? old_block.call(ctx.deferred_options[sw][:data], *a, **k, &b) : ctx.deferred_options[sw][:data]
|
|
97
|
+
ctx.deferred_options[sw][:data] = new_amnt unless OptParse2::DONT_ASSIGN.equal? new_amnt
|
|
98
|
+
|
|
99
|
+
new_amnt
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
in :collect | [:collect, _]
|
|
103
|
+
transform = Array(multiple)[1]
|
|
104
|
+
@block = ->(arg, **nil) do
|
|
105
|
+
ctx = OptParse2::Context.current
|
|
106
|
+
ctx.deferred_options[sw] ||= {
|
|
107
|
+
proc: proc { |data| old_block ? old_block.call(data) : data },
|
|
108
|
+
data: []
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
ctx.deferred_options[sw][:data] << (transform ? transform.(arg) : arg)
|
|
112
|
+
OptParse2::DONT_ASSIGN
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
else
|
|
116
|
+
raise ArgumentError, "invalid multiple type: #{multiple}", caller(2)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Same as `switch_name`, except it also will set the block to just return the original switch
|
|
121
|
+
# name as a symbol. Useful for group switches which don't actually have blocks:
|
|
122
|
+
# op.on '--interactive', key: :mode
|
|
123
|
+
# op.on '--force', key: :mode
|
|
124
|
+
# instead of:
|
|
125
|
+
# op.on '--interactive', key: :mode do :interactive end
|
|
126
|
+
# op.on '--force', key: :mode do :force end
|
|
127
|
+
# without this method, passing `--interactive` would just set `:mode` to `true`.
|
|
128
|
+
#
|
|
129
|
+
# This only happens if no block exists, and the argument does not take an arg.
|
|
130
|
+
def set_switch_name_possibly_block_value(val)
|
|
131
|
+
if @block.nil? && @arg.nil?
|
|
132
|
+
old_switch_name = switch_name.to_sym
|
|
133
|
+
@block = proc { old_switch_name }
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
self.switch_name = val
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Default values of switches are used when the switch is never passed in.
|
|
140
|
+
# If the `value` that's provided doesn't respond to `.call`, it's converted to a proc.
|
|
141
|
+
# If `bypass` is truthy, then the default value is never passed to the block's proc (if any)
|
|
142
|
+
def set_default(value, description, bypass)
|
|
143
|
+
if @arg.nil? && value != true && !bypass
|
|
144
|
+
raise ArgumentError, "Cannot supply a non-true default value to a flag which takes no arguments", caller(4)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
if defined? value.call
|
|
148
|
+
@default_proc = value
|
|
149
|
+
else
|
|
150
|
+
@default_proc = proc { |_switch_name| value }
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
@default_description = description
|
|
154
|
+
@default_bypass = bypass
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Calls the default proc to figure out what the default value is for this switch
|
|
158
|
+
def default_bypass? = @default_bypass
|
|
159
|
+
def default? = defined?(@default_proc)
|
|
160
|
+
def default = @default_proc&.call(switch_name)
|
|
161
|
+
|
|
162
|
+
def default_description = @default_description || default.inspect
|
|
163
|
+
def desc
|
|
164
|
+
return super unless defined? @default_proc
|
|
165
|
+
x = super
|
|
166
|
+
x << '' if x.empty?
|
|
167
|
+
x[-1] += " [default: #{default_description}]"
|
|
168
|
+
x
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
end
|
data/lib/optparse2/version.rb
CHANGED
data/lib/optparse2.rb
CHANGED
|
@@ -6,6 +6,8 @@ OptionParser2 = OptParse2 # Alias
|
|
|
6
6
|
|
|
7
7
|
require_relative "optparse2/version"
|
|
8
8
|
require_relative "optparse2/fixes"
|
|
9
|
+
require_relative "optparse2/switch-helpers"
|
|
10
|
+
require_relative "optparse2/context"
|
|
9
11
|
|
|
10
12
|
class OptParse2
|
|
11
13
|
class << self
|
|
@@ -13,63 +15,31 @@ class OptParse2
|
|
|
13
15
|
end
|
|
14
16
|
self.pos_set_banner = true
|
|
15
17
|
|
|
16
|
-
attr_reader :into
|
|
17
|
-
|
|
18
18
|
def initialize(...)
|
|
19
19
|
@defaults = Set[]
|
|
20
20
|
@positional = []
|
|
21
21
|
@required = Set[]
|
|
22
22
|
@rest = nil
|
|
23
|
-
@into = nil
|
|
24
23
|
@group = nil
|
|
25
24
|
self.pos_set_banner = OptParse2.pos_set_banner
|
|
26
25
|
super
|
|
27
26
|
end
|
|
28
27
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def set_hidden
|
|
32
|
-
def self.summarize(*) end
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
attr_writer :switch_name
|
|
36
|
-
def switch_name; defined?(@switch_name) ? @switch_name : super end
|
|
37
|
-
|
|
38
|
-
def set_switch_name_possibly_block_value(val)
|
|
39
|
-
if @block.nil? && @arg.nil?
|
|
40
|
-
q = switch_name.to_sym
|
|
41
|
-
@block = proc { q }
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
self.switch_name = val
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
# requires `switch_name`, `desc` to work
|
|
49
|
-
def set_default(value, description)
|
|
50
|
-
if defined? value.call
|
|
51
|
-
@default = value
|
|
52
|
-
else
|
|
53
|
-
@default = proc { value }
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
@default_description = description
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
def default = @default.call(switch_name)
|
|
60
|
-
def default_description = @default_description || default.inspect
|
|
61
|
-
def desc
|
|
62
|
-
return super unless defined? @default
|
|
63
|
-
x = super
|
|
64
|
-
x << '' if x.empty?
|
|
65
|
-
x[-1] += " [default: #{default_description}]"
|
|
66
|
-
x
|
|
67
|
-
end
|
|
68
|
-
end
|
|
28
|
+
# A constant that, when returned, will not actually assign objects inside `into:`s.
|
|
29
|
+
DONT_ASSIGN = Object.new.freeze
|
|
69
30
|
|
|
70
31
|
# Update `make_switch` to support OptParse2's keyword arguments
|
|
71
|
-
def make_switch(
|
|
72
|
-
|
|
32
|
+
def make_switch(
|
|
33
|
+
opts,
|
|
34
|
+
block,
|
|
35
|
+
hidden: false,
|
|
36
|
+
key: @group,
|
|
37
|
+
default: nodefault=true,
|
|
38
|
+
default_bypass: false,
|
|
39
|
+
default_description: nil,
|
|
40
|
+
required: false,
|
|
41
|
+
multiple: nil
|
|
42
|
+
)
|
|
73
43
|
sw, *rest = super(opts, block)
|
|
74
44
|
|
|
75
45
|
sw.extend Helpers
|
|
@@ -77,6 +47,7 @@ class OptParse2
|
|
|
77
47
|
sw.set_switch_name_possibly_block_value key
|
|
78
48
|
end
|
|
79
49
|
sw.set_hidden if hidden
|
|
50
|
+
sw.set_multiple multiple if multiple
|
|
80
51
|
|
|
81
52
|
if (not_style = rest[2])
|
|
82
53
|
not_style.extend Helpers
|
|
@@ -93,8 +64,10 @@ class OptParse2
|
|
|
93
64
|
|
|
94
65
|
if nodefault && default_description != nil
|
|
95
66
|
raise ArgumentError, "default: not supplied, but default_description: given"
|
|
67
|
+
elsif nodefault && default_bypass
|
|
68
|
+
raise ArgumentError, "default: not supplied, but default_bypass: given"
|
|
96
69
|
elsif not nodefault
|
|
97
|
-
sw.set_default(default, default_description)
|
|
70
|
+
sw.set_default(default, default_description, default_bypass)
|
|
98
71
|
@defaults << sw
|
|
99
72
|
end
|
|
100
73
|
|
|
@@ -117,31 +90,20 @@ class OptParse2
|
|
|
117
90
|
@group = old_group
|
|
118
91
|
end
|
|
119
92
|
|
|
120
|
-
|
|
121
|
-
if into.nil? && !@defaults.empty?
|
|
122
|
-
raise "cannot call `order!` without an `into:` if there are default values"
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
already_done = {}
|
|
126
|
-
already_done.define_singleton_method(:[]=) do |key, value|
|
|
127
|
-
super(key, value)
|
|
128
|
-
into[key] = value
|
|
129
|
-
end
|
|
130
|
-
|
|
131
|
-
# Really this shouldn't be on the class and should probably be passed to the block of each
|
|
132
|
-
# parameter as requested, but that requires a _significant_ amount of tinkering with `optparse`'s
|
|
133
|
-
# internals, which is not really in scope.
|
|
134
|
-
@into = already_done
|
|
93
|
+
alias _super_order! order!
|
|
135
94
|
|
|
136
|
-
|
|
95
|
+
# Parses all positional arguments from `argv` into `into`. Replaces `argv` with non-positional
|
|
96
|
+
# arguments when it's done.
|
|
97
|
+
private def parse_positional_arguments!(argv, context, keywords)
|
|
98
|
+
return if argv.empty? || @positional.empty?
|
|
137
99
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
argv2 = (non_options + result).each_with_index.flat_map { ["--*-positional-#{_2}", _1] }
|
|
100
|
+
# Prepend argument number to the argument array
|
|
101
|
+
argv.replace argv.each_with_index.flat_map { |value, idx| ["--*-positional-#{idx}", value] }
|
|
141
102
|
|
|
103
|
+
# Fetch all positional arguments using the same option parsing code
|
|
142
104
|
old_raise, self.raise_unknown = self.raise_unknown, false
|
|
143
105
|
begin
|
|
144
|
-
|
|
106
|
+
p _super_order!(argv, into: context, **keywords)
|
|
145
107
|
rescue OptParse::InvalidArgument => err
|
|
146
108
|
err.args[0] = @positional[err.args[0][/\d+/].to_i].name
|
|
147
109
|
raise
|
|
@@ -149,35 +111,74 @@ class OptParse2
|
|
|
149
111
|
self.raise_unknown = old_raise
|
|
150
112
|
end
|
|
151
113
|
|
|
152
|
-
|
|
114
|
+
# Delete any non-matching arguments. TODO: Can this be the return value of `_super_order!` ?
|
|
115
|
+
argv.replace argv.each_slice(2).map { |_flag_name, value| value }
|
|
116
|
+
end
|
|
153
117
|
|
|
118
|
+
# If a "rest" parameter was given, populates it. Also raises a ParseError exception for unexepcted
|
|
119
|
+
# positionals if no `.rest` parameter is present, `self.raise_unknown` is set, and at least one `.pos`
|
|
120
|
+
# positional argument was supplied
|
|
121
|
+
private def parse_rest_argument!(argv, context)
|
|
154
122
|
if @rest
|
|
155
|
-
if
|
|
156
|
-
raise ParseError, "at least #{@rest[:required]} trailing arguments required (only got #{
|
|
123
|
+
if argv.length < @rest.fetch(:required, 0)
|
|
124
|
+
raise ParseError, "at least #{@rest[:required]} trailing arguments required (only got #{argv.length})", caller(1)
|
|
157
125
|
end
|
|
158
126
|
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
elsif !
|
|
163
|
-
raise ParseError, "got unexpected positional argument: #{
|
|
164
|
-
else
|
|
165
|
-
argv2.each(&nonopt)
|
|
127
|
+
argv = @rest[:block] ? @rest[:block].call(argv) : argv
|
|
128
|
+
context[@rest[:key]] = argv.dup if @rest[:key]
|
|
129
|
+
argv.clear
|
|
130
|
+
elsif !argv.empty? && self.raise_unknown && !@positional.empty?
|
|
131
|
+
raise ParseError, "got unexpected positional argument: #{argv.first}", caller(1)
|
|
166
132
|
end
|
|
133
|
+
end
|
|
167
134
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
135
|
+
# Goes thru every default option, and calls
|
|
136
|
+
private def assign_defaults!(context)
|
|
137
|
+
visit :each_option do |sw|
|
|
138
|
+
next if !sw.default? || context.key?(key = sw.switch_name.to_sym)
|
|
139
|
+
|
|
140
|
+
if sw.default_bypass?
|
|
141
|
+
context[key] = sw.default
|
|
142
|
+
else
|
|
143
|
+
flag = sw.short.first || sw.long.first || raise("<INTERNAL ERROR: CAN THIS EVER HAPPEN?>")
|
|
144
|
+
_super_order! [flag, sw.default], into: context
|
|
145
|
+
end
|
|
172
146
|
end
|
|
147
|
+
end
|
|
173
148
|
|
|
149
|
+
private def ensure_all_required_arguments_were_supplied!(context)
|
|
174
150
|
@required.each do |key|
|
|
175
|
-
raise ParseError, "required option '#{key}' not provided" unless
|
|
151
|
+
raise ParseError, "required option '#{key}' not provided" unless context.key? key.to_sym
|
|
176
152
|
end
|
|
153
|
+
end
|
|
177
154
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
155
|
+
def order!(argv = default_argv, into: nil, **keywords, &nonopt)
|
|
156
|
+
Context.with_context into:, nonopt: do |context|
|
|
157
|
+
|
|
158
|
+
# Parse all normal options in the command line
|
|
159
|
+
non_options = []
|
|
160
|
+
trailing_options = super(argv, into: context, **keywords, &non_options.method(:<<))
|
|
161
|
+
not_matched_options = non_options + trailing_options
|
|
162
|
+
|
|
163
|
+
# Now parse positional arguments and the "rest" argument
|
|
164
|
+
parse_positional_arguments!(not_matched_options, context, keywords)
|
|
165
|
+
parse_rest_argument!(not_matched_options, context)
|
|
166
|
+
|
|
167
|
+
context.handle_deferred!
|
|
168
|
+
|
|
169
|
+
# Now handle defaults---anything with a default that hasn't been assigned so far is set
|
|
170
|
+
assign_defaults!(context)
|
|
171
|
+
|
|
172
|
+
# Now that all arguments are parsed, and the defaults have been handled, check to make sure
|
|
173
|
+
# that all required arguments are handled.
|
|
174
|
+
ensure_all_required_arguments_were_supplied!(context)
|
|
175
|
+
|
|
176
|
+
# For each non-option argument, call the `nonopt` block
|
|
177
|
+
not_matched_options.each(&nonopt)
|
|
178
|
+
|
|
179
|
+
# Replace the original argv with the resulting options
|
|
180
|
+
argv.replace not_matched_options
|
|
181
|
+
end
|
|
181
182
|
end
|
|
182
183
|
|
|
183
184
|
module Positional
|
|
@@ -258,10 +259,56 @@ class OptParse2
|
|
|
258
259
|
title += " (#{required} arg minimum)" if required > 0
|
|
259
260
|
|
|
260
261
|
on sprintf "%s%-*s %s", summary_indent, summary_width, title, description.first
|
|
261
|
-
description[1..]
|
|
262
|
+
description[1..]&.each do |descr|
|
|
262
263
|
on sprintf "%s%-*s %s", summary_indent, summary_width, '', descr
|
|
263
264
|
end
|
|
264
265
|
|
|
265
266
|
@rest = { name:, key:, required: required || 0, block: }
|
|
266
267
|
end
|
|
267
268
|
end
|
|
269
|
+
|
|
270
|
+
__END__
|
|
271
|
+
OptParse2.new do |op|
|
|
272
|
+
op.on '--foo=FOO', multiple: :first! do puts "FOO: #{it}"; it end
|
|
273
|
+
op.on '--bar=BAR' do puts "BAR: #{it}"; it end
|
|
274
|
+
|
|
275
|
+
op.on '-v', '--verbose[=X]', Integer, multiple: :count
|
|
276
|
+
|
|
277
|
+
# op.on '-v', '--verbose[=X]', Integer, multiple: :count! do
|
|
278
|
+
# puts "verbose is: #{it}"
|
|
279
|
+
# it
|
|
280
|
+
# end
|
|
281
|
+
|
|
282
|
+
op.on '-aF', Integer, multiple: [:collect, :succ.to_proc] do |x|
|
|
283
|
+
p x
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
op.parse! %w[ -vvv --foo=abc --bar=123 --foo=xyz -a3 -a4 -a5 ], into: opts={}
|
|
287
|
+
p opts
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# OptParse.new do |op|
|
|
291
|
+
# op.on '--foo=bar', /(.)(.)/ do |x| p x end
|
|
292
|
+
# op.parse! %w[ --foo=34 ]
|
|
293
|
+
# end
|
|
294
|
+
__END__
|
|
295
|
+
OptionParser2.new do |op|
|
|
296
|
+
op.on '-e', default: true
|
|
297
|
+
op.on '--bar1=ALL', default: 'hello', &:upcase
|
|
298
|
+
op.on '--doit=WHAT', /(.)(.)/, key: :A, default: 'xu' do
|
|
299
|
+
p [_1, _2, 'both!']
|
|
300
|
+
end
|
|
301
|
+
|
|
302
|
+
op.pos 'foo', required: true do 3 end
|
|
303
|
+
op.pos 'bar', required: true
|
|
304
|
+
op.pos 'baz', required: false
|
|
305
|
+
op.pos 'quux', required: false
|
|
306
|
+
# op.rest 'files'
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
rest = op.order!(argv = %w[ --doit 3q a b ] , into: opts={})
|
|
310
|
+
|
|
311
|
+
puts "rest=#{rest}"
|
|
312
|
+
puts "argv=#{argv}"
|
|
313
|
+
puts "opts=#{opts}"
|
|
314
|
+
end
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: optparse2
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- SamW
|
|
@@ -19,8 +19,10 @@ files:
|
|
|
19
19
|
- README.md
|
|
20
20
|
- Rakefile
|
|
21
21
|
- lib/optparse2.rb
|
|
22
|
+
- lib/optparse2/context.rb
|
|
22
23
|
- lib/optparse2/fixes.rb
|
|
23
24
|
- lib/optparse2/pathname.rb
|
|
25
|
+
- lib/optparse2/switch-helpers.rb
|
|
24
26
|
- lib/optparse2/version.rb
|
|
25
27
|
- publish
|
|
26
28
|
- sig/optparse2.rbs
|