options_by_example 4.0.0 → 4.2.0
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/README.md +27 -40
- data/lib/options_by_example/commandline_parser.rb +75 -60
- data/lib/options_by_example/usage_specification.rb +38 -25
- data/lib/options_by_example/version.rb +14 -1
- data/lib/options_by_example.rb +15 -4
- metadata +3 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 59f737886cd6b035ebefb01b6d513d6a4cd66957858d9c3f9d4b489058fe5725
|
|
4
|
+
data.tar.gz: d3f6f2d559a4f5c077f878dec4e8fb8c8f9cf9e4daaeebaba8b5611f343ac86b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ad0b05e43b8ec21c9bbc434085896082fe0a956190d255c5ef24382f3f0ec8faf5f05b32f3acda272ca8d0fbbb63e736d9a5db4860e6bd750be26f7601a263c6
|
|
7
|
+
data.tar.gz: e2008e435bbfc129170599f7e28626ede57501b078e65684c436fea0644d69b8a564c388da1bd197a60332c4c34404d60424541dd9912307c0b2316ad069a811
|
data/README.md
CHANGED
|
@@ -1,61 +1,48 @@
|
|
|
1
1
|
# Options by Example
|
|
2
2
|
|
|
3
|
-
No-code options parser that automatically detects command-line options from the usage text
|
|
3
|
+
No-code options parser that automatically detects command-line options from the usage text.
|
|
4
4
|
|
|
5
5
|
Features
|
|
6
6
|
|
|
7
|
-
- Automatically
|
|
8
|
-
- Automatically detects option names and associated arguments (if any) from usage text
|
|
7
|
+
- Automatically infers options and argument names from usage text
|
|
9
8
|
- Parses those arguments and options from the command line (ARGV)
|
|
10
9
|
- Raises errors for unknown options or missing required arguments
|
|
10
|
+
- Supports typed arguments, eg `--lines NUM` or `--since DATE`
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Example
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
```ruby
|
|
15
|
+
require %(options_by_example)
|
|
15
16
|
|
|
16
|
-
|
|
17
|
-
gem install options_by_example
|
|
18
|
-
```
|
|
17
|
+
flags = OptionsByExample.read(DATA).parse(ARGV)
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
puts 'Feeling verbose today' if flags.include_verbose?
|
|
20
|
+
puts flags.get_words.sample(flags.get_num)
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Example
|
|
22
|
+
__END__
|
|
23
|
+
Choose at random from a list of provided words.
|
|
27
24
|
|
|
28
|
-
|
|
29
|
-
require 'options_by_example'
|
|
25
|
+
Usage: random.rb [options] words ...
|
|
30
26
|
|
|
31
|
-
Options
|
|
27
|
+
Options:
|
|
28
|
+
-n, --num NUM Number of choices (default 1)
|
|
29
|
+
--verbose Enable verbose mode
|
|
30
|
+
```
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
puts Options.include? :verbose
|
|
35
|
-
puts Options.include? :retries
|
|
36
|
-
puts Options.include? :timeout
|
|
37
|
-
puts Options.get :retries
|
|
38
|
-
puts Options.get :timeout
|
|
39
|
-
puts Options.get :mode
|
|
40
|
-
puts Options.get :host
|
|
41
|
-
puts Options.get :port
|
|
32
|
+
And then call the program with eg
|
|
42
33
|
|
|
34
|
+
ruby random.rb -n 2 foo bar qux
|
|
43
35
|
|
|
44
|
-
|
|
45
|
-
Establishes a network connection to a designated host and port, enabling
|
|
46
|
-
users to assess network connectivity and diagnose potential problems.
|
|
36
|
+
### Installation
|
|
47
37
|
|
|
48
|
-
|
|
38
|
+
To use options_by_example, first install the gem by running:
|
|
49
39
|
|
|
50
|
-
Options:
|
|
51
|
-
-s, --secure Establish a secure connection (SSL/TSL)
|
|
52
|
-
-v, --verbose Enable verbose output for detailed information
|
|
53
|
-
-r, --retries NUM Number of connection retries (default 3)
|
|
54
|
-
-t, --timeout NUM Set connection timeout in seconds
|
|
55
|
-
|
|
56
|
-
Arguments:
|
|
57
|
-
host The target host to connect to (e.g., example.com)
|
|
58
|
-
port The target port to connect to (e.g., 80)
|
|
59
|
-
[mode] Optional connection mode (active or passive)
|
|
60
40
|
```
|
|
41
|
+
gem install options_by_example
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Alternatively, add this line to your Gemfile and run bundle install:
|
|
61
45
|
|
|
46
|
+
```
|
|
47
|
+
gem 'options_by_example'
|
|
48
|
+
```
|
|
@@ -16,37 +16,26 @@ class OptionsByExample
|
|
|
16
16
|
|
|
17
17
|
def initialize(usage)
|
|
18
18
|
@argument_names = usage.argument_names
|
|
19
|
-
@default_values = usage.default_values
|
|
20
|
-
@ends_with_optional_vararg = usage.ends_with_optional_vararg
|
|
21
19
|
@option_names = usage.option_names
|
|
22
20
|
|
|
23
|
-
@argument_values =
|
|
21
|
+
@argument_values = {}
|
|
24
22
|
@option_values = {}
|
|
25
23
|
end
|
|
26
24
|
|
|
27
|
-
def parse(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
# chunks, plus tracking leading excess arguments. This organization
|
|
31
|
-
# facilitates further processing and validation of the input.
|
|
32
|
-
|
|
33
|
-
@slices = []
|
|
34
|
-
@remainder = current = []
|
|
35
|
-
array.each do |each|
|
|
36
|
-
@slices << current = [] if each.start_with?(?-)
|
|
37
|
-
current << each
|
|
38
|
-
end
|
|
25
|
+
def parse(argv)
|
|
26
|
+
@slices = argv.slice_before(/^-/).entries
|
|
27
|
+
treat_everything_after_double_dash_as_positionals
|
|
39
28
|
|
|
40
29
|
exit_if_help_option
|
|
41
30
|
unpack_combined_shorthand_options
|
|
42
31
|
expand_dash_number_to_dash_n_option
|
|
43
32
|
raise_if_unknown_options
|
|
44
|
-
|
|
33
|
+
|
|
34
|
+
@remainder = parse_options_and_return_remainder
|
|
45
35
|
coerce_num_date_time_etc
|
|
46
36
|
|
|
47
37
|
validate_number_of_arguments
|
|
48
38
|
parse_positional_arguments
|
|
49
|
-
special_case_if_ends_with_optional_vararg
|
|
50
39
|
|
|
51
40
|
# :nocov:
|
|
52
41
|
raise %{unreachable given we check number of arguments} unless @remainder.empty?
|
|
@@ -55,14 +44,17 @@ class OptionsByExample
|
|
|
55
44
|
|
|
56
45
|
private
|
|
57
46
|
|
|
47
|
+
def treat_everything_after_double_dash_as_positionals
|
|
48
|
+
index = @slices.index { |head,| head == '--' }
|
|
49
|
+
@slices[index..-1] = [@slices.drop(index).flatten] if index
|
|
50
|
+
end
|
|
51
|
+
|
|
58
52
|
def exit_if_help_option
|
|
59
53
|
@slices.each do |option, *args|
|
|
60
54
|
case option
|
|
61
55
|
when '-h', '--help'
|
|
62
56
|
if args.first == 'debug!'
|
|
63
57
|
puts "@argument_names = #{@argument_names.inspect}"
|
|
64
|
-
puts "@default_values = #{@default_values.inspect}"
|
|
65
|
-
puts "@ends_with_optional_vararg = #{@ends_with_optional_vararg}"
|
|
66
58
|
puts "@option_names = #{@option_names.inspect}"
|
|
67
59
|
end
|
|
68
60
|
raise PrintUsageMessage
|
|
@@ -108,50 +100,85 @@ class OptionsByExample
|
|
|
108
100
|
end
|
|
109
101
|
|
|
110
102
|
def raise_if_unknown_options
|
|
111
|
-
@slices.each do |option
|
|
112
|
-
|
|
103
|
+
@slices.each do |option,|
|
|
104
|
+
if option =~ /^--?\w/ and not @option_names.include?(option)
|
|
105
|
+
raise "Found unknown option '#{option}'"
|
|
106
|
+
end
|
|
113
107
|
end
|
|
114
108
|
end
|
|
115
109
|
|
|
116
|
-
def
|
|
117
|
-
@slices.
|
|
118
|
-
|
|
119
|
-
|
|
110
|
+
def parse_options_and_return_remainder
|
|
111
|
+
pending = @slices.dup
|
|
112
|
+
|
|
113
|
+
until pending.empty?
|
|
114
|
+
current = pending.first
|
|
115
|
+
|
|
116
|
+
unless current.first =~ /^--?\w/
|
|
117
|
+
current.shift if current.first == '--' # consume double-dash
|
|
118
|
+
return current if pending.length == 1
|
|
119
|
+
raise "Unexpected arguments found before option '#{pending[1].first}', please provide all options before arguments"
|
|
120
120
|
end
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
option = current.shift # consume the option/flag
|
|
123
|
+
option_name, argument_arity, _, _ = @option_names[option]
|
|
123
124
|
@option_values[option_name] = true
|
|
124
125
|
|
|
125
|
-
if
|
|
126
|
-
raise "Expected argument for option '#{option}', got none"
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
126
|
+
if argument_arity == :required
|
|
127
|
+
raise "Expected argument for option '#{option}', got none" unless current.first
|
|
128
|
+
|
|
129
|
+
if pending.length == 1
|
|
130
|
+
minimum_count = count_arguments(:required) + count_arguments(:vararg)
|
|
131
|
+
if current.length <= minimum_count
|
|
132
|
+
raise "Ambiguous argument for option '#{option}', not enough remaining positional arguments"
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
@argument_values[option_name] = current.shift
|
|
137
|
+
elsif argument_arity == :optional && current.first
|
|
138
|
+
if pending.length == 1
|
|
139
|
+
required_count = count_arguments(:required)
|
|
140
|
+
if current.length <= required_count and required_count == @argument_names.length
|
|
141
|
+
return current
|
|
142
|
+
end
|
|
143
|
+
if current.length <= required_count or required_count != @argument_names.length
|
|
144
|
+
raise "Ambiguous argument for option '#{option}', please use -- before positional arguments"
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
@argument_values[option_name] = current.shift
|
|
131
149
|
end
|
|
132
150
|
|
|
133
|
-
|
|
151
|
+
pending.shift if current.empty?
|
|
134
152
|
end
|
|
153
|
+
|
|
154
|
+
return []
|
|
135
155
|
end
|
|
136
156
|
|
|
137
157
|
def coerce_num_date_time_etc
|
|
138
|
-
@option_names.each do |option, (each,
|
|
139
|
-
|
|
158
|
+
@option_names.each do |option, (each, _, argument_type, default_value)|
|
|
159
|
+
value = @argument_values.fetch(each, default_value)
|
|
160
|
+
next unless value
|
|
161
|
+
|
|
140
162
|
begin
|
|
141
|
-
case
|
|
163
|
+
case argument_type
|
|
142
164
|
when 'NUM'
|
|
143
165
|
expected_type = 'an integer value'
|
|
144
|
-
|
|
166
|
+
value = Integer value
|
|
167
|
+
when 'FLOAT'
|
|
168
|
+
expected_type = 'a floating-point value'
|
|
169
|
+
value = Float value
|
|
145
170
|
when 'DATE'
|
|
146
171
|
expected_type = 'a date (e.g. YYYY-MM-DD)'
|
|
147
|
-
|
|
172
|
+
value = Date.parse value
|
|
148
173
|
when 'TIME'
|
|
149
174
|
expected_type = 'a timestamp (e.g. HH:MM:SS)'
|
|
150
|
-
|
|
175
|
+
value = Time.parse value
|
|
151
176
|
end
|
|
152
177
|
rescue ArgumentError
|
|
153
178
|
raise "Invalid argument \"#{value}\" for option '#{option}', please provide #{expected_type}"
|
|
154
179
|
end
|
|
180
|
+
|
|
181
|
+
@argument_values[each] = value
|
|
155
182
|
end
|
|
156
183
|
end
|
|
157
184
|
|
|
@@ -159,14 +186,9 @@ class OptionsByExample
|
|
|
159
186
|
# ASSUME: either varargs or optional arguments, never both. That
|
|
160
187
|
# constraint is guaranteed upstream. Here, we just count
|
|
161
188
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
min_length = count_required + count_vararg
|
|
167
|
-
max_length = count_required + count_optional
|
|
168
|
-
max_length = nil if @ends_with_optional_vararg
|
|
169
|
-
max_length = nil if count_vararg > 0
|
|
189
|
+
min_length = count_arguments(:required) + count_arguments(:vararg)
|
|
190
|
+
max_length = count_arguments(:required) + count_arguments(/optional/)
|
|
191
|
+
max_length = nil if count_arguments(/vararg/) > 0
|
|
170
192
|
|
|
171
193
|
unless (min_length..max_length) === @remainder.size
|
|
172
194
|
|
|
@@ -192,10 +214,6 @@ class OptionsByExample
|
|
|
192
214
|
# :nocov:
|
|
193
215
|
end
|
|
194
216
|
|
|
195
|
-
if @option_took_argument
|
|
196
|
-
msg += " (considering #{@option_took_argument} takes an argument)"
|
|
197
|
-
end
|
|
198
|
-
|
|
199
217
|
raise msg
|
|
200
218
|
end
|
|
201
219
|
end
|
|
@@ -207,10 +225,10 @@ class OptionsByExample
|
|
|
207
225
|
case arity
|
|
208
226
|
when :required
|
|
209
227
|
@argument_values[argument_name] = @remainder.shift
|
|
210
|
-
when :vararg
|
|
228
|
+
when :vararg, :optional_vararg
|
|
211
229
|
@argument_values[argument_name] = @remainder.shift(@remainder.length - remaining_arguments)
|
|
212
230
|
when :optional
|
|
213
|
-
|
|
231
|
+
next if @remainder.empty?
|
|
214
232
|
@argument_values[argument_name] = @remainder.shift
|
|
215
233
|
# :nocov:
|
|
216
234
|
else
|
|
@@ -220,13 +238,10 @@ class OptionsByExample
|
|
|
220
238
|
end
|
|
221
239
|
end
|
|
222
240
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
@
|
|
227
|
-
*@argument_values[final_argument_name],
|
|
228
|
-
*@remainder.shift(@remainder.length),
|
|
229
|
-
]
|
|
241
|
+
private
|
|
242
|
+
|
|
243
|
+
def count_arguments(pattern)
|
|
244
|
+
@argument_names.values.grep(pattern).count
|
|
230
245
|
end
|
|
231
246
|
end
|
|
232
247
|
end
|
|
@@ -6,8 +6,6 @@ class OptionsByExample
|
|
|
6
6
|
|
|
7
7
|
attr_reader :message
|
|
8
8
|
attr_reader :argument_names
|
|
9
|
-
attr_reader :default_values
|
|
10
|
-
attr_reader :ends_with_optional_vararg
|
|
11
9
|
attr_reader :option_names
|
|
12
10
|
|
|
13
11
|
def initialize(text)
|
|
@@ -24,10 +22,10 @@ class OptionsByExample
|
|
|
24
22
|
inline_options = []
|
|
25
23
|
|
|
26
24
|
usage_line = text.lines.grep(/Usage:/).first
|
|
27
|
-
raise
|
|
28
|
-
tokens = usage_line.
|
|
29
|
-
raise unless tokens.shift == 'Usage:'
|
|
30
|
-
raise unless tokens.shift
|
|
25
|
+
raise "Expected usage string, got none" unless usage_line
|
|
26
|
+
tokens = usage_line.split(/(\[.*?\]?\]|\w+ \.\.\.)|\s+/).reject(&:empty?)
|
|
27
|
+
raise "Expected usage line to start with 'Usage:'" unless tokens.shift == 'Usage:'
|
|
28
|
+
raise "Expected command name on same line as 'Usage:'" unless tokens.shift
|
|
31
29
|
tokens.shift if tokens.first == '[options]'
|
|
32
30
|
|
|
33
31
|
while /^\[(--?\w.*)\]$/ === tokens.first
|
|
@@ -47,18 +45,19 @@ class OptionsByExample
|
|
|
47
45
|
end
|
|
48
46
|
|
|
49
47
|
if /^\[(\w+) ?\.\.\.\]$/ === tokens.first
|
|
50
|
-
@argument_names[sanitize $1] = :
|
|
51
|
-
@ends_with_optional_vararg = true
|
|
48
|
+
@argument_names[sanitize $1] = :optional_vararg
|
|
52
49
|
tokens.shift
|
|
53
50
|
end
|
|
54
51
|
|
|
55
52
|
raise "Found invalid usage token '#{tokens.first}'" unless tokens.empty?
|
|
56
53
|
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
if count_arguments(:vararg) > 0 && count_arguments(/optional/) > 0
|
|
55
|
+
raise "Cannot combine vararg and optional arguments"
|
|
56
|
+
end
|
|
59
57
|
|
|
60
|
-
|
|
61
|
-
|
|
58
|
+
if count_arguments(/vararg/) > 1
|
|
59
|
+
raise "Found more than one vararg arguments"
|
|
60
|
+
end
|
|
62
61
|
|
|
63
62
|
# --- 2) Parse option names ---------------------------------------
|
|
64
63
|
#
|
|
@@ -72,38 +71,48 @@ class OptionsByExample
|
|
|
72
71
|
# -t, --timeout NUM Set connection timeout in seconds
|
|
73
72
|
|
|
74
73
|
@option_names = {}
|
|
75
|
-
@default_values = {}
|
|
76
74
|
|
|
77
75
|
options = inline_options + text.lines.grep(/^\s*--?\w/)
|
|
78
76
|
options.each do |string|
|
|
79
|
-
tokens = string.
|
|
77
|
+
tokens = string.strip.split(/(\s+)|(,)\s*/)
|
|
80
78
|
|
|
81
79
|
short_form = nil
|
|
82
80
|
long_form = nil
|
|
83
81
|
option_name = nil
|
|
84
|
-
|
|
82
|
+
argument_arity = nil
|
|
83
|
+
argument_type = nil
|
|
85
84
|
default_value = nil
|
|
86
85
|
|
|
87
|
-
if /^-(\w)
|
|
88
|
-
short_form
|
|
86
|
+
if /^-(\w)$/ === tokens.first
|
|
87
|
+
short_form = tokens.shift
|
|
89
88
|
option_name = sanitize $1
|
|
90
|
-
tokens.shift if ','
|
|
89
|
+
tokens.shift if tokens.first == ','
|
|
91
90
|
end
|
|
92
91
|
|
|
93
|
-
if /^--([\w-]+)
|
|
94
|
-
long_form
|
|
92
|
+
if /^--([\w-]+)$/ === tokens.first
|
|
93
|
+
long_form = tokens.shift
|
|
95
94
|
option_name = sanitize $1
|
|
96
95
|
end
|
|
97
96
|
|
|
98
|
-
if
|
|
99
|
-
|
|
97
|
+
if tokens.shift == ' '
|
|
98
|
+
if /^(\[(.+)\]|(.+)\?)$/ === tokens.first
|
|
99
|
+
argument_type = $2 || $3
|
|
100
|
+
argument_arity = :optional
|
|
101
|
+
tokens.shift
|
|
102
|
+
else
|
|
103
|
+
argument_type = tokens.shift
|
|
104
|
+
argument_arity = :required
|
|
105
|
+
end
|
|
100
106
|
end
|
|
101
107
|
|
|
102
|
-
|
|
103
|
-
|
|
108
|
+
if /\(default\s+(\S+)\)$/ === tokens.join
|
|
109
|
+
default_value = $1
|
|
104
110
|
end
|
|
105
111
|
|
|
106
|
-
|
|
112
|
+
ary = [option_name, argument_arity, argument_type, default_value]
|
|
113
|
+
[short_form, long_form].each do |each|
|
|
114
|
+
@option_names[each] = ary if each
|
|
115
|
+
end
|
|
107
116
|
end
|
|
108
117
|
end
|
|
109
118
|
|
|
@@ -112,5 +121,9 @@ class OptionsByExample
|
|
|
112
121
|
def sanitize(string)
|
|
113
122
|
string.tr('^a-zA-Z0-9', '_').downcase.to_sym
|
|
114
123
|
end
|
|
124
|
+
|
|
125
|
+
def count_arguments(pattern)
|
|
126
|
+
@argument_names.values.grep(pattern).count
|
|
127
|
+
end
|
|
115
128
|
end
|
|
116
129
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class OptionsByExample
|
|
4
|
-
VERSION = '4.
|
|
4
|
+
VERSION = '4.2.0'
|
|
5
5
|
end
|
|
6
6
|
|
|
7
7
|
|
|
@@ -11,6 +11,19 @@ __END__
|
|
|
11
11
|
# Minor version bump when backward-compatible changes or enhancements
|
|
12
12
|
# Patch version bump when backward-compatible bug fixes, security updates etc
|
|
13
13
|
|
|
14
|
+
4.2.0
|
|
15
|
+
- Generate #get_NAME methods for arguments
|
|
16
|
+
- Keep #argument_NAME as backwards-compatible aliases
|
|
17
|
+
- New method #get_at_most_one returns the provided option or nil
|
|
18
|
+
- New method #get_mutually_exclusive aliases #get_at_most_one
|
|
19
|
+
- Deprecate #expect_at_most_one_of in favor of #expect_at_most_one
|
|
20
|
+
- Support optional option arguments, eg '--compress [NUM]'
|
|
21
|
+
|
|
22
|
+
4.1.0
|
|
23
|
+
- Treat everything after double-dash as positional arguments
|
|
24
|
+
- Improve error message when usage is split across lines
|
|
25
|
+
- Add support for FLOAT arguments, eg `--ratio FLOAT`
|
|
26
|
+
|
|
14
27
|
4.0.0
|
|
15
28
|
- Remove support for leading optional arguments (breaking change)
|
|
16
29
|
- Add support for trailing optional arguments
|
data/lib/options_by_example.rb
CHANGED
|
@@ -32,16 +32,24 @@ class OptionsByExample
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def expect_at_most_one_except(*extra_options)
|
|
35
|
-
|
|
35
|
+
expect_at_most_one *(@options.keys - extra_options)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def expect_at_most_one_of(*mutually_exclusive_options)
|
|
39
|
+
expect_at_most_one *mutually_exclusive_options
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def get_at_most_one(*mutually_exclusive_options)
|
|
39
43
|
provided_options = @options.keys & mutually_exclusive_options
|
|
40
44
|
if provided_options.length > 1
|
|
41
45
|
abort "ERR: Found more than one mutually-exclusive option {#{provided_options.join ', '}}"
|
|
42
46
|
end
|
|
47
|
+
provided_options.first
|
|
43
48
|
end
|
|
44
49
|
|
|
50
|
+
alias expect_at_most_one get_at_most_one
|
|
51
|
+
alias get_mutually_exclusive get_at_most_one
|
|
52
|
+
|
|
45
53
|
def fetch(*args, &block)
|
|
46
54
|
@arguments.fetch(*args, &block)
|
|
47
55
|
end
|
|
@@ -76,13 +84,17 @@ class OptionsByExample
|
|
|
76
84
|
def initialize_argument_accessors
|
|
77
85
|
[
|
|
78
86
|
*@usage_spec.argument_names.keys,
|
|
79
|
-
*@usage_spec.option_names.values
|
|
87
|
+
*@usage_spec.option_names.values
|
|
88
|
+
.map { |option_name, argument_arity| option_name if argument_arity }
|
|
89
|
+
.compact,
|
|
80
90
|
].each do |argument_name|
|
|
81
91
|
instance_eval %{
|
|
82
|
-
def
|
|
92
|
+
def get_#{argument_name}
|
|
83
93
|
val = @arguments[:#{argument_name}]
|
|
84
94
|
val && block_given? ? (yield val) : val
|
|
85
95
|
end
|
|
96
|
+
|
|
97
|
+
alias argument_#{argument_name} get_#{argument_name}
|
|
86
98
|
}
|
|
87
99
|
end
|
|
88
100
|
end
|
|
@@ -97,4 +109,3 @@ class OptionsByExample
|
|
|
97
109
|
end
|
|
98
110
|
end
|
|
99
111
|
end
|
|
100
|
-
|
metadata
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: options_by_example
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 4.
|
|
4
|
+
version: 4.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Adrian Kuhn
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies: []
|
|
13
|
-
description:
|
|
14
12
|
email:
|
|
15
13
|
- akuhn@iam.unibe.ch
|
|
16
14
|
executables: []
|
|
@@ -29,7 +27,6 @@ metadata:
|
|
|
29
27
|
homepage_uri: https://github.com/akuhn/options_by_example
|
|
30
28
|
source_code_uri: https://github.com/akuhn/options_by_example
|
|
31
29
|
changelog_uri: https://github.com/akuhn/options_by_example/blob/master/lib/options_by_example/version.rb
|
|
32
|
-
post_install_message:
|
|
33
30
|
rdoc_options: []
|
|
34
31
|
require_paths:
|
|
35
32
|
- lib
|
|
@@ -44,8 +41,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
44
41
|
- !ruby/object:Gem::Version
|
|
45
42
|
version: '0'
|
|
46
43
|
requirements: []
|
|
47
|
-
rubygems_version: 3.
|
|
48
|
-
signing_key:
|
|
44
|
+
rubygems_version: 3.6.9
|
|
49
45
|
specification_version: 4
|
|
50
46
|
summary: No-code options parser that extracts arguments directly from usage text.
|
|
51
47
|
test_files: []
|