opt-simple 0.9.12 → 1.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 57cca5d40e69cf2f9a914290d54ca098293e06a122cfecbe0170045de81a3c2a
4
+ data.tar.gz: b069da5060d12936a2d2022053d414c3ecf84708ac62d530aae91d675a72f593
5
+ SHA512:
6
+ metadata.gz: 61a13e28947dc8ecf33690b7b6013692849fc6075136814fb831facb6cb82bdc0ae0fe90247f525e35b6ff8644cd3cdb98bd6f37d4cdbf8a715ea8e7335bd4b4
7
+ data.tar.gz: 4694df6658044f29a6c0dd2ad616bb2f0cc3ed636db884fe521f601139e4bdb2a0a4ca98a8a50063881d43b30048852a93f991d809169796ccf7152d0c4bca34
data/LICENSE.txt ADDED
@@ -0,0 +1,7 @@
1
+ Copyright (C) 2011 Ethan Stryker
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
+
5
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -1,6 +1,6 @@
1
- == OptSimple provides a very simple interface to command line parsing.
1
+ ## OptSimple provides a very simple interface to command line parsing.
2
2
 
3
- == Description
3
+ ## Description
4
4
  Parameter specification, validity checking and argument transformations
5
5
  can be put in one place, default parameters are easily set, and an automatic
6
6
  usage statement is constructed.
@@ -25,19 +25,22 @@ The order in which the parameters are defined dictate their order on the command
25
25
 
26
26
  User defined help banners, summaries or the whole usage statement can be defined.
27
27
 
28
- == Documentation
28
+ ## Documentation
29
29
 
30
30
  See OptSimple for the API specification
31
31
 
32
- == Installation
32
+ ## Installation
33
33
 
34
34
  It is recommended to install OptSimple using RubyGems:
35
35
 
36
- $ sudo gem install opt-simple
36
+ ```
37
+ $ gem install opt-simple
38
+ ```
37
39
 
38
- == Examples
40
+ ## Examples
39
41
 
40
- === One example that shows most of the behavior you might use
42
+ ### One example that shows most of the behavior you might use
43
+ ```ruby
41
44
  require 'opt_simple'
42
45
 
43
46
  defaults = {
@@ -46,7 +49,7 @@ It is recommended to install OptSimple using RubyGems:
46
49
  :range => [5,10]
47
50
  }
48
51
 
49
- opts = OptSimple.new(defaults).parse_opts! do
52
+ opts = OptSimple.new(defaults: defaults).parse_opts! do
50
53
  argument %w[-i --infile], "Infile, multiple allowed", "FILE" do | arg |
51
54
  accumulate_opt arg
52
55
  end
@@ -103,43 +106,45 @@ It is recommended to install OptSimple using RubyGems:
103
106
  puts "Range: #{opts.range.first} #{opts.range.last}"
104
107
 
105
108
  puts "Things: #{opts.things.inspect}"
109
+ ```
106
110
 
107
111
  Which prints out an automatic usage statement:
112
+ ```
108
113
  Usage: opt_ex.rb [options]
109
-
114
+
110
115
  MANDATORY ARGS:
111
- -i, --infile FILE Infile, multiple allowed
112
-
116
+ -i, --infile FILE Infile, multiple allowed
117
+
113
118
  OPTIONS:
114
- -d, --debug debug mode
115
-
116
- -m, --cowbell, --morecowbell I've got a fever, and this is the only prescription.
117
-
118
- -n, -num, --num-values VAL The answer to everything
119
-
120
- -p, --pattern, --glob-pattern PATTERN glob pattern (default is '*')
121
-
122
- --range ARG range: min,max (both >0) (default is '[5, 10]')
119
+ -d, --debug debug mode
120
+
121
+ -m, --cowbell, --morecowbell I've got a fever, and this is the only prescription.
123
122
 
124
- -t, --things THINGS Some things - variable number allowed
123
+ -n, -num, --num-values VAL The answer to everything (default is '42')
125
124
 
126
- -h, --help (for this help message)
125
+ -p, --pattern, --glob-pattern PATTERN glob pattern (default is '*')
127
126
 
128
- === A very simple example with no error checking. Use at your own risk!
127
+ --range ARG range: min,max (both >0) (default is '5,10')
129
128
 
129
+ -t, --things THINGS Some things - variable number allowed
130
+
131
+ -h, --help (for this help message)
132
+ ```
133
+ ### A very simple example with no error checking. Use at your own risk!
134
+ ``` ruby
130
135
  require 'opt_simple'
131
136
 
132
137
  defaults = {
133
138
  'max' => 40
134
139
  }
135
140
 
136
- options = OptSimple.new(defaults).parse_opts!
141
+ options = OptSimple.new(defaults: defaults).parse_opts!
137
142
 
138
143
  puts "Options"
139
144
  puts options
140
-
141
- === An example using all default behavior
142
-
145
+ ```
146
+ ### An example using all default behavior
147
+ ```ruby
143
148
  require 'opt_simple'
144
149
 
145
150
  options = OptSimple.new.parse_opts! do
@@ -151,16 +156,16 @@ Which prints out an automatic usage statement:
151
156
 
152
157
  puts "Options"
153
158
  puts options
154
-
155
- === An example that shows how to set the banner string, and add a summary.
156
-
159
+ ```
160
+ ### An example that shows how to set the banner string, and add a summary.
161
+ ```ruby
157
162
  require 'opt_simple'
158
163
 
159
164
  defaults = {
160
165
  "max" => 10
161
166
  }
162
167
 
163
- options = OptSimple.new(defaults).parse_opts! do
168
+ options = OptSimple.new(defaults: defaults).parse_opts! do
164
169
  banner "USAGE: #{$0}"
165
170
  summary "Show how to set a banner and summary."
166
171
 
@@ -172,9 +177,9 @@ Which prints out an automatic usage statement:
172
177
 
173
178
  puts "Options"
174
179
  puts options
175
-
176
- === An example that shows that you can easily set your defaults in normal Ruby variables and provide your own help.
177
-
180
+ ```
181
+ ### An example that shows that you can easily set your defaults in normal Ruby variables and provide your own help.
182
+ ```ruby
178
183
  require 'opt_simple'
179
184
 
180
185
  in_file = nil
@@ -223,9 +228,9 @@ Which prints out an automatic usage statement:
223
228
 
224
229
  puts "ARGV"
225
230
  puts ARGV
226
-
227
- === An example showing how to register parms in multiple places before parsing the command line
228
-
231
+ ```
232
+ ### An example showing how to register parms in multiple places before parsing the command line
233
+ ```ruby
229
234
  require 'opt_simple'
230
235
 
231
236
  min = 5
@@ -241,7 +246,7 @@ Which prints out an automatic usage statement:
241
246
  option %w[-p --pattern --glob-pattern], "glob pattern","PATTERN"
242
247
  end
243
248
 
244
- os.register_opts do
249
+ os.register_opts(defaults: {num: 7}) do
245
250
  option %w[-n -num --num-values],"Number of val","VAL" do |arg|
246
251
  set_opt arg.to_i
247
252
  end
@@ -263,9 +268,10 @@ Which prints out an automatic usage statement:
263
268
 
264
269
  puts "Options"
265
270
  puts options
266
-
267
- === A totally contrived example showing how to change multiple options within an option specification block
271
+ ```
272
+ ### A totally contrived example showing how to change multiple options within an option specification block
268
273
 
