optimist_xl 3.2.0 → 3.3.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/.travis.yml +0 -1
- data/History.md +7 -0
- data/README.md +134 -4
- data/examples/didyoumean.rb +11 -0
- data/examples/optional_string_arg_type.rb +10 -0
- data/examples/partialmatch.rb +11 -0
- data/lib/optimist_xl.rb +127 -40
- data/optimist_xl.gemspec +2 -1
- data/test/optimist_xl/stringflag_test.rb +158 -0
- metadata +13 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3fe9d9828ccf8519f081c520339195ab106b579a0d09564aab1a0ea47134b00a
|
4
|
+
data.tar.gz: 1f60a13d38245b2d7214093843403ffeefb917d3a4466ef50b11904cdc733037
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ce7e01b876214a131e653a5c9a3f700aaa7388bf68b211927b8bcda2f80d65d963b0991bc323a23d356f4be3bf2a41c943dcc44219bc421e18be824d0d897bb8
|
7
|
+
data.tar.gz: 4f6a89f6a84d5609e84f6323e104ee535893e7f74f2ca964665d4e745292b32a640cfead9efde73d9681c2d637edc38af2fe6f68ebaa3b428e28d72d58ace39f
|
data/.travis.yml
CHANGED
data/History.md
CHANGED
@@ -1,8 +1,15 @@
|
|
1
|
+
# [3.3.0] / 2020-11-12
|
2
|
+
|
3
|
+
* Adding new `:stringflag` option.
|
4
|
+
* Improving documentation.
|
5
|
+
* Removing support for Ruby 2.2
|
6
|
+
|
1
7
|
# [3.2.0] / 2020-03-02
|
2
8
|
|
3
9
|
* Added alternate-named long options using `:alt`
|
4
10
|
* Added alternate-named short-options by allowing `:short` to take an Array.
|
5
11
|
* Refactored some short/long handling code into ShortNames and LongNames classes.
|
12
|
+
|
6
13
|
# [3.1.1] / 2020-01-20
|
7
14
|
|
8
15
|
* The gem has been forked from optimist to optimist_xl
|
data/README.md
CHANGED
@@ -29,13 +29,15 @@ See the **Extended Features** section below for the differences/enhancements
|
|
29
29
|
conversion.
|
30
30
|
- Automatic help message generation, wrapped to current screen width.
|
31
31
|
|
32
|
-
## Extended
|
32
|
+
## Extended features unavailable in the original Optimist gem
|
33
33
|
|
34
34
|
### Parser Settings
|
35
35
|
- Automatic suggestions whens incorrect options are given
|
36
36
|
- disable with `suggestions: false`
|
37
|
+
- see example below
|
37
38
|
- Inexact matching of long arguments
|
38
39
|
- disable with `exact_match: true`
|
40
|
+
- see example below
|
39
41
|
- Available prevention of short-arguments by default
|
40
42
|
- enable with `explicit_short_opts: true`
|
41
43
|
|
@@ -61,16 +63,144 @@ Long options can be given alternate names using `alt:`
|
|
61
63
|
|
62
64
|
See [example](examples/alt_names.rb)
|
63
65
|
|
66
|
+
### Stringflag option-type
|
67
|
+
|
68
|
+
It is useful to allow an option that can be set as a string, used with a default string or unset, especialy in the case of specifying a log-file. AFAICT this was not possible with the original Optimist.
|
69
|
+
|
70
|
+
Example:
|
71
|
+
|
72
|
+
For this option definition:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
opt :log, "Specify optional logfile", :type => :stringflag, :default => "progname.log"
|
76
|
+
```
|
77
|
+
|
78
|
+
The programmer should use the value of `log_given` in conjunction with the value of `log`
|
79
|
+
to determine whether to enable logging what what the filename should be.
|
80
|
+
|
81
|
+
```sh
|
82
|
+
$ ./examples/optional_string_arg_type.rb -h
|
83
|
+
Options:
|
84
|
+
-l, --log=<s?>, --no-log specify optional log-file path (Default: progname.log)
|
85
|
+
-h, --help Show this message
|
86
|
+
|
87
|
+
# Note that when no options are given, :log_given is not set, but the default passes through.
|
88
|
+
$ ./examples/optional_string_arg_type.rb
|
89
|
+
{:log=>"progname.log", :help=>false}
|
90
|
+
|
91
|
+
$ ./examples/optional_string_arg_type.rb -l
|
92
|
+
{:log=>"progname.log", :help=>false, :log_given=>true}
|
93
|
+
|
94
|
+
# In this case no-log can be used to force a false value into :log
|
95
|
+
$ ./examples/optional_string_arg_type.rb --no-log
|
96
|
+
{:log=>false, :help=>false, :log_given=>true}
|
97
|
+
|
98
|
+
# Overriding the default case
|
99
|
+
$ ./examples/optional_string_arg_type.rb --log othername.log
|
100
|
+
{:log=>"othername.log", :help=>false, :log_given=>true}
|
101
|
+
```
|
102
|
+
|
64
103
|
### Subcommands
|
65
104
|
"Native" subcommand support - similar to sub-commands in Git.
|
66
105
|
- See [example](examples/subcommands.rb)
|
67
106
|
- Ideas borrowed from https://github.com/jwliechty/trollop-subcommands
|
68
107
|
|
108
|
+
### Automatic suggestions
|
109
|
+
|
110
|
+
Suggestions are formed using the DidYouMean gem, which is part of Ruby 2.3+. It uses the JaroWinkler and Levenshtein distance to determine when unknown options are within a certain 'distance' of a known option keyword.
|
111
|
+
|
112
|
+
```sh
|
113
|
+
$ ./examples/didyoumean.rb -h
|
114
|
+
Options:
|
115
|
+
-c, --cone Ice cream cone
|
116
|
+
-z, --zippy It zips
|
117
|
+
-a, --zapzy It zapz
|
118
|
+
-b, --big-bug Madagascar cockroach
|
119
|
+
-h, --help Show this message
|
120
|
+
|
121
|
+
# Suggestions for a simple misspelling
|
122
|
+
|
123
|
+
$ ./examples/didyoumean.rb --bone
|
124
|
+
Error: unknown argument '--bone'. Did you mean: [--cone] ?.
|
125
|
+
Try --help for help.
|
126
|
+
|
127
|
+
# Letter transposition
|
128
|
+
$ ./examples/didyoumean.rb --ocne
|
129
|
+
Error: unknown argument '--ocne'. Did you mean: [--cone] ?.
|
130
|
+
Try --help for help.
|
131
|
+
|
132
|
+
# Accidental plural
|
133
|
+
|
134
|
+
$ ./examples/didyoumean.rb --cones
|
135
|
+
Error: unknown argument '--cones'. Did you mean: [--cone] ?.
|
136
|
+
Try --help for help.
|
137
|
+
|
138
|
+
# Two close matches, provide two suggestions.
|
139
|
+
|
140
|
+
$ ./examples/didyoumean.rb --zipzy
|
141
|
+
Error: unknown argument '--zipzy'. Did you mean: [--zippy, --zapzy] ?.
|
142
|
+
Try --help for help.
|
143
|
+
|
144
|
+
# Posix-style options with '-' instead of '_' are a common mistake
|
145
|
+
|
146
|
+
$ ./examples/didyoumean.rb --big_bug
|
147
|
+
Error: unknown argument '--big_bug'. Did you mean: [--big-bug] ?.
|
148
|
+
Try --help for help.
|
149
|
+
|
150
|
+
# Eventually the input option is too far away from
|
151
|
+
# anything we know about so just say we dont understand
|
152
|
+
|
153
|
+
$ ./examples/didyoumean.rb --bugblatter-beast
|
154
|
+
Error: unknown argument '--bugblatter-beast'.
|
155
|
+
Try --help for help.
|
156
|
+
```
|
157
|
+
|
158
|
+
### Inexact Matching
|
159
|
+
|
160
|
+
Similar to Perl's Getopt::Long, partially specified long-options can be used as long as they would unambiguously match a single option.
|
161
|
+
|
162
|
+
```sh
|
163
|
+
$ ./examples/partialmatch.rb -h
|
164
|
+
Options:
|
165
|
+
-a, --apple An apple
|
166
|
+
-p, --apple-sauce Cooked apple puree
|
167
|
+
-t, --atom Smallest unit of ordinary matter
|
168
|
+
-n, --anvil Heavy metal
|
169
|
+
-e, --anteater Eats ants
|
170
|
+
-h, --help Show this message
|
171
|
+
|
172
|
+
# Exact match for 'apple'
|
173
|
+
$ ./examples/partialmatch.rb --apple
|
174
|
+
{:apple=>true, :apple_sauce=>false, :atom=>false, :anvil=>false, :anteater=>false, :help=>false, :apple_given=>true}
|
175
|
+
|
176
|
+
# Cannot inexact match with 'app', as it partially matches more than one known option
|
177
|
+
$ ./examples/partialmatch.rb --app
|
178
|
+
Error: ambiguous option '--app' matched keys (apple,apple-sauce).
|
179
|
+
Try --help for help.
|
180
|
+
|
181
|
+
# Inexact match for 'apple-sauce'
|
182
|
+
$ ./examples/partialmatch.rb --apple-s
|
183
|
+
{:apple=>false, :apple_sauce=>true, :atom=>false, :anvil=>false, :anteater=>false, :help=>false, :apple_sauce_given=>true}
|
184
|
+
|
185
|
+
# Shortest match for 'anvil'
|
186
|
+
$ ./examples/partialmatch.rb --anv
|
187
|
+
{:apple=>false, :apple_sauce=>false, :atom=>false, :anvil=>true, :anteater=>false, :help=>false, :anvil_given=>true}
|
188
|
+
|
189
|
+
# Cannot inexact match with 'an', as it partially matches more than one known option
|
190
|
+
$ ./examples/partialmatch.rb --an
|
191
|
+
Error: ambiguous option '--an' matched keys (anvil,anteater).
|
192
|
+
Try --help for help.
|
193
|
+
|
194
|
+
# Shortest match for 'atom'
|
195
|
+
$ ./examples/partialmatch.rb --at
|
196
|
+
{:apple=>false, :apple_sauce=>false, :atom=>true, :anvil=>false, :anteater=>false, :help=>false, :atom_given=>true}
|
197
|
+
```
|
198
|
+
|
69
199
|
## Requirements
|
70
200
|
|
71
|
-
* Ruby 2.2+
|
72
201
|
* A burning desire to write less code.
|
73
|
-
|
202
|
+
* Ruby 2.3+
|
203
|
+
|
74
204
|
## Install
|
75
205
|
|
76
206
|
* `gem install optimist_xl`
|
@@ -94,6 +224,6 @@ Copyright © 2008-2014 [William Morgan](http://masanjin.net/).
|
|
94
224
|
|
95
225
|
Copyright © 2014 Red Hat, Inc.
|
96
226
|
|
97
|
-
Copyright © 2019 Ben Bowers
|
227
|
+
Copyright © 2019-2020 Ben Bowers
|
98
228
|
|
99
229
|
OptimistXL is released under the [MIT License](http://www.opensource.org/licenses/MIT).
|
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require_relative '../lib/optimist_xl'
|
3
|
+
|
4
|
+
opts = OptimistXL::options do
|
5
|
+
opt :apple, "An apple"
|
6
|
+
opt :apple_sauce, "Cooked apple puree"
|
7
|
+
opt :atom, "Smallest unit of ordinary matter"
|
8
|
+
opt :anvil, "Heavy metal"
|
9
|
+
opt :anteater, "Eats ants"
|
10
|
+
end
|
11
|
+
p opts
|
data/lib/optimist_xl.rb
CHANGED
@@ -9,7 +9,7 @@ require 'date'
|
|
9
9
|
module OptimistXL
|
10
10
|
# note: this is duplicated in gemspec
|
11
11
|
# please change over there too
|
12
|
-
VERSION = "3.
|
12
|
+
VERSION = "3.3.0"
|
13
13
|
|
14
14
|
## Thrown by Parser in the event of a commandline error. Not needed if
|
15
15
|
## you're using the OptimistXL::options entry.
|
@@ -402,15 +402,34 @@ class Parser
|
|
402
402
|
# The block returns the number of parameters taken.
|
403
403
|
num_params_taken = 0
|
404
404
|
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
405
|
+
#DEBUG# puts "\nparams:#{params} npt:#{num_params_taken},ps:#{params.size}"
|
406
|
+
|
407
|
+
#if params.size > 0
|
408
|
+
# if @specs[sym].min_args == 1 && @specs[sym].max_args == 1
|
409
|
+
# given_args[sym][:params] << params[0, 1] # take the first parameter
|
410
|
+
# num_params_taken = 1
|
411
|
+
# elsif @specs[sym].max_args > 1
|
412
|
+
# given_args[sym][:params] << params # take all the parameters
|
413
|
+
# num_params_taken = params.size
|
414
|
+
# end
|
415
|
+
#end
|
416
|
+
|
417
|
+
if params.size == 0
|
418
|
+
if @specs[sym].min_args == 0
|
419
|
+
given_args[sym][:params] << [ @specs[sym].default || true]
|
420
|
+
end
|
421
|
+
elsif params.size > 0
|
422
|
+
if params.size >= @specs[sym].max_args
|
423
|
+
# take smaller of the two sizes to determine how many parameters to take
|
424
|
+
num_params_taken = [params.size, @specs[sym].max_args].min
|
425
|
+
given_args[sym][:params] << params[0, num_params_taken]
|
426
|
+
else
|
427
|
+
# take all the parameters
|
428
|
+
given_args[sym][:params] << params
|
411
429
|
num_params_taken = params.size
|
412
430
|
end
|
413
431
|
end
|
432
|
+
|
414
433
|
|
415
434
|
num_params_taken
|
416
435
|
end
|
@@ -443,7 +462,8 @@ class Parser
|
|
443
462
|
arg, params, negative_given = given_data.values_at :arg, :params, :negative_given
|
444
463
|
|
445
464
|
opts = @specs[sym]
|
446
|
-
|
465
|
+
|
466
|
+
if params.size < opts.min_args
|
447
467
|
raise CommandlineError, "option '#{arg}' needs a parameter" unless opts.default
|
448
468
|
params << (opts.array_default? ? opts.default.clone : [opts.default])
|
449
469
|
end
|
@@ -458,13 +478,19 @@ class Parser
|
|
458
478
|
|
459
479
|
vals[sym] = opts.parse(params, negative_given)
|
460
480
|
|
461
|
-
if opts.
|
481
|
+
if opts.min_args==0 && opts.max_args==1
|
482
|
+
if opts.multi?
|
483
|
+
vals[sym] = vals[sym].map { |p| p[0] }
|
484
|
+
else
|
485
|
+
vals[sym] = vals[sym][0][0]
|
486
|
+
end
|
487
|
+
elsif opts.min_args==1 && opts.max_args==1
|
462
488
|
if opts.multi? # multiple options, each with a single parameter
|
463
489
|
vals[sym] = vals[sym].map { |p| p[0] }
|
464
490
|
else # single parameter
|
465
491
|
vals[sym] = vals[sym][0][0]
|
466
492
|
end
|
467
|
-
elsif opts.
|
493
|
+
elsif opts.max_args>1 && !opts.multi?
|
468
494
|
vals[sym] = vals[sym][0] # single option, with multiple parameters
|
469
495
|
end
|
470
496
|
# else: multiple options, with multiple parameters
|
@@ -847,6 +873,17 @@ class Option
|
|
847
873
|
@permitted = nil
|
848
874
|
@permitted_response = "option '%{arg}' only accepts %{valid_string}"
|
849
875
|
@optshash = Hash.new()
|
876
|
+
@min_args = 1
|
877
|
+
@max_args = 1
|
878
|
+
# maximum max_args is likely ~~ 128*1024, as linux MAX_ARG_STRLEN is 128kiB
|
879
|
+
end
|
880
|
+
|
881
|
+
|
882
|
+
# Check that an option is compatible with another option.
|
883
|
+
# By default, checking that they are the same class, but we
|
884
|
+
# can override this in the subclass as needed.
|
885
|
+
def compatible_with?(other_option)
|
886
|
+
self.is_a? other_option.class
|
850
887
|
end
|
851
888
|
|
852
889
|
def opts(key)
|
@@ -857,24 +894,24 @@ class Option
|
|
857
894
|
@optshash = o
|
858
895
|
end
|
859
896
|
|
860
|
-
## Indicates a flag option, which is an option without an argument
|
861
|
-
def flag? ; false ; end
|
862
|
-
def single_arg?
|
863
|
-
!self.multi_arg? && !self.flag?
|
864
|
-
end
|
865
897
|
|
866
898
|
def multi ; @multi_given ; end
|
867
899
|
alias multi? multi
|
868
900
|
|
869
|
-
|
870
|
-
|
871
|
-
|
872
|
-
|
901
|
+
attr_reader :min_args, :max_args
|
902
|
+
# |@min_args | @max_args |
|
903
|
+
# +----------+-----------+
|
904
|
+
# | 0 | 0 | formerly flag?==true (option without any arguments)
|
905
|
+
# | 1 | 1 | formerly single_arg?==true (single-parameter/normal option)
|
906
|
+
# | 1 | >1 | formerly multi_arg?==true
|
907
|
+
# | ? | ? | presumably illegal condition. untested.
|
908
|
+
|
873
909
|
def array_default? ; self.default.kind_of?(Array) ; end
|
874
910
|
|
875
911
|
def doesnt_need_autogen_short ; !short.auto || !short.chars.empty? ; end
|
876
912
|
|
877
913
|
def callback ; opts(:callback) ; end
|
914
|
+
|
878
915
|
def desc ; opts(:desc) ; end
|
879
916
|
|
880
917
|
def required? ; opts(:required) ; end
|
@@ -890,7 +927,7 @@ class Option
|
|
890
927
|
optionlist = []
|
891
928
|
optionlist.concat(short.chars.map { |o| "-#{o}" })
|
892
929
|
optionlist.concat(long.names.map { |o| "--#{o}" })
|
893
|
-
optionlist.compact.join(', ') + type_format + (
|
930
|
+
optionlist.compact.join(', ') + type_format + (min_args==0 && default ? ", --no-#{long}" : "")
|
894
931
|
end
|
895
932
|
|
896
933
|
## Format the educate-line description including the default and permitted value(s)
|
@@ -998,9 +1035,11 @@ class Option
|
|
998
1035
|
|
999
1036
|
opttype = OptimistXL::Parser.registry_getopttype(opts[:type])
|
1000
1037
|
opttype_from_default = get_klass_from_default(opts, opttype)
|
1001
|
-
|
1002
|
-
|
1003
|
-
|
1038
|
+
#DEBUG# puts "\nopt:#{opttype||'nil'} ofd:#{opttype_from_default}" if opttype_from_default
|
1039
|
+
if opttype && opttype_from_default && !opttype.compatible_with?(opttype_from_default) # opttype.is_a? opttype_from_default.class
|
1040
|
+
raise ArgumentError, ":type specification (#{opttype.class}) and default type don't match (default type is #{opttype_from_default.class})"
|
1041
|
+
end
|
1042
|
+
|
1004
1043
|
opt_inst = (opttype || opttype_from_default || OptimistXL::BooleanOption.new)
|
1005
1044
|
|
1006
1045
|
## fill in :long
|
@@ -1034,7 +1073,7 @@ class Option
|
|
1034
1073
|
if disambiguated_default.is_a? Array
|
1035
1074
|
return(optdef.first.class.name.downcase + "s") if !optdef.empty?
|
1036
1075
|
if opttype
|
1037
|
-
raise ArgumentError, "multiple argument type must be plural" unless opttype.
|
1076
|
+
raise ArgumentError, "multiple argument type must be plural" unless opttype.max_args > 1
|
1038
1077
|
return nil
|
1039
1078
|
else
|
1040
1079
|
raise ArgumentError, "multiple argument type cannot be deduced from an empty array"
|
@@ -1063,16 +1102,42 @@ class Option
|
|
1063
1102
|
private_class_method :get_type_from_disdef
|
1064
1103
|
private_class_method :get_klass_from_default
|
1065
1104
|
|
1105
|
+
def self.handle_long_opt(lopt, name)
|
1106
|
+
lopt = lopt ? lopt.to_s : name.to_s.gsub("_", "-")
|
1107
|
+
lopt = case lopt
|
1108
|
+
when /^--([^-].*)$/ then $1
|
1109
|
+
when /^[^-]/ then lopt
|
1110
|
+
else raise ArgumentError, "invalid long option name #{lopt.inspect}"
|
1111
|
+
end
|
1112
|
+
end
|
1113
|
+
|
1114
|
+
def self.handle_short_opt(sopt)
|
1115
|
+
sopt = sopt.to_s if sopt && sopt != :none
|
1116
|
+
sopt = case sopt
|
1117
|
+
when /^-(.)$/ then $1
|
1118
|
+
when nil, :none, /^.$/ then sopt
|
1119
|
+
else raise ArgumentError, "invalid short option name '#{sopt.inspect}'"
|
1120
|
+
end
|
1121
|
+
|
1122
|
+
if sopt
|
1123
|
+
raise ArgumentError, "a short option name can't be a number or a dash" if sopt =~ ::OptimistXL::Parser::INVALID_SHORT_ARG_REGEX
|
1124
|
+
end
|
1125
|
+
return sopt
|
1126
|
+
end
|
1127
|
+
|
1066
1128
|
end
|
1067
1129
|
|
1068
1130
|
# Flag option. Has no arguments. Can be negated with "no-".
|
1069
1131
|
class BooleanOption < Option
|
1070
1132
|
register_alias :flag, :bool, :boolean, :trueclass, :falseclass
|
1133
|
+
|
1071
1134
|
def initialize
|
1072
1135
|
super()
|
1073
1136
|
@default = false
|
1137
|
+
@min_args = 0
|
1138
|
+
@max_args = 0
|
1074
1139
|
end
|
1075
|
-
|
1140
|
+
|
1076
1141
|
def parse(_paramlist, neg_given)
|
1077
1142
|
return(self.name.to_s =~ /^no_/ ? neg_given : !neg_given)
|
1078
1143
|
end
|
@@ -1139,6 +1204,38 @@ class StringOption < Option
|
|
1139
1204
|
end
|
1140
1205
|
end
|
1141
1206
|
|
1207
|
+
#
|
1208
|
+
class StringFlagOption < StringOption
|
1209
|
+
register_alias :stringflag
|
1210
|
+
def type_format ; "=<s?>" ; end
|
1211
|
+
def parse(paramlist, neg_given)
|
1212
|
+
paramlist.map do |plist|
|
1213
|
+
plist.map do |pg|
|
1214
|
+
neg_given ? false : pg
|
1215
|
+
#case pg
|
1216
|
+
#when FalseClass then () ? neg_given : !neg_given
|
1217
|
+
#when TrueClass then (self.name.to_s =~ /^no_/) ? neg_given : !neg_given
|
1218
|
+
#else pg
|
1219
|
+
#end
|
1220
|
+
end
|
1221
|
+
end
|
1222
|
+
end
|
1223
|
+
|
1224
|
+
def initialize
|
1225
|
+
super
|
1226
|
+
@default = false
|
1227
|
+
@min_args = 0
|
1228
|
+
@max_args = 1
|
1229
|
+
end
|
1230
|
+
|
1231
|
+
def compatible_with?(other_option)
|
1232
|
+
self.is_a?(other_option.class) ||
|
1233
|
+
other_option.is_a?(BooleanOption) ||
|
1234
|
+
other_option.is_a?(StringArrayOption)
|
1235
|
+
end
|
1236
|
+
|
1237
|
+
end
|
1238
|
+
|
1142
1239
|
# Option for dates. No longer uses Chronic if available.
|
1143
1240
|
# If chronic style dates are needed, then you may
|
1144
1241
|
# require 'optimist_xl/chronic'
|
@@ -1169,35 +1266,35 @@ end
|
|
1169
1266
|
class IntegerArrayOption < IntegerOption
|
1170
1267
|
register_alias :fixnums, :ints, :integers
|
1171
1268
|
def type_format ; "=<i+>" ; end
|
1172
|
-
def
|
1269
|
+
def initialize ; super ; @max_args = 999 ; end
|
1173
1270
|
end
|
1174
1271
|
|
1175
1272
|
# Option class for handling multiple Floats
|
1176
1273
|
class FloatArrayOption < FloatOption
|
1177
1274
|
register_alias :doubles, :floats
|
1178
1275
|
def type_format ; "=<f+>" ; end
|
1179
|
-
def
|
1276
|
+
def initialize ; super ; @max_args = 999 ; end
|
1180
1277
|
end
|
1181
1278
|
|
1182
1279
|
# Option class for handling multiple Strings
|
1183
1280
|
class StringArrayOption < StringOption
|
1184
1281
|
register_alias :strings
|
1185
1282
|
def type_format ; "=<s+>" ; end
|
1186
|
-
def
|
1283
|
+
def initialize ; super ; @max_args = 999 ; end
|
1187
1284
|
end
|
1188
1285
|
|
1189
1286
|
# Option class for handling multiple dates
|
1190
1287
|
class DateArrayOption < DateOption
|
1191
1288
|
register_alias :dates
|
1192
1289
|
def type_format ; "=<date+>" ; end
|
1193
|
-
def
|
1290
|
+
def initialize ; super ; @max_args = 999 ; end
|
1194
1291
|
end
|
1195
1292
|
|
1196
1293
|
# Option class for handling Files/URLs via 'open'
|
1197
1294
|
class IOArrayOption < IOOption
|
1198
1295
|
register_alias :ios
|
1199
1296
|
def type_format ; "=<filename/uri+>" ; end
|
1200
|
-
def
|
1297
|
+
def initialize ; super ; @max_args = 999 ; end
|
1201
1298
|
end
|
1202
1299
|
|
1203
1300
|
## The easy, syntactic-sugary entry method into OptimistXL. Creates a Parser,
|
@@ -1342,13 +1439,3 @@ end
|
|
1342
1439
|
|
1343
1440
|
module_function :options, :die, :educate, :with_standard_exception_handling
|
1344
1441
|
end # module
|
1345
|
-
|
1346
|
-
|
1347
|
-
# @global_parser.stop_on(subcommands)
|
1348
|
-
# @global_parser.stop_on_unknown
|
1349
|
-
# Trollop::with_standard_exception_handling(@global_parser) do
|
1350
|
-
# global_options = @global_parser.parse(args)
|
1351
|
-
# cmd = parse_subcommand(args)
|
1352
|
-
# cmd_options = parse_subcommand_options(args, cmd)
|
1353
|
-
# Result.new(global_options, cmd, cmd_options)
|
1354
|
-
# end
|
data/optimist_xl.gemspec
CHANGED
@@ -11,6 +11,7 @@ Gem::Specification.new do |spec|
|
|
11
11
|
spec.email = "nanobowers@gmail.com"
|
12
12
|
spec.summary = "OptimistXL is feature fork of the Optimist commandline option parser."
|
13
13
|
spec.description = "OptimistXL is feature filled but lightweight commandline option parser.
|
14
|
+
It contains all of the features of the Optimist gem, plus lots of additional features you didnt know you needed.
|
14
15
|
One line of code per option is all you typically need to write.
|
15
16
|
For that, you get a nice automatically-generated help page, robust option
|
16
17
|
parsing, command subcompletion, and sensible defaults for everything you don't
|
@@ -32,6 +33,6 @@ specify. This gem is an enhanced-feature fork of the Optimist gem."
|
|
32
33
|
spec.required_ruby_version = '>= 2.2'
|
33
34
|
|
34
35
|
spec.add_development_dependency "minitest", "~> 5.4.3"
|
35
|
-
spec.add_development_dependency "rake", "
|
36
|
+
spec.add_development_dependency "rake", ">= 12.3.3"
|
36
37
|
spec.add_development_dependency "chronic"
|
37
38
|
end
|
@@ -0,0 +1,158 @@
|
|
1
|
+
require 'stringio'
|
2
|
+
require 'test_helper'
|
3
|
+
|
4
|
+
module OptimistXL
|
5
|
+
class StringFlagParserTest < ::MiniTest::Test
|
6
|
+
def setup
|
7
|
+
@p = Parser.new
|
8
|
+
end
|
9
|
+
|
10
|
+
# in this case, the stringflag should return false
|
11
|
+
def test_stringflag_unset
|
12
|
+
@p.opt :xyz, "desc", :type => :stringflag
|
13
|
+
@p.opt :abc, "desc", :type => :flag
|
14
|
+
opts = @p.parse %w()
|
15
|
+
assert_equal false, opts[:xyz]
|
16
|
+
assert_equal false, opts[:abc]
|
17
|
+
opts = @p.parse %w(--abc)
|
18
|
+
assert_equal false, opts[:xyz]
|
19
|
+
assert_equal true, opts[:abc]
|
20
|
+
end
|
21
|
+
|
22
|
+
# in this case, the stringflag should return true
|
23
|
+
def test_stringflag_as_flag
|
24
|
+
@p.opt :xyz, "desc", :type => :stringflag
|
25
|
+
@p.opt :abc, "desc", :type => :flag
|
26
|
+
opts = @p.parse %w(--xyz )
|
27
|
+
assert_equal true, opts[:xyz_given]
|
28
|
+
assert_equal true, opts[:xyz]
|
29
|
+
assert_equal false, opts[:abc]
|
30
|
+
opts = @p.parse %w(--xyz --abc)
|
31
|
+
assert_equal true, opts[:xyz_given]
|
32
|
+
assert_equal true, opts[:xyz]
|
33
|
+
assert_equal true, opts[:abc]
|
34
|
+
end
|
35
|
+
|
36
|
+
# in this case, the stringflag should return a string
|
37
|
+
def test_stringflag_as_string
|
38
|
+
@p.opt :xyz, "desc", :type => :stringflag
|
39
|
+
@p.opt :abc, "desc", :type => :flag
|
40
|
+
opts = @p.parse %w(--xyz abcd)
|
41
|
+
assert_equal true, opts[:xyz_given]
|
42
|
+
assert_equal "abcd", opts[:xyz]
|
43
|
+
assert_equal false, opts[:abc]
|
44
|
+
opts = @p.parse %w(--xyz abcd --abc)
|
45
|
+
assert_equal true, opts[:xyz_given]
|
46
|
+
assert_equal "abcd", opts[:xyz]
|
47
|
+
assert_equal true, opts[:abc]
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_stringflag_with_string_default
|
51
|
+
@p.opt :log, "desc", :type => :stringflag, default: "output.log"
|
52
|
+
opts = @p.parse []
|
53
|
+
assert_nil opts[:log_given]
|
54
|
+
assert_equal "output.log", opts[:log]
|
55
|
+
|
56
|
+
opts = @p.parse %w(--no-log)
|
57
|
+
assert_equal true, opts[:log_given]
|
58
|
+
assert_equal false, opts[:log]
|
59
|
+
|
60
|
+
opts = @p.parse %w(--log)
|
61
|
+
assert_equal true, opts[:log_given]
|
62
|
+
assert_equal "output.log", opts[:log]
|
63
|
+
|
64
|
+
opts = @p.parse %w(--log other.log)
|
65
|
+
assert_equal true, opts[:log_given]
|
66
|
+
assert_equal "other.log", opts[:log]
|
67
|
+
end
|
68
|
+
|
69
|
+
# should be same as with no default, but making sure.
|
70
|
+
def test_stringflag_with_false_default
|
71
|
+
@p.opt :log, "desc", :type => :stringflag, default: false
|
72
|
+
opts = @p.parse []
|
73
|
+
assert_nil opts[:log_given]
|
74
|
+
assert_equal false, opts[:log]
|
75
|
+
|
76
|
+
opts = @p.parse %w(--no-log)
|
77
|
+
assert_equal true, opts[:log_given]
|
78
|
+
assert_equal false, opts[:log]
|
79
|
+
|
80
|
+
opts = @p.parse %w(--log)
|
81
|
+
assert_equal true, opts[:log_given]
|
82
|
+
assert_equal true, opts[:log]
|
83
|
+
|
84
|
+
opts = @p.parse %w(--log other.log)
|
85
|
+
assert_equal true, opts[:log_given]
|
86
|
+
assert_equal "other.log", opts[:log]
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_stringflag_with_no_defaults
|
90
|
+
@p.opt :log, "desc", :type => :stringflag
|
91
|
+
|
92
|
+
opts = @p.parse []
|
93
|
+
assert_nil opts[:log_given]
|
94
|
+
assert_equal false, opts[:log]
|
95
|
+
|
96
|
+
opts = @p.parse %w(--no-log)
|
97
|
+
assert_equal true, opts[:log_given]
|
98
|
+
assert_equal false, opts[:log]
|
99
|
+
|
100
|
+
opts = @p.parse %w(--log)
|
101
|
+
assert_equal true, opts[:log_given]
|
102
|
+
assert_equal true, opts[:log]
|
103
|
+
|
104
|
+
opts = @p.parse %w(--log other.log)
|
105
|
+
assert_equal true, opts[:log_given]
|
106
|
+
assert_equal "other.log", opts[:log]
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
class MultiStringFlagParserTest < ::MiniTest::Test
|
112
|
+
def setup
|
113
|
+
@p = Parser.new
|
114
|
+
@p.opt :xyz, "desc", :type => :stringflag, :multi => true
|
115
|
+
@p.opt :ghi, "desc", :type => :stringflag, :multi => true, default: ["gg","hh"]
|
116
|
+
@p.opt :abc, "desc", :type => :string, :multi => true
|
117
|
+
end
|
118
|
+
|
119
|
+
# in this case, the stringflag should return multiple strings
|
120
|
+
def test_multi_stringflag_as_strings
|
121
|
+
opts = @p.parse %w(--xyz dog --xyz cat)
|
122
|
+
assert_equal true, opts[:xyz_given]
|
123
|
+
assert_equal ["dog","cat"], opts[:xyz]
|
124
|
+
assert_equal [], opts[:abc] # note, multi-args default to empty array
|
125
|
+
assert_nil opts[:ghi_given]
|
126
|
+
assert_equal ["gg","hh"], opts[:ghi]
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_multi_stringflag_as_flags
|
130
|
+
opts = @p.parse %w(--xyz --xyz --xyz)
|
131
|
+
assert_equal true, opts[:xyz_given]
|
132
|
+
assert_equal [true, true, true], opts[:xyz]
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_multi_stringflag_as_mix1
|
136
|
+
opts = @p.parse %w(--xyz --xyz dog --xyz cat)
|
137
|
+
assert_equal true, opts[:xyz_given]
|
138
|
+
assert_equal [true, "dog", "cat"], opts[:xyz]
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_multi_stringflag_as_mix2
|
142
|
+
opts = @p.parse %w(--xyz dog --xyz cat --xyz --abc letters)
|
143
|
+
assert_equal true, opts[:xyz_given]
|
144
|
+
assert_equal ["dog", "cat", true], opts[:xyz]
|
145
|
+
assert_equal ["letters"], opts[:abc]
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_multi_stringflag_override_array_default
|
149
|
+
opts = @p.parse %w(--xyz --ghi yy --ghi zz)
|
150
|
+
assert_equal true, opts[:xyz_given]
|
151
|
+
assert_equal true, opts[:ghi_given]
|
152
|
+
assert_equal ["yy","zz"], opts[:ghi]
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: optimist_xl
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- William Morgan
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2020-
|
14
|
+
date: 2020-11-12 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: minitest
|
@@ -31,16 +31,16 @@ dependencies:
|
|
31
31
|
name: rake
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
33
33
|
requirements:
|
34
|
-
- - "
|
34
|
+
- - ">="
|
35
35
|
- !ruby/object:Gem::Version
|
36
|
-
version:
|
36
|
+
version: 12.3.3
|
37
37
|
type: :development
|
38
38
|
prerelease: false
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
|
-
- - "
|
41
|
+
- - ">="
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version:
|
43
|
+
version: 12.3.3
|
44
44
|
- !ruby/object:Gem::Dependency
|
45
45
|
name: chronic
|
46
46
|
requirement: !ruby/object:Gem::Requirement
|
@@ -57,6 +57,7 @@ dependencies:
|
|
57
57
|
version: '0'
|
58
58
|
description: |-
|
59
59
|
OptimistXL is feature filled but lightweight commandline option parser.
|
60
|
+
It contains all of the features of the Optimist gem, plus lots of additional features you didnt know you needed.
|
60
61
|
One line of code per option is all you typically need to write.
|
61
62
|
For that, you get a nice automatically-generated help page, robust option
|
62
63
|
parsing, command subcompletion, and sensible defaults for everything you don't
|
@@ -79,7 +80,10 @@ files:
|
|
79
80
|
- examples/banners1.rb
|
80
81
|
- examples/banners2.rb
|
81
82
|
- examples/banners3.rb
|
83
|
+
- examples/didyoumean.rb
|
82
84
|
- examples/medium_example.rb
|
85
|
+
- examples/optional_string_arg_type.rb
|
86
|
+
- examples/partialmatch.rb
|
83
87
|
- examples/permitted.rb
|
84
88
|
- examples/subcommands.rb
|
85
89
|
- examples/types.rb
|
@@ -95,6 +99,7 @@ files:
|
|
95
99
|
- test/optimist_xl/parser_parse_test.rb
|
96
100
|
- test/optimist_xl/parser_test.rb
|
97
101
|
- test/optimist_xl/permitted_test.rb
|
102
|
+
- test/optimist_xl/stringflag_test.rb
|
98
103
|
- test/optimist_xl/subcommands_test.rb
|
99
104
|
- test/optimist_xl/version_needed_test.rb
|
100
105
|
- test/optimist_xl_test.rb
|
@@ -122,8 +127,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
122
127
|
- !ruby/object:Gem::Version
|
123
128
|
version: '0'
|
124
129
|
requirements: []
|
125
|
-
|
126
|
-
rubygems_version: 2.7.4
|
130
|
+
rubygems_version: 3.1.2
|
127
131
|
signing_key:
|
128
132
|
specification_version: 4
|
129
133
|
summary: OptimistXL is feature fork of the Optimist commandline option parser.
|
@@ -136,6 +140,7 @@ test_files:
|
|
136
140
|
- test/optimist_xl/parser_parse_test.rb
|
137
141
|
- test/optimist_xl/parser_test.rb
|
138
142
|
- test/optimist_xl/permitted_test.rb
|
143
|
+
- test/optimist_xl/stringflag_test.rb
|
139
144
|
- test/optimist_xl/subcommands_test.rb
|
140
145
|
- test/optimist_xl/version_needed_test.rb
|
141
146
|
- test/optimist_xl_test.rb
|