optimist_xl 3.2.0 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4f71228547d0536054a7e674aa46cedde06fc46ea310e46076f4beb19585f9f5
4
- data.tar.gz: 948058a857b01c3939866fbb5b484131f5d104083c4ca4800c7072ce8eba8de7
3
+ metadata.gz: 3fe9d9828ccf8519f081c520339195ab106b579a0d09564aab1a0ea47134b00a
4
+ data.tar.gz: 1f60a13d38245b2d7214093843403ffeefb917d3a4466ef50b11904cdc733037
5
5
  SHA512:
6
- metadata.gz: f8f594f3eb28c433b9b34b81ae452060d0cd2f5cb1302b6167da58358e1c2cbf3749792918ce1f96da6c02112fb9f4f30ee85c00eb38a7cfd46372387dd37446
7
- data.tar.gz: 6fcabc7018c7660aa0fdc3c79d1d020a0a352b76fe22655c9d02bed4eb8556ec40503e6e349ba2bd968504e3cedf13ca54d4c9f8598b59f06c25121579f1b328
6
+ metadata.gz: ce7e01b876214a131e653a5c9a3f700aaa7388bf68b211927b8bcda2f80d65d963b0991bc323a23d356f4be3bf2a41c943dcc44219bc421e18be824d0d897bb8
7
+ data.tar.gz: 4f6a89f6a84d5609e84f6323e104ee535893e7f74f2ca964665d4e745292b32a640cfead9efde73d9681c2d637edc38af2fe6f68ebaa3b428e28d72d58ace39f
@@ -1,7 +1,6 @@
1
1
  language: ruby
2
2
  cache: bundler
3
3
  rvm:
4
- - 2.2
5
4
  - 2.3
6
5
  - 2.4
7
6
  - 2.5
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 Features
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 &copy; 2008-2014 [William Morgan](http://masanjin.net/).
94
224
 
95
225
  Copyright &copy; 2014 Red Hat, Inc.
96
226
 
97
- Copyright &copy; 2019 Ben Bowers
227
+ Copyright &copy; 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 :cone, "Ice cream cone"
6
+ opt :zippy, "It zips"
7
+ opt :zapzy, "It zapz"
8
+ opt :big_bug, "Madagascar cockroach"
9
+ end
10
+ p opts
11
+
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/optimist_xl'
3
+
4
+ #module OptimistXL
5
+
6
+ opts = OptimistXL::options do
7
+ opt :log, "specify optional log-file path", :type => :stringflag, :default => "progname.log"
8
+ end
9
+ p opts
10
+
@@ -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
@@ -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.2.0"
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
- unless params.empty?
406
- if @specs[sym].single_arg?
407
- given_args[sym][:params] << params[0, 1] # take the first parameter
408
- num_params_taken = 1
409
- elsif @specs[sym].multi_arg?
410
- given_args[sym][:params] << params # take all the parameters
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
- if params.empty? && !opts.flag?
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.single_arg?
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.multi_arg? && !opts.multi?
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
- ## Indicates that this is a multivalued (Array type) argument
870
- def multi_arg? ; false ; end
871
- ## note: Option-Types with both multi_arg? and flag? false are single-parameter (normal) options.
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 + (flag? && default ? ", --no-#{long}" : "")
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
- raise ArgumentError, ":type specification and default type don't match (default type is #{opttype_from_default.class})" if opttype && opttype_from_default && (opttype.class != opttype_from_default.class)
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.multi_arg?
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
- def flag? ; true ; end
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 multi_arg? ; true ; end
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 multi_arg? ; true ; end
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 multi_arg? ; true ; end
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 multi_arg? ; true ; end
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 multi_arg? ; true ; end
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
@@ -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", "~> 10.0"
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.2.0
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-03-07 00:00:00.000000000 Z
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: '10.0'
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: '10.0'
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
- rubyforge_project:
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