274
+ ```ruby
269
275
  require 'opt_simple'
270
276
 
271
277
  allowed_odds = [1,3,5,7]
@@ -277,7 +283,7 @@ Which prints out an automatic usage statement:
277
283
 
278
284
  # Note that b/c we define option [--odd] and [--evens] _after_ the max-out flag,
279
285
  # they will override the max-out setting.
280
- opts = OptSimple.new(defaults).parse_opts! do | opt_obj |
286
+ opts = OptSimple.new(defaults: defaults).parse_opts! do | opt_obj |
281
287
  flag %w[-m --max-out], "Maximize evens and odds." do
282
288
  opt_obj.odd_val = allowed_odds.last
283
289
  opt_obj.even_val = allowed_evens.last
@@ -298,9 +304,28 @@ Which prints out an automatic usage statement:
298
304
 
299
305
  puts "Options"
300
306
  puts opts
301
-
302
- == Questions and/or Comments
307
+ ```
308
+ ## Errors
309
+
310
+ Parsing problems raise a subclass of `OptSimple::Error` (a `StandardError`):
311
+ `MissingArgument`, `InvalidOption` or `ParameterUsageError`, and `error` inside a
312
+ parameter block raises `OptSimple::Error`. The `message` is the reason followed by
313
+ the usage statement; `reason` and `usage` are also available on their own.
314
+
315
+ ```ruby
316
+ begin
317
+ opts = OptSimple.new.parse_opts! { argument '--model', 'Model name', 'MODEL' }
318
+ rescue OptSimple::Error => e
319
+ abort e.message # or: abort "error: #{e.reason}"
320
+ end
321
+ ```
322
+
323
+ A registered switch is never taken as a value, so `--model --num 4` is reported
324
+ as a missing value for `--model` (negative numbers like `--num -1` still work).
325
+
326
+ ## Questions and/or Comments
303
327
 
304
- email {Ethan Stryker}[mailto:e.stryker@gmail.com]
328
+ email [Ethan Stryker](mailto:e.stryker@gmail.com])
305
329
 
306
-
330
+ ## License
331
+ MIT - see [LICENSE](LICENSE.txt)
data/lib/opt_simple.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  =begin rdoc
2
4
 
3
5
  = OptSimple provides a very simple interface to command line parsing.
@@ -27,14 +29,15 @@ class OptSimple
27
29
 
28
30
  # default keys should should be strings,
29
31
  # args can be any list of strings, but defaults to ARGV
30
- def initialize(defaults = {},args = ARGV)
32
+ def initialize(defaults: {},args: ARGV)
31
33
  @mandatory_opts = []
32
34
  @optional_opts = []
33
35
  @parameters = []
34
36
  @param_names = {}
35
37
  @results = OptSimple::Result.new
36
38
  @longest_switch_len = 0
37
- @defaults = defaults
39
+ # set defaults to empty hash just in case user sets them to nil
40
+ @defaults = defaults || {}
38
41
  @args = args.to_a # especially for jruby
39
42
  @banner = "Usage: #{File.basename($0)} [options]"
40
43
  @summary = ""
@@ -58,10 +61,11 @@ class OptSimple
58
61
 
59
62
  # Simply register options without actually parsing them.
60
63
  # This allows registering parms in multiple places in your code.
61
- def register_opts(&block)
64
+ def register_opts(defaults: {}, &block)
62
65
  # call the block to register all the parameters and
63
66
  # their corresponding code blocks
64
67
  # We use instance_exec so that the API is cleaner.
68
+ @defaults.merge!(defaults)
65
69
  instance_exec(@results,&block)
66
70
  self
67
71
  end
@@ -89,12 +93,6 @@ class OptSimple
89
93
  # go through the registered parameters, and pull out
90
94
  # the specified parms from @arg
91
95
 
92
- # first look for a call for help
93
- unless (help_strings & @args).empty?
94
- $stdout.puts self.to_s
95
- exit(0)
96
- end
97
-
98
96
  mandatory_check = mandatory_opts.map {|m| m.switches}
99
97
 
100
98
  positional_args = []
@@ -102,15 +100,28 @@ class OptSimple
102
100
  #remove the '--', but don't include it w/ positional arguments
103
101
  positional_args = @args.slice!(loc..-1)[1..-1]
104
102
  end
103
+
104
+ # look for a call for help, but not in the positional args after '--'
105
+ unless (help_strings & @args).empty?
106
+ $stdout.puts self.to_s
107
+ exit(0)
108
+ end
105
109
 
106
- # Handle the case where a user specifies --foo=bar, or --foo=bar,baz
107
- equal_args = @args.find_all {|arg| arg.include?('=') }
108
- @args.delete_if {|arg| arg.include?('=') }
109
- equal_args.each do | e |
110
- switch,list = e.split('=')
111
- @args << switch
112
- list.split(',').each {|val| @args << val }
110
+ # Handle the case where a user specifies --foo=bar, or --foo=bar,baz.
111
+ # Only switch-looking args are split, and they are expanded in place so
112
+ # values like 'a=b' and the order of positional args are left alone.
113
+ # replace (rather than reassign) keeps parse_opts! destructive on ARGV.
114
+ expanded = @args.flat_map do | arg |
115
+ if (m = arg.match(/\A(-[^=]+)=(.*)\z/m))
116
+ [m[1], *m[2].split(',')]
117
+ else
118
+ arg
119
+ end
113
120
  end
121
+ @args.replace(expanded)
122
+
123
+ # a value that is itself a registered switch means the real value is missing
124
+ all_switches = @parameters.flat_map(&:switches)
114
125
 
115
126
  # now actually parse the args, and call all the stored up blocks from the options
116
127
  @parameters.each do | parm |
@@ -146,16 +157,20 @@ class OptSimple
146
157
  end
147
158
 
148
159
  chunks.each do | pieces |
149
- if parm.block.arity > 0 and
150
- (pieces.length < parm.block.arity or
151
- pieces.any? {|p| p.start_with?('-')})
160
+ # Note that we remove the leading '-' restriction to allow for negative numbers in arguments
161
+ if parm.block.arity > 0 and (pieces.length < parm.block.arity) # or # pieces.any? {|p| p.start_with?('-')})
152
162
  raise OptSimple::ParameterUsageError.new "Not enough args following #{intersection}",self
153
163
  end
164
+
165
+ # but a registered switch is never a value: '--model --num 4' is a missing value
166
+ if (clash = pieces.find {|p| all_switches.include?(p) })
167
+ raise OptSimple::ParameterUsageError.new "Missing value for #{intersection.join(' OR ')} (found switch #{clash})",self
168
+ end
154
169
 
155
170
  begin
156
171
  parm.instance_exec(*pieces,&parm.block)
157
172
  rescue OptSimple::Error => e
158
- raise OptSimple::Error.new e.message,self
173
+ raise e.class.new(e.reason,self)
159
174
  end
160
175
  end
161
176
  @results.merge! parm.param_options
@@ -245,7 +260,7 @@ class OptSimple
245
260
  parm.set_opt @defaults[intersection.first]
246
261
  @results.merge! parm.param_options
247
262
  elsif intersection.length > 1
248
- raise OptSimple::Error "Clashes in the defaults for #{parm.switches}"
263
+ raise OptSimple::Error.new("Clashes in the defaults for #{parm.switches}",self)
249
264
  end
250
265
 
251
266
  add_parameter(parm)
@@ -271,7 +286,7 @@ class OptSimple
271
286
  # returns the automatic usage statement
272
287
  def to_s
273
288
  if @help.empty?
274
- help_str = ""
289
+ help_str = +""
275
290
 
276
291
  help_str << " MANDATORY ARGS:\n" unless mandatory_opts.empty?
277
292
  mandatory_opts.each do | parm |
@@ -292,7 +307,9 @@ class OptSimple
292
307
  elsif @defaults[intersection.first].nil?
293
308
  help_str << " (default is 'nil')\n\n"
294
309
  else
295
- help_str << " (default is '#{@defaults[intersection.first]}')\n\n"
310
+ default = @defaults[intersection.first]
311
+ default = default.join(',') if default.is_a?(Array)
312
+ help_str << " (default is '#{default}')\n\n"
296
313
  end
297
314
  end
298
315
  help_str << " SUMMARY:\n\n #{@summary}\n\n" unless @summary.empty?
@@ -358,24 +375,23 @@ class OptSimple
358
375
  end
359
376
 
360
377
  def switch_len #:nodoc:
361
- @switches.join(', ').length
378
+ switch_str.length
362
379
  end
363
380
 
381
+ # Short switches first, then the rest in the order given. Without a short
382
+ # switch, indent so long switches line up under '-x, --long'.
383
+ # (Build a new string: the switches may be frozen literals.)
364
384
  def switch_str #:nodoc:
365
385
  short_parms = @switches.find_all {|st| st.start_with?('-') and st.length == 2 and st[1] != '-'}
366
- long_parms = @switches.find_all {|st| st.start_with?('--') or (st.start_with?('-') and st.length > 2)}
367
- other_parms = @switches.find_all {|st| not st.start_with?('-')}
386
+ other_parms = @switches - short_parms
368
387
 
369
- sh_str = short_parms.empty? ? " " * 4 : short_parms.first
370
- long_str = long_parms.join(', ') + other_parms.join(', ')
371
- sh_str << ', ' unless sh_str =~/^\s+$/ or long_str.empty?
372
-
373
- sh_str + long_str
388
+ indent = short_parms.empty? ? " " * 4 : ""
389
+ indent + (short_parms + other_parms).join(', ')
374
390
  end
375
391
 
376
392
  # a single line that will be put in the overall usage string
377
393
  def help_str(switch_len)
378
- " %-#{switch_len}s" % self.switch_str + " \t#{@help}"
394
+ " %-#{switch_len}s" % self.switch_str + " #{@help}"
379
395
  end
380
396
 
381
397
  # A shortcut for raising an OptSimple::Error exception
@@ -435,7 +451,6 @@ class OptSimple
435
451
  # leading '-'s removed) will be used as keys in the Result
436
452
  # and the values set to the arg following the switch on the CL.
437
453
  class Option < Parameter
438
- attr_reader = :metavar
439
454
  def initialize(switches,help="",metavar="ARG",&block)
440
455
  super(switches,help,&block)
441
456
  @metavar = metavar
@@ -446,13 +461,8 @@ class OptSimple
446
461
  @first_call = true # use this so we can accumulate non-defaults
447
462
  end
448
463
 
449
- def switch_len #:nodoc:
450
- metavar_space = @metavar.empty? ? 0 : @metavar.length + 1
451
- super + metavar_space
452
- end
453
-
454
464
  def switch_str #:nodoc:
455
- super + " #{@metavar}"
465
+ @metavar.empty? ? super : super + " #{@metavar}"
456
466
  end
457
467
 
458
468
  # append val to the parameter list
@@ -480,10 +490,15 @@ class OptSimple
480
490
  def mandatory?; true; end
481
491
  end
482
492
 
483
- # A general error in the options
484
- class Error < Exception
485
- def initialize(string,option_obj=nil)
486
- super("#{string}\n#{option_obj.to_s}")
493
+ # A general error in the options. The message is the reason followed by the
494
+ # usage statement; both are also available separately.
495
+ class Error < StandardError
496
+ attr_reader :reason, :usage
497
+
498
+ def initialize(string=nil,option_obj=nil)
499
+ @reason = string.to_s
500
+ @usage = option_obj.to_s
501
+ super("#{@reason}\n#{@usage}")
487
502
  set_backtrace []
488
503
  end
489
504
  end
@@ -582,7 +597,7 @@ class OptSimple
582
597
 
583
598
  # a hash looking return string.
584
599
  def to_s
585
- ret = ""
600
+ ret = +""
586
601
  aliases.each do | a |
587
602
  ret << "#{a}=>#{@inside_hash[a]},"
588
603
  end
@@ -1,6 +1,6 @@
1
1
  module Test::Unit
2
2
  # Used to fix a minor minitest/unit incompatibility in flexmock
3
- AssertionFailedError = Class.new(StandardError)
3
+ # AssertionFailedError = Class.new(StandardError)
4
4
 
5
5
  class TestCase
6
6
 
data/test/test_arglist.rb CHANGED
@@ -5,7 +5,7 @@ require 'test_unit_extensions'
5
5
  class ArglistTest < Test::Unit::TestCase
6
6
  def setup
7
7
  @args = %w[infile1 -v infile2 -o out.txt infile3 -- -infile4 infile5]
8
- @os = OptSimple.new({},@args)
8
+ @os = OptSimple.new(defaults: {},args: @args)
9
9
  @block = Proc.new {
10
10
  flag '-v'
11
11
  argument '-o'
data/test/test_help.rb CHANGED
@@ -8,14 +8,14 @@ class TestHelpStatement < Test::Unit::TestCase
8
8
  defaults = {
9
9
  :a => 1000
10
10
  }
11
- os = OptSimple.new(defaults)
11
+ os = OptSimple.new(defaults: defaults)
12
12
  os.add_parameter(OptSimple::Option.new '-a' )
13
13
 
14
14
  assert os.to_s =~/default.*#{defaults['a']}/
15
15
  end
16
16
 
17
17
  must "order help according to specification order" do
18
- os = OptSimple.new({},['-h'])
18
+ os = OptSimple.new(defaults: {},args: ['-h'])
19
19
  os.add_parameter(OptSimple::Option.new '-a')
20
20
  os.add_parameter(OptSimple::Flag.new '-b')
21
21
  os.add_parameter(OptSimple::Option.new '-c')
@@ -28,7 +28,7 @@ class TestHelpStatement < Test::Unit::TestCase
28
28
 
29
29
  must "allow user defined help statements" do
30
30
  my_help = "Totally irrelevant"
31
- os = OptSimple.new({},['-h'])
31
+ os = OptSimple.new(defaults: {},args: ['-h'])
32
32
  os.add_parameter(OptSimple::Option.new '-a')
33
33
  os.add_parameter(OptSimple::Flag.new '-b')
34
34
  os.help my_help
@@ -37,14 +37,14 @@ class TestHelpStatement < Test::Unit::TestCase
37
37
  end
38
38
 
39
39
  must "provide user specified summary statement in help" do
40
- os = OptSimple.new({},['-h'])
40
+ os = OptSimple.new(defaults: {},args: ['-h'])
41
41
  os.add_parameter(OptSimple::Option.new '-a')
42
42
  os.summary "My summary"
43
43
  assert os.to_s.match(Regexp.new("My summary",Regexp::MULTILINE))
44
44
  end
45
45
 
46
46
  must "add metavars to help statement" do
47
- os = OptSimple.new({},['-h'])
47
+ os = OptSimple.new(defaults: {},args: ['-h'])
48
48
  os.add_parameter(OptSimple::Option.new '-a',"some help","THING")
49
49
  assert os.to_s.match(Regexp.new("\-a.*THING.*some help",Regexp::MULTILINE))
50
50
  end
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Regression tests for bugs found in 1.0.0. The magic comment above matters:
4
+ # switch strings below are frozen, as they are in most modern Ruby files.
5
+ require 'stringio'
6
+ require 'opt_simple'
7
+ require 'test/unit'
8
+ require 'test_unit_extensions'
9
+
10
+ class TestRegressions < Test::Unit::TestCase
11
+
12
+ def parse(args, defaults: {}, &block)
13
+ OptSimple.new(defaults: defaults, args: args).parse_opts!(&block)
14
+ end
15
+
16
+ # --- frozen switch strings -------------------------------------------------
17
+
18
+ must "build the usage statement when switches are frozen strings" do
19
+ os = OptSimple.new(args: [])
20
+ os.register_opts { option %w[-n --num], "a number", "N"; flag %w[-v --verbose] }
21
+ assert_nothing_raised { os.to_s }
22
+ assert_match(/-n, --num N/, os.to_s)
23
+ assert_match(/-v, --verbose/, os.to_s)
24
+ end
25
+
26
+ must "report a missing argument, not a FrozenError, when switches are frozen" do
27
+ assert_raise(OptSimple::MissingArgument) { parse([]) { argument %w[-i --infile] } }
28
+ end
29
+
30
+ must "leave the caller's switch strings unchanged after building usage" do
31
+ switches = %w[-n --num]
32
+ os = OptSimple.new(args: [])
33
+ os.register_opts { option switches }
34
+ os.to_s
35
+ assert_equal %w[-n --num], switches
36
+ end
37
+
38
+ must "show every short switch in the usage statement" do
39
+ os = OptSimple.new(args: [])
40
+ os.register_opts { flag %w[-v -V --verbose] }
41
+ assert_match(/-v, -V, --verbose/, os.to_s)
42
+ end
43
+
44
+ # --- '=' handling --------------------------------------------------------------
45
+
46
+ must "not treat a value containing '=' as a switch" do
47
+ o = parse(%w[--model a=b]) { option '--model' }
48
+ assert_equal 'a=b', o['model']
49
+ assert_equal [], o.positional_args
50
+ end
51
+
52
+ must "keep everything after the first '=' in --switch=value" do
53
+ o = parse(%w[--filter=key=val]) { option '--filter' }
54
+ assert_equal 'key=val', o['filter']
55
+ end
56
+
57
+ must "let the last repeated option win whether or not it uses '='" do
58
+ # 1.0.0 moved '=' args to the end, so '--a=1' was applied last
59
+ o = parse(%w[--a=1 --a 2]) { option '--a' }
60
+ assert_equal '2', o['a']
61
+ end
62
+
63
+ # --- switches used as values ---------------------------------------------------
64
+
65
+ must "raise a usage error when a switch is followed by another switch instead of a value" do
66
+ e = assert_raise(OptSimple::ParameterUsageError) do
67
+ parse(%w[--model --num 4]) { option '--model'; option '--num' }
68
+ end
69
+ assert_match(/--model.*--num/, e.reason)
70
+ end
71
+
72
+ # guard: the check above must not reject negative numbers
73
+ must "still allow negative numbers as values" do
74
+ o = parse(%w[--num -1]) { option('--num') { |v| set_opt v.to_i } }
75
+ assert_equal(-1, o['num'])
76
+ end
77
+
78
+ # --- error class ---------------------------------------------------------------
79
+
80
+ must "raise errors a plain rescue can catch" do
81
+ caught = begin
82
+ parse([]) { argument '--model' }
83
+ rescue => e
84
+ e
85
+ end
86
+ assert_kind_of OptSimple::MissingArgument, caught
87
+ assert_kind_of StandardError, caught
88
+ end
89
+
90
+ must "expose the reason and the usage statement separately" do
91
+ e = assert_raise(OptSimple::MissingArgument) { parse([]) { argument '--model' } }
92
+ assert_equal 'Must set the following parameters: --model', e.reason
93
+ assert_match(/\AUsage:/, e.usage)
94
+ assert_equal "#{e.reason}\n#{e.usage}", e.message
95
+ end
96
+
97
+ must "keep the error subclass and add usage when a block calls error" do
98
+ e = assert_raise(OptSimple::Error) do
99
+ parse(%w[--num x]) { option('--num') { |v| error "--num must be a number" } }
100
+ end
101
+ assert_equal '--num must be a number', e.reason
102
+ assert_match(/--num/, e.usage)
103
+ end
104
+
105
+ must "raise OptSimple::Error when two defaults name the same option" do
106
+ assert_raise(OptSimple::Error) do
107
+ parse([], defaults: { 'num-val' => 1, num_val: 2 }) { option '--num-val' }
108
+ end
109
+ end
110
+
111
+ # --- help ----------------------------------------------------------------------
112
+
113
+ must "not show help for --help after '--'" do
114
+ o = parse(%w[-- --help]) { option '--model' }
115
+ assert_equal %w[--help], o.positional_args
116
+ end
117
+
118
+ # guard: moving the help check after the '--' split must not break help
119
+ must "show help and exit for --help before '--'" do
120
+ out = StringIO.new
121
+ begin
122
+ $stdout = out
123
+ assert_raise(SystemExit) { parse(%w[--help --]) { option '--model' } }
124
+ ensure
125
+ $stdout = STDOUT
126
+ end
127
+ assert_match(/--model/, out.string)
128
+ end
129
+
130
+ must "line up the help descriptions in one column" do
131
+ os = OptSimple.new(args: [])
132
+ os.register_opts do
133
+ argument %w[-i --infile], "infile", "FILE"
134
+ option '--a-much-longer-option-name', "long one", "VAL"
135
+ flag '-v', "verbose"
136
+ end
137
+ lines = os.to_s.lines.grep(/infile$|long one$|verbose$/)
138
+ assert_equal 3, lines.size
139
+ columns = lines.map { |l| l.index(/infile$|long one$|verbose$/) }
140
+ assert_equal 1, columns.uniq.size, os.to_s
141
+ end
142
+
143
+ must "show array defaults the way they are typed on the command line" do
144
+ os = OptSimple.new(defaults: { tasks: %w[a b] }, args: [])
145
+ os.register_opts { option('--tasks') { |*t| set_opt t } }
146
+ assert_match(/default is 'a,b'/, os.to_s)
147
+ end
148
+ end
data/test/test_usage.rb CHANGED
@@ -9,7 +9,7 @@ class TestHelpStatement < Test::Unit::TestCase
9
9
  end
10
10
 
11
11
  must "throw parameter missing exception when the argument method is used but the parm isn't specified" do
12
- os = OptSimple.new({})
12
+ os = OptSimple.new(defaults: {})
13
13
  assert_raise(OptSimple::MissingArgument) do
14
14
  os.parse_opts! do
15
15
  argument '-a'
@@ -18,7 +18,7 @@ class TestHelpStatement < Test::Unit::TestCase
18
18
  end
19
19
 
20
20
  must "throw missing parameter usage error when a parameter isn't followed by enough arguments" do
21
- os = OptSimple.new({},['-a'])
21
+ os = OptSimple.new(defaults: {},args: ['-a'])
22
22
  assert_raise(OptSimple::ParameterUsageError) do
23
23
  os.parse_opts! do
24
24
  argument '-a' do |arg|
@@ -27,7 +27,7 @@ class TestHelpStatement < Test::Unit::TestCase
27
27
  end
28
28
  end
29
29
 
30
- os = OptSimple.new({},%w[--range 5])
30
+ os = OptSimple.new(defaults: {},args: %w[--range 5])
31
31
  assert_raise(OptSimple::ParameterUsageError) do
32
32
  os.parse_opts! do
33
33
  argument '--range' do |min,max|
@@ -38,12 +38,12 @@ class TestHelpStatement < Test::Unit::TestCase
38
38
  end
39
39
 
40
40
  must "raise error when unknown option is given" do
41
- os = OptSimple.new({},['-not-specified'])
41
+ os = OptSimple.new(defaults: {},args: ['-not-specified'])
42
42
  assert_raise(OptSimple::InvalidOption) { os.parse_opts! { option %w[-a --awesome] } }
43
43
  end
44
44
 
45
45
  must "handle arguments with equals and commas" do
46
- os = OptSimple.new({},['-a=2','--foo=4,5'])
46
+ os = OptSimple.new(defaults: {},args: ['-a=2','--foo=4,5'])
47
47
  x = nil
48
48
  y = nil
49
49
  opts= os.parse_opts! do
@@ -62,7 +62,7 @@ class TestHelpStatement < Test::Unit::TestCase
62
62
  end
63
63
 
64
64
  must "accumulate lists of args when asked" do
65
- os = OptSimple.new({},%w[-i foo.bar --infile bar.in])
65
+ os = OptSimple.new(defaults: {},args: %w[-i foo.bar --infile bar.in])
66
66
  o = os.parse_opts! do
67
67
  option %w[-i --infile], "Infile, multiple allowed", "FILE" do | arg |
68
68
  accumulate_opt arg
@@ -74,7 +74,7 @@ class TestHelpStatement < Test::Unit::TestCase
74
74
  end
75
75
 
76
76
  must "accumulate numbers of flags set when asked" do
77
- os = OptSimple.new({},%w[-v -v --verbose -v])
77
+ os = OptSimple.new(defaults: {},args: %w[-v -v --verbose -v])
78
78
  o = os.parse_opts! do
79
79
  flag %w[-v --verbose],"Verbosity. the more you set, the more we give" do
80
80
  accumulate_opt
@@ -85,7 +85,7 @@ class TestHelpStatement < Test::Unit::TestCase
85
85
  end
86
86
 
87
87
  must "set last arg when duplicated when accumulate opt isn't used" do
88
- os = OptSimple.new({},%w[-i foo.bar --infile bar.in -i baz])
88
+ os = OptSimple.new(defaults: {},args: %w[-i foo.bar --infile bar.in -i baz])
89
89
  o = os.parse_opts! do
90
90
  option %w[-i --infile], "Infile, multiple allowed", "FILE" do | arg |
91
91
  set_opt arg
@@ -97,7 +97,7 @@ class TestHelpStatement < Test::Unit::TestCase
97
97
  end
98
98
 
99
99
  must "allow parameters to be registered in multiple spots" do
100
- os = OptSimple.new({},%w[-i foo.bar --outfile bar.out])
100
+ os = OptSimple.new(defaults: {},args: %w[-i foo.bar --outfile bar.out])
101
101
  os.register_opts do
102
102
  option %w[-i --infile], "Infile", "FILE" do | arg |
103
103
  set_opt arg
@@ -115,6 +115,27 @@ class TestHelpStatement < Test::Unit::TestCase
115
115
  assert_equal o['o'],'bar.out'
116
116
  end
117
117
 
118
+ must "allow defaults when registering opts in multiple spots" do
119
+ os = OptSimple.new(defaults: {outfile: 'old-default'},args: %w[-i foo.bar])
120
+
121
+ os.register_opts do
122
+ option %w[-i --infile], "Infile", "FILE" do | arg |
123
+ set_opt arg
124
+ end
125
+ end
126
+
127
+ os.register_opts(defaults: {outfile: 'new-default'}) do
128
+ option %w[-o --outfile], "outfile", "FILE" do | arg |
129
+ set_opt arg
130
+ end
131
+ end
132
+ o = os.parse_opts!
133
+
134
+ assert_equal o['i'],'foo.bar'
135
+ assert_equal o['o'],'new-default'
136
+
137
+ end
138
+
118
139
  must "set flags to false by default" do
119
140
  os = OptSimple.new
120
141
  o = os.parse_opts! do
@@ -125,7 +146,7 @@ class TestHelpStatement < Test::Unit::TestCase
125
146
  end
126
147
 
127
148
  must "add undersore versions to all switch names that have a dash" do
128
- os = OptSimple.new({},%w[--some-stuff foo])
149
+ os = OptSimple.new(defaults: {},args: %w[--some-stuff foo])
129
150
  o = os.parse_opts! do
130
151
  option %w[-s --some-stuff]
131
152
  end
@@ -135,7 +156,7 @@ class TestHelpStatement < Test::Unit::TestCase
135
156
  end
136
157
 
137
158
  must "overwrite defaults when accumulate opt is used and option is seen on the CL" do
138
- os = OptSimple.new({:some_stuff => ['bar']},%w[--some-stuff foo --some-stuff bar])
159
+ os = OptSimple.new(defaults: {:some_stuff => ['bar']},args: %w[--some-stuff foo --some-stuff bar])
139
160
  o = os.parse_opts! do
140
161
  option %w[-s --some-stuff] do | arg |
141
162
  accumulate_opt arg
@@ -146,7 +167,7 @@ class TestHelpStatement < Test::Unit::TestCase
146
167
  end
147
168
 
148
169
  must "successfully pull out two options following a switch when desired" do
149
- o = OptSimple.new(nil,%w[--range 4 9]).parse_opts! do
170
+ o = OptSimple.new(defaults: nil,args: %w[--range 4 9]).parse_opts! do
150
171
  argument "--range","min,max" do | arg1, arg2 |
151
172
  set_opt [arg1.to_i,arg2.to_i]
152
173
  end
@@ -155,7 +176,17 @@ class TestHelpStatement < Test::Unit::TestCase
155
176
  assert_equal o.range.first, 4
156
177
  assert_equal o.range.last, 9
157
178
  end
158
-
179
+
180
+ must "successfully pull out two options with negative numbers following a switch when desired" do
181
+ o = OptSimple.new(defaults: nil,args: %w[--range -4 9]).parse_opts! do
182
+ argument "--range","min,max" do | arg1, arg2 |
183
+ set_opt [arg1.to_i,arg2.to_i]
184
+ end
185
+ end
186
+
187
+ assert_equal o.range.first, -4
188
+ assert_equal o.range.last, 9
189
+ end
159
190
 
160
191
  must "allow for a variable number of options following a switch when a splat is used" do
161
192
 
@@ -166,7 +197,7 @@ class TestHelpStatement < Test::Unit::TestCase
166
197
  arg_lists.each do |args |
167
198
  # Gotta account for '--whatever and --things switch in there'
168
199
  expected_len = args.length - 2
169
- os = OptSimple.new(nil,args).parse_opts! do
200
+ os = OptSimple.new(defaults: nil,args: args).parse_opts! do
170
201
  argument "--things" do | *arg |
171
202
  arg.each do |a|
172
203
  accumulate_opt a
@@ -184,7 +215,7 @@ class TestHelpStatement < Test::Unit::TestCase
184
215
  # Hmm ... just want to be flexible here. perhaps not necessary
185
216
  # Note you still gotta initialize things to an empty list to make it not nil
186
217
  must "allow for no options following an option defined with a splat" do
187
- os = OptSimple.new({:things => [] },%w[--whatever --things]).parse_opts! do
218
+ os = OptSimple.new(defaults: {:things => [] },args: %w[--whatever --things]).parse_opts! do
188
219
  option "--things" do | *arg |
189
220
  arg.each do |a|
190
221
  accumulate_opt a
@@ -196,4 +227,3 @@ class TestHelpStatement < Test::Unit::TestCase
196
227
  assert os.things.empty?
197
228
  end
198
229
  end
199
-
metadata CHANGED
@@ -1,66 +1,51 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: opt-simple
3
- version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.9.12
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.1.0
6
5
  platform: ruby
7
- authors:
6
+ authors:
8
7
  - Ethan Stryker
9
- autorequire:
10
8
  bindir: bin
11
9
  cert_chain: []
12
-
13
- date: 2011-12-27 00:00:00 +00:00
14
- default_executable:
10
+ date: 1980-01-02 00:00:00.000000000 Z
15
11
  dependencies: []
16
-
17
- description: " Parameter specification, validity checking and argument transformations \n can be put in one place, default parameters are easily set, and an \n automatic usage statement is constructed.\n"
12
+ description: " Parameter specification, validity checking and argument transformations
13
+ \n can be put in one place, default parameters are easily set, and an \n automatic
14
+ usage statement is constructed.\n"
18
15
  email: e.stryker@gmail.com
19
16
  executables: []
20
-
21
17
  extensions: []
22
-
23
18
  extra_rdoc_files: []
24
-
25
- files:
26
- - lib/test_unit_extensions.rb
19
+ files:
20
+ - LICENSE.txt
21
+ - README.md
22
+ - extensions/test_unit_extensions.rb
27
23
  - lib/opt_simple.rb
28
- - lib/tmp.rb
29
- - test/test_help.rb
30
- - test/test_usage.rb
24
+ - lib/test_unit_extensions.rb
31
25
  - test/test_arglist.rb
32
- - test/test_internals.rb
26
+ - test/test_help.rb
27
+ - test/test_regressions.rb
33
28
  - test/test_return.rb
34
- - extensions/test_unit_extensions.rb
35
- - README
36
- - GPLv2-LICENSE
37
- has_rdoc: true
38
- homepage: http://opt-simple.rubyforge.org/
39
- licenses: []
40
-
41
- post_install_message:
29
+ - test/test_usage.rb
30
+ homepage: https://github.com/estryker/opt-simple
31
+ licenses:
32
+ - MIT
33
+ metadata: {}
42
34
  rdoc_options: []
43
-
44
- require_paths:
35
+ require_paths:
45
36
  - lib
46
- required_ruby_version: !ruby/object:Gem::Requirement
47
- none: false
48
- requirements:
37
+ required_ruby_version: !ruby/object:Gem::Requirement
38
+ requirements:
49
39
  - - ">="
50
- - !ruby/object:Gem::Version
51
- version: "0"
52
- required_rubygems_version: !ruby/object:Gem::Requirement
53
- none: false
54
- requirements:
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ requirements:
55
44
  - - ">="
56
- - !ruby/object:Gem::Version
57
- version: "0"
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
58
47
  requirements: []
59
-
60
- rubyforge_project: opt-simple
61
- rubygems_version: 1.6.2
62
- signing_key:
63
- specification_version: 3
48
+ rubygems_version: 4.0.10
49
+ specification_version: 4
64
50
  summary: A simple and elegant command line option parser.
65
51
  test_files: []
66
-
data/GPLv2-LICENSE DELETED
@@ -1,18 +0,0 @@
1
- OptSimple is a utility for parsing command line arguments.
2
- Copyright (C) 2011 Ethan Stryker
3
-
4
- This program is free software; you can redistribute it and/or
5
- modify it under the terms of the GNU General Public License
6
- as published by the Free Software Foundation; either version 2
7
- of the License, or (at your option) any later version.
8
-
9
- This program is distributed in the hope that it will be useful,
10
- but WITHOUT ANY WARRANTY; without even the implied warranty of
11
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
- GNU General Public License for more details.
13
-
14
- You should have received a copy of the GNU General Public License
15
- along with this program; if not, write to the Free Software
16
- Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
-
18
- Questions/Comments on OptSimple can be mailed to Ethan Stryker, e.stryker@gmail.com
data/lib/tmp.rb DELETED
@@ -1,63 +0,0 @@
1
- # in parse_opts!
2
- # now actually parse the args, and call all the stored up blocks from the options
3
- @parameters.each do | parm |
4
- intersection = @args & parm.switches
5
-
6
- unless intersection.empty?
7
- mandatory_check.delete(parm.switches)
8
-
9
- arg_locations = []
10
- @args.each_with_index {|arg,i| arg_locations << i if intersection.include?(arg) }
11
- # ...
12
- def flag(switches,help="",&block)
13
- parm = Flag.new(switches,help,&block)
14
- parm.set_opt false
15
- add_parameter parm
16
-
17
- # add it to the results to make checking flags nicer.
18
- # use the first name b/c they are all aliased anyways.
19
- @results.merge! parm.param_options
20
- end
21
-
22
- def option(switches,help="",metavar="ARG",&block)
23
- parm = Option.new(switches,help,metavar,&block)
24
-
25
- # set the defaults, if there are any in the defaults hash
26
- intersection = parm.names & @defaults.keys
27
- if intersection.length == 1
28
- parm.set_opt @defaults[intersection.first]
29
- @results.merge! parm.param_options
30
- elsif intersection.length > 1
31
- raise OptSimple::Error "Clashes in the defaults for #{parm.switches}"
32
- end
33
-
34
- add_parameter(parm)
35
- end
36
-
37
- class Option < Parameter
38
- attr_reader = :metavar
39
- def initialize(switches,help="",metavar="ARG",&block)
40
- super(switches,help,&block)
41
- @metavar = metavar
42
- unless block_given?
43
- @block = Proc.new {|arg| @param_options[names.first] = arg}
44
- end
45
- @first_call = true # use this so we can accumulate non-defaults
46
- end
47
- def switch_len #:nodoc:
48
- metavar_space = @metavar.empty? ? 0 : @metavar.length + 1
49
- super + metavar_space
50
- end
51
- def switch_str #:nodoc:
52
- super + " #{@metavar}"
53
- end
54
- # append val to the parameter list
55
- def accumulate_opt(val)
56
- if @first_call
57
- @param_options[names.first] = [val].flatten
58
- else
59
- @param_options[names.first] << val
60
- end
61
- @first_call = false
62
- end
63
- end
@@ -1,21 +0,0 @@
1
- require 'opt_simple'
2
- require 'test/unit'
3
- require 'test_unit_extensions'
4
-
5
- class InternalsTest < Test::Unit::TestCase
6
- def setup
7
- @args = %w[infile1 -v infile2 -o out.txt infile3 -- -infile4 infile5]
8
- @os = OptSimple.new({},@args)
9
- @block = Proc.new {
10
- flag '-v'
11
- argument '-o'
12
- }
13
- end
14
-
15
- must "not freeze strings" do
16
- options = @os.parse_opts! &@block
17
- assert_nothing_raised do
18
- options[:o].gsub!('.txt','.out')
19
- end
20
- end
21
- end