benry-cmdopt 1.1.0 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -1,38 +1,52 @@
1
- Benry::Cmdopt README
2
- ====================
1
+ # Benry-CmdOpt
3
2
 
4
- ($Release: 1.1.0 $)
3
+ ($Release: 2.0.1 $)
5
4
 
6
- Benry::Cmdopt is a command option parser library, like `optparse.rb`.
7
5
 
8
- Compared to `optparse.rb`, Benry::Cmdopt is easy to use, easy to extend,
6
+
7
+ ## What's This?
8
+
9
+ Benry-CmdOpt is a command option parser library, like `optparse.rb`
10
+ (Ruby standard library).
11
+
12
+ Compared to `optparse.rb`, Benry-CmdOpt is easy to use, easy to extend,
9
13
  and easy to understahnd.
10
14
 
11
- (Benry::Cmdopt requires Ruby >= 2.3)
15
+ * Document: <https://kwatch.github.io/benry-ruby/benry-cmdopt.html>
16
+ * GitHub: <https://github.com/kwatch/benry-ruby/tree/main/benry-cmdopt>
17
+ * Changes: <https://github.com/kwatch/benry-ruby/tree/main/benry-cmdopt/CHANGES.md>
12
18
 
19
+ Benry-CmdOpt requires Ruby >= 2.3.
13
20
 
14
- Why not `optparse.rb`?
15
- ======================
16
21
 
17
- * Source code of `optparse.rb` is very large and complicated, because
18
- `OptParse` class does everything about command option parsing.
19
- It is hard to customize or extend `OptionParser` class.
20
22
 
21
- On the other hand, `benry/cmdopt.rb` consists of several classes
22
- (schema class, parser class, and facade class).
23
- Therefore it is easy to understand and extend these classes.
23
+ ### Table of Contents
24
24
 
25
- File `optparse.rb` contains 1234 lines (without comments), while
26
- `benry/cmdopt.rb` (v1.1.0) contains only 361 lines (without comments).
25
+ <!-- TOC -->
26
+
27
+ * [What's This?](#whats-this)
28
+ * [Why not `optparse.rb`?](#why-not-optparserb)
29
+ * [Install](#install)
30
+ * [Usage](#usage)
31
+ * [Define, Parse, and Print Help](#define-parse-and-print-help)
32
+ * [Command Option Parameter](#command-option-parameter)
33
+ * [Argument Validation](#argument-validation)
34
+ * [Boolean (on/off) Option](#boolean-onoff-option)
35
+ * [Alternative Value](#alternative-value)
36
+ * [Multiple Value Option](#multiple-value-option)
37
+ * [Hidden Option](#hidden-option)
38
+ * [Global Options with Sub-Commands](#global-options-with-sub-commands)
39
+ * [Detailed Description of Option](#detailed-description-of-option)
40
+ * [Option Tag](#option-tag)
41
+ * [Not Supported](#not-supported)
42
+ * [Internal Classes](#internal-classes)
43
+ * [License and Copyright](#license-and-copyright)
44
+
45
+ <!-- /TOC -->
27
46
 
28
- * `optparse.rb` regards `-x` as a short cut of `--xxx` automatically
29
- even if you have not defined `-x` option.
30
- That is, short options which are not defined can be available unexpectedly.
31
- This feature is hard-coded in `OptionParser#parse_in_order()`
32
- and hard to be disabled.
33
47
 
34
- On the other hand, `benry/cmdopt.rb` doesn't behave this way.
35
- `-x` option is available only when `-x` is defined.
48
+
49
+ ## Why not `optparse.rb`?
36
50
 
37
51
  * `optparse.rb` can handle both `--name=val` and `--name val` styles.
38
52
  The later style is ambiguous; you may wonder whether `--name` takes
@@ -44,39 +58,20 @@ Why not `optparse.rb`?
44
58
  `optparse.rb` cannot disable `--name val` style.
45
59
  `benry/cmdopt.rb` supports only `--name=val` style.
46
60
 
47
- * `optparse.rb` enforces you to catch `OptionParser::ParseError` exception.
48
- That is, you must know error class name.
49
-
50
- `benry/cmdopt.rb` provides error handler without exception class name.
51
- You don't need to know error class name on error handling.
52
-
53
- ```ruby
54
- ### optparse.rb
55
- require 'optparse'
56
- parser = OptionParser.new
57
- parser.on('-f', '--file=<FILE>', "filename")
58
- opts = {}
59
- begin
60
- parser.parse!(ARGV, into: opts)
61
- rescue OptionParser::ParseError => ex # specify error class
62
- $stderr.puts "ERROR: #{ex.message}"
63
- exit 1
64
- end
61
+ * `optparse.rb` regards `-x` and `--x` as a short cut of `--xxx` automatically
62
+ even if you have not defined `-x` option.
63
+ That is, short options which are not defined can be available unexpectedly.
64
+ This feature is hard-coded in `OptionParser#parse_in_order()`
65
+ and hard to be disabled.
65
66
 
66
- ### benry/cmdopt.rb
67
- require 'benry/cmdopt'
68
- cmdopt = Benry::Cmdopt.new
69
- cmdopt.add(:file, '-f, --file=<FILE>', "filename")
70
- opts = cmdopt.parse(ARGV) do |err| # error handling wihtout error class name
71
- $stderr.puts "ERROR: #{err.message}"
72
- exit 1
73
- end
74
- ```
67
+ In contact, `benry/cmdopt.rb` doesn't behave this way.
68
+ `-x` option is available only when `-x` is defined.
69
+ `benry/cmdopt.rb` does nothing superfluous.
75
70
 
76
71
  * `optparse.rb` uses long option name as hash key automatically, but
77
- it doesn't provide the way to specify hash key of short-only option.
72
+ it doesn't provide the way to specify hash key for short-only option.
78
73
 
79
- `benry/cmdopt.rb` can specify hash key of short-only option.
74
+ `benry/cmdopt.rb` can specify hash key for short-only option.
80
75
 
81
76
  ```ruby
82
77
  ### optparse.rb
@@ -95,7 +90,7 @@ p opts #=> {:q=>true} # hash key is short option name
95
90
 
96
91
  ### benry/cmdopt.rb
97
92
  require 'benry/cmdopt'
98
- cmdopt = Benry::Cmdopt.new
93
+ cmdopt = Benry::CmdOpt.new
99
94
  cmdopt.add(:verbose, '-v, --verbose', "verbose mode") # short and long
100
95
  cmdopt.add(:quiet , '-q' , "quiet mode") # short-only
101
96
  #
@@ -106,12 +101,49 @@ opts = cmdopt.parse(['-q']) # short option
106
101
  p opts #=> {:quiet=>true} # independent hash key of option name
107
102
  ```
108
103
 
104
+ * `optparse.rb` provides severay ways to validate option values, such as
105
+ type class, Regexp as pattern, or Array/Set as enum. But it doesn't
106
+ accept Range object. This means that, for examle, it is not simple to
107
+ validate whether integer or float value is positive or not.
108
+
109
+ In contract, `benry/cmdopt.rb` accepts Range object so it is very simple
110
+ to validate whether integer or float value is positive or not.
111
+
112
+ ```ruby
113
+ ### optparse.rb
114
+ parser = OptionParser.new
115
+ parser.on('-n <N>', "number", Integer, (1..)) #=> NoMethodError
116
+
117
+ ### benry/cmdopt.rb
118
+ require 'benry/cmdopt'
119
+ cmdopt = Benry::CmdOpt.new
120
+ cmdopt.add(:number, "-n <N>", "number", type: Integer, range: (1..)) #=> ok
121
+ ```
122
+
123
+ * `optparse.rb` accepts Array or Set object as enum values. But values
124
+ of enum should be a String in spite that type class specified.
125
+ This seems very strange and not intuitive.
126
+
127
+ `benry/cmdopt.rb` accepts integer values as enum when type class is Integer.
128
+
129
+ ```ruby
130
+ ### optparse.rb
131
+ parser = OptionParser.new
132
+ parser.on('-n <N>', "number", Integer, [1, 2, 3]) # wrong
133
+ parser.on('-n <N>', "number", Integer, ['1','2','3']) # ok (but not intuitive)
134
+
135
+ ### benry/cmdopt.rb
136
+ require 'benry/cmdopt'
137
+ cmdopt = Benry::CmdOpt.new
138
+ cmdopt.add(:number, "-n <N>", "number", type: Integer, enum: [1, 2, 3]) # very intuitive
139
+ ```
140
+
109
141
  * `optparse.rb` adds `-h` and `--help` options automatically, and
110
- terminates current process when `-v` or `--version` specified in terminal.
142
+ terminates current process when `-h` or `--help` specified in command-line.
111
143
  It is hard to remove these options.
112
144
 
113
- On the other hand, `benry/cmdopt.rb` does not add these options.
114
- benry/cmdopt.rb` does nothing extra.
145
+ In contract, `benry/cmdopt.rb` does not add these options.
146
+ `benry/cmdopt.rb` does nothing superfluous.
115
147
 
116
148
  ```ruby
117
149
  require 'optparse'
@@ -127,9 +159,11 @@ puts 'xxx' #<== not printed because current process alreay terminated
127
159
  * `optparse.rb` adds `-v` and `--version` options automatically, and
128
160
  terminates current process when `-v` or `--version` specified in terminal.
129
161
  It is hard to remove these options.
162
+ This behaviour is not desirable because `optparse.rb` is just a library,
163
+ not framework.
130
164
 
131
- On the other hand, `benry/cmdopt.rb` does not add these options.
132
- benry/cmdopt.rb` does nothing extra.
165
+ In contract, `benry/cmdopt.rb` does not add these options.
166
+ `benry/cmdopt.rb` does nothing superfluous.
133
167
 
134
168
  ```ruby
135
169
  require 'optparse'
@@ -146,6 +180,15 @@ puts 'xxx' #<== not printed because current process alreay terminated
146
180
  contain `-h`, `--help`, `-v`, nor `--version`.
147
181
  These options are available but not shown in help message. Strange.
148
182
 
183
+ * `optparse.rb` generate help message which contains command usage string
184
+ such as `Usage: <command> [options]`. `optparse.rb` should NOT include
185
+ it in help message because it is just a library, not framework.
186
+ If you want to change '[options]' to '[<options>]', you must manipulate
187
+ help message string by yourself.
188
+
189
+ `benry/cmdopt.rb` doesn't include extra text (such as usage text) into
190
+ help message. `benry/cmdopt.rb` does nothing superfluous.
191
+
149
192
  * `optparse.rb` generates help message with too wide option name
150
193
  by default. You must specify proper width.
151
194
 
@@ -156,7 +199,7 @@ puts 'xxx' #<== not printed because current process alreay terminated
156
199
  ### optparse.rb
157
200
  require 'optparse'
158
201
  banner = "Usage: blabla <options>"
159
- parser = OptionParser.new(banner) # or OptionParser.new(banner, 25)
202
+ parser = OptionParser.new(banner) # or: OptionParser.new(banner, 25)
160
203
  parser.on('-f', '--file=<FILE>', "filename")
161
204
  parser.on('-m <MODE>' , "verbose/quiet")
162
205
  puts parser.help
@@ -167,37 +210,83 @@ puts parser.help
167
210
 
168
211
  ### benry/cmdopt.rb
169
212
  require 'benry/cmdopt'
170
- cmdopt = Benry::Cmdopt.new()
213
+ cmdopt = Benry::CmdOpt.new()
171
214
  cmdopt.add(:file, '-f, --file=<FILE>', "filename")
172
215
  cmdopt.add(:mode, '-m <MODE>' , "verbose/quiet")
173
- puts "Usage: blabla <options>"
174
- puts cmdopt.option_help()
216
+ puts "Usage: blabla [<options>]"
217
+ puts cmdopt.to_s()
175
218
  ### output (calculated proper width)
176
- # Usage: blabla <options>
219
+ # Usage: blabla [<options>]
177
220
  # -f, --file=<FILE> : filename
178
221
  # -m <MODE> : verbose/quiet
179
222
  ```
180
223
 
224
+ * `optparse.rb` enforces you to catch `OptionParser::ParseError` exception.
225
+ That is, you must know the error class name.
226
+
227
+ `benry/cmdopt.rb` provides error handler without exception class name.
228
+ You don't need to know the error class name on error handling.
229
+
230
+ ```ruby
231
+ ### optparse.rb
232
+ require 'optparse'
233
+ parser = OptionParser.new
234
+ parser.on('-f', '--file=<FILE>', "filename")
235
+ opts = {}
236
+ begin
237
+ parser.parse!(ARGV, into: opts)
238
+ rescue OptionParser::ParseError => err # specify error class
239
+ abort "ERROR: #{err.message}"
240
+ end
241
+
242
+ ### benry/cmdopt.rb
243
+ require 'benry/cmdopt'
244
+ cmdopt = Benry::CmdOpt.new
245
+ cmdopt.add(:file, '-f, --file=<FILE>', "filename")
246
+ opts = cmdopt.parse(ARGV) do |err| # error handling wihtout error class name
247
+ abort "ERROR: #{err.message}"
248
+ end
249
+ ```
250
+
251
+ * The source code of "optparse.rb" is quite large and complex for a command
252
+ option parser library. The reason is that one large `OptParse` class
253
+ does everything related to parsing command options. Bad class design.
254
+ Therefore it is hard to customize or extend `OptionParser` class.
255
+
256
+ In contract, `benry/cmdopt.rb` consists of several classes
257
+ (schema class, parser class, and facade class).
258
+ Therefore it is easy to understand and extend these classes.
259
+
260
+ In fact, file `optparse.rb` and `optparse/*.rb` (in Ruby 3.2)
261
+ contains total 1298 lines (except comments and blanks), while
262
+ `benry/cmdopt.rb` (v2.0.0) contains only 429 lines (except both, too).
263
+
264
+
265
+
266
+ ## Install
267
+
268
+ ```
269
+ $ gem install benry-cmdopt
270
+ ```
271
+
181
272
 
182
- Usage
183
- =====
184
273
 
274
+ ## Usage
185
275
 
186
- Define, parse, and print help
187
- -----------------------------
276
+
277
+ ### Define, Parse, and Print Help
188
278
 
189
279
  ```ruby
190
280
  require 'benry/cmdopt'
191
281
 
192
282
  ## define
193
- cmdopt = Benry::Cmdopt.new
283
+ cmdopt = Benry::CmdOpt.new
194
284
  cmdopt.add(:help , '-h, --help' , "print help message")
195
285
  cmdopt.add(:version, ' --version', "print version")
196
286
 
197
287
  ## parse with error handling
198
288
  options = cmdopt.parse(ARGV) do |err|
199
- $stderr.puts "ERROR: #{err.message}"
200
- exit(1)
289
+ abort "ERROR: #{err.message}"
201
290
  end
202
291
  p options # ex: {:help => true, :version => true}
203
292
  p ARGV # options are removed from ARGV
@@ -207,139 +296,374 @@ if options[:help]
207
296
  puts "Usage: foobar [<options>] [<args>...]"
208
297
  puts ""
209
298
  puts "Options:"
210
- puts cmdopt.option_help()
211
- ## or
299
+ puts cmdopt.to_s()
300
+ ## or: puts cmdopt.to_s(20) # width
301
+ ## or: puts cmdopt.to_s(" %-20s : %s") # format
302
+ ## or:
212
303
  #format = " %-20s : %s"
213
- #cmdopt.each_option_help {|opt, help| puts format % [opt, help] }
304
+ #cmdopt.each_option_and_desc {|opt, help| puts format % [opt, help] }
214
305
  end
215
306
  ```
216
307
 
308
+ You can set `nil` to option name only if long option specified.
309
+
310
+ ```ruby
311
+ ## both are same
312
+ cmdopt.add(:help, "-h, --help", "print help message")
313
+ cmdopt.add(nil , "-h, --help", "print help message")
314
+ ```
315
+
217
316
 
218
- Command option parameter
219
- ------------------------
317
+ ### Command Option Parameter
220
318
 
221
319
  ```ruby
222
320
  ## required parameter
223
- cmdopt.add(:file, '-f, --file=<FILE>', "filename")
224
- cmdopt.add(:file, ' --file=<FILE>', "filename")
225
- cmdopt.add(:file, '-f <FILE>' , "filename")
321
+ cmdopt.add(:file, '-f, --file=<FILE>', "filename") # short & long
322
+ cmdopt.add(:file, ' --file=<FILE>', "filename") # long only
323
+ cmdopt.add(:file, '-f <FILE>' , "filename") # short only
226
324
 
227
325
  ## optional parameter
228
- cmdopt.add(:file, '-f, --file[=<FILE>]', "filename")
229
- cmdopt.add(:file, ' --file[=<FILE>]', "filename")
230
- cmdopt.add(:file, '-f[<FILE>]' , "filename")
326
+ cmdopt.add(:indent, '-i, --indent[=<N>]', "indent width") # short & long
327
+ cmdopt.add(:indent, ' --indent[=<N>]', "indent width") # long only
328
+ cmdopt.add(:indent, '-i[<N>]' , "indent width") # short only
231
329
  ```
232
330
 
233
- Notice that `"--file <FILE>"` style is not supported.
234
- Please use `"--file=<FILE>"` style.
331
+ Notice that `"--file <FILE>"` style is **not supported for usability reason**.
332
+ Use `"--file=<FILE>"` style instead.
235
333
 
334
+ (From a usability perspective, the former style should not be supported.
335
+ `optparse.rb` is wrong because it supports both styles
336
+ and doesn't provide the way to disable the former style.)
236
337
 
237
- Argument varidation
238
- -------------------
338
+
339
+ ### Argument Validation
239
340
 
240
341
  ```ruby
241
- ## type
342
+ ## type (class)
242
343
  cmdopt.add(:indent , '-i <N>', "indent width", type: Integer)
243
- ## pattern
244
- cmdopt.add(:indent , '-i <N>', "indent width", pattern: /\A\d+\z/)
245
- ## enum
246
- cmdopt.add(:indent , '-i <N>', "indent width", enum: [2, 4, 8])
344
+ ## pattern (regular expression)
345
+ cmdopt.add(:indent , '-i <N>', "indent width", rexp: /\A\d+\z/)
346
+ ## enum (Array or Set)
347
+ cmdopt.add(:indent , '-i <N>', "indent width", enum: ["2", "4", "8"])
348
+ ## range (endless range such as ``1..`` available)
349
+ cmdopt.add(:indent , '-i <N>', "indent width", range: (0..8))
247
350
  ## callback
248
351
  cmdopt.add(:indent , '-i <N>', "indent width") {|val|
249
352
  val =~ /\A\d+\z/ or
250
- raise "integer expected." # raise without exception class.
353
+ raise "Integer expected." # raise without exception class.
251
354
  val.to_i # convert argument value.
252
355
  }
253
356
  ```
254
357
 
358
+ (For backward compatibilidy, keyword parameter `pattern:` is available
359
+ which is same as `rexp:`.)
255
360
 
256
- Available types
257
- ---------------
361
+ `type:` keyword argument accepts the following classes.
258
362
 
259
363
  * Integer (`/\A[-+]?\d+\z/`)
260
364
  * Float (`/\A[-+]?(\d+\.\d*\|\.\d+)z/`)
261
365
  * TrueClass (`/\A(true|on|yes|false|off|no)\z/`)
262
366
  * Date (`/\A\d\d\d\d-\d\d?-\d\d?\z/`)
263
367
 
368
+ Notice that Ruby doesn't have Boolean class.
369
+ Benry-CmdOpt uses TrueClass instead.
370
+
371
+ In addition:
372
+
373
+ * Values of `enum:` or `range:` should match to type class specified by `type:`.
374
+ * When `type:` is not specified, then String class will be used instead.
375
+
376
+ ```ruby
377
+ ## ok
378
+ cmdopt.add(:lang, '-l <lang>', "language", enum: ["en", "fr", "it"])
379
+
380
+ ## error: enum values are not Integer
381
+ cmdopt.add(:lang, '-l <lang>', "language", enum: ["en", "fr", "it"], type: Integer)
382
+
383
+ ## ok
384
+ cmdopt.add(:indent, '-i <N>', "indent", range: (0..), type: Integer)
385
+
386
+ ## error: beginning value of range is not a String
387
+ cmdopt.add(:indent, '-i <N>', "indent", range: (0..))
388
+ ```
389
+
390
+
391
+ ### Boolean (on/off) Option
392
+
393
+ Benry-CmdOpt doens't support `--no-xxx` style option for usability reason.
394
+ Use boolean option instead.
395
+
396
+ ex3.rb:
397
+
398
+ ```ruby
399
+ require 'benry/cmdopt'
400
+ cmdopt = Benry::CmdOpt.new()
401
+ cmdopt.add(:foo, "--foo[=on|off]", "foo feature", type: TrueClass) # !!!!
402
+ ## or:
403
+ #cmdopt.add(:foo, "--foo=<on|off>", "foo feature", type: TrueClass)
404
+ options = cmdopt.parse(ARGV)
405
+ p options
406
+ ```
407
+
408
+ Output example:
409
+
410
+ ```terminal
411
+ $ ruby ex3.rb --foo # enable
412
+ {:foo=>true}
413
+ $ ruby ex3.rb --foo=on # enable
414
+ {:foo=>true}
415
+ $ ruby ex3.rb --foo=off # disable
416
+ {:foo=>false}
417
+ ```
418
+
419
+
420
+ ### Alternative Value
421
+
422
+ Benry-CmdOpt supports alternative value.
264
423
 
265
- Multiple parameters
266
- -------------------
424
+ ```ruby
425
+ require 'benry/cmdopt'
426
+ cmdopt = Benry::CmdOpt.new
427
+ cmdopt.add(:help1, "-h", "help")
428
+ cmdopt.add(:help2, "-H", "help", value: "HELP") # !!!!!
429
+
430
+ options = cmdopt.parse(["-h", "-H"])
431
+ p options[:help1] #=> true # normal
432
+ p options[:help2] #=> "HELP" # alternative value
433
+ ```
434
+
435
+ This is useful for boolean option.
267
436
 
268
437
  ```ruby
269
- cmdopt.add(:lib , '-I <NAME>', "library name") {|optdict, key, val|
270
- arr = optdict[key] || []
438
+ require 'benry/cmdopt'
439
+ cmdopt = Benry::CmdOpt.new
440
+ cmdopt.add(:flag1, "--flag1[=<on|off>]", "f1", type: TrueClass)
441
+ cmdopt.add(:flag2, "--flag2[=<on|off>]", "f2", type: TrueClass, value: false) # !!!!
442
+
443
+ ## when `--flag2` specified, got `false` value.
444
+ options = cmdopt.parse(["--flag1", "--flag2"])
445
+ p options[:flag1] #=> true
446
+ p options[:flag2] #=> false (!!!!!)
447
+ ```
448
+
449
+
450
+ ### Multiple Value Option
451
+
452
+ ```ruby
453
+ require 'benry/cmdopt'
454
+ cmdopt = Benry::CmdOpt.new
455
+
456
+ cmdopt.add(:lib , '-I <NAME>', "library name") {|options, key, val|
457
+ arr = options[key] || []
271
458
  arr << val
272
459
  arr
460
+ ## or:
461
+ #(options[key] || []) << val
273
462
  }
463
+
464
+ options = cmdopt.parse(["-I", "foo", "-I", "bar", "-Ibaz"])
465
+ p options #=> {:lib=>["foo", "bar", "baz"]}
466
+ ```
467
+
468
+
469
+ ### Hidden Option
470
+
471
+ Benry-CmdOpt regards the following options as hidden.
472
+
473
+ * Key name starts with `_` (for example `:_debug`).
474
+ * Or description is nil.
475
+
476
+ The former is better than the latter, because even hidden option should have its own description.
477
+
478
+ These hidden options are not included in help message.
479
+
480
+ ```ruby
481
+ require 'benry/cmdopt'
482
+ cmdopt = Benry::CmdOpt.new
483
+ cmdopt.add(:help , '-h', "help message")
484
+ cmdopt.add(:debug, '-D', nil) # hidden (because description is nil)
485
+ cmdopt.add(:_log , '-L', "logging") # hidden (because key starts with '_')
486
+ puts cmdopt.to_s()
487
+
488
+ ### output (neither '-D' nor '-L' is shown because hidden options)
489
+ # -h : help message
490
+ ```
491
+
492
+ To show all options including hidden ones, add `all: true` to `cmdopt.to_s()`.
493
+
494
+ ```ruby
495
+ ...(snip)...
496
+ puts cmdopt.to_s(all: true) # or: cmdopt.to_s(nil, all: true)
497
+
498
+ ### output
499
+ # -h : help message
500
+ # -D :
501
+ # -L : logging
502
+ ```
503
+
504
+
505
+ ### Global Options with Sub-Commands
506
+
507
+ `parse()` accepts boolean keyword argument `all`.
508
+
509
+ * `parse(argv, all: true)` parses even options placed after arguments. This is the default.
510
+ * `parse(argv, all: false)` only parses options placed before arguments.
511
+
512
+ ```ruby
513
+ require 'benry/cmdopt'
514
+ cmdopt = Benry::CmdOpt.new()
515
+ cmdopt.add(:help , '--help' , "print help message")
516
+ cmdopt.add(:version, '--version', "print version")
517
+
518
+ ## `parse(argv, all: true)` (default)
519
+ argv = ["--help", "arg1", "--version", "arg2"]
520
+ options = cmdopt.parse(argv, all: true) # !!!
521
+ p options #=> {:help=>true, :version=>true}
522
+ p argv #=> ["arg1", "arg2"]
523
+
524
+ ## `parse(argv, all: false)`
525
+ argv = ["--help", "arg1", "--version", "arg2"]
526
+ options = cmdopt.parse(argv, all: false) # !!!
527
+ p options #=> {:help=>true}
528
+ p argv #=> ["arg1", "--version", "arg2"]
529
+ ```
530
+
531
+ This is useful when parsing global options of sub-commands, like Git command.
532
+
533
+ ```ruby
534
+ require 'benry/cmdopt'
535
+
536
+ argv = ["-h", "commit", "xxx", "-m", "yyy"]
537
+
538
+ ## parse global options
539
+ cmdopt = Benry::CmdOpt.new()
540
+ cmdopt.add(:help, '-h', "print help message")
541
+ global_opts = cmdopt.parse(argv, all: false) # !!!false!!!
542
+ p global_opts #=> {:help=>true}
543
+ p argv #=> ["commit", "xxx", "-m", "yyy"]
544
+
545
+ ## get sub-command
546
+ sub_command = argv.shift()
547
+ p sub_command #=> "commit"
548
+ p argv #=> ["xxx", "-m", "yyy"]
549
+
550
+ ## parse sub-command options
551
+ cmdopt = Benry::CmdOpt.new()
552
+ case sub_command
553
+ when "commit"
554
+ cmdopt.add(:message, '-m <message>', "commit message")
555
+ else
556
+ # ...
557
+ end
558
+ sub_opts = cmdopt.parse(argv, all: true) # !!!true!!!
559
+ p sub_opts #=> {:message => "yyy"}
560
+ p argv #=> ["xxx"]
274
561
  ```
275
562
 
276
563
 
277
- Hidden option
278
- -------------
564
+ ### Detailed Description of Option
279
565
 
280
- If help string of command otpion is nil, it will not included
281
- in help message.
566
+ `#add()` method in `Benry::CmdOpt` or `Benry::CmdOpt::Schema` supports `detail:` keyword argument which takes detailed description of option.
282
567
 
283
568
  ```ruby
284
569
  require 'benry/cmdopt'
285
- cmdopt = Benry::Cmdopt.new
286
- cmdopt.add(:verbose, '-v, --verbose', "verbose mode")
287
- cmdopt.add(:debug , '-d[<LEVEL>]' , nil, type: Integer) # hidden
288
- puts cmdopt.option_help()
289
- ### output ('-d' is not included)
290
- # -v, --verbose : verbose mode
570
+
571
+ cmdopt = Benry::CmdOpt.new()
572
+ cmdopt.add(:mode, "-m, --mode=<MODE>", "output mode", detail: <<"END")
573
+ v, verbose: print many output
574
+ q, quiet: print litte output
575
+ c, compact: print summary output
576
+ END
577
+ puts cmdopt.to_s()
578
+ ## or:
579
+ #cmdopt.each_option_and_desc do |optstr, desc, detail|
580
+ # puts " %-20s : %s\n" % [optstr, desc]
581
+ # puts detail.gsub(/^/, ' ' * 25) if detail
582
+ #end
291
583
  ```
292
584
 
585
+ Output:
586
+
587
+ ```
588
+ -m, --mode=<MODE> : output mode
589
+ v, verbose: print many output
590
+ q, quiet: print litte output
591
+ c, compact: print summary output
592
+ ```
293
593
 
294
- Not supported
295
- -------------
594
+
595
+ ### Option Tag
596
+
597
+ `#add()` method in `Benry::CmdOpt` or `Benry::CmdOpt::Schema` supports `tag:` keyword argument.
598
+ You can use it for any purpose.
599
+
600
+ ```ruby
601
+ require 'benry/cmdopt'
602
+
603
+ cmdopt = Benry::CmdOpt.new()
604
+ cmdopt.add(:help, "-h, --help", "help message", tag: "important") # !!!
605
+ cmdopt.add(:version, "--version", "print version", tag: nil)
606
+ cmdopt.schema.each do |item|
607
+ puts "#{item.key}: tag=#{item.tag.inspect}"
608
+ end
609
+
610
+ ## output:
611
+ #help: tag="important"
612
+ #version: tag=nil
613
+ ```
614
+
615
+
616
+ ### Not Supported
296
617
 
297
618
  * default value
298
619
  * `--no-xxx` style option
299
620
  * bash/zsh completion
621
+ * I18N of error message (may be supported in the future)
622
+
300
623
 
301
624
 
302
- Internal classes
303
- ================
625
+ ## Internal Classes
304
626
 
305
- * `Benry::Cmdopt::Schema` -- command option schema.
306
- * `Benry::Cmdopt::Parser` -- command option parser.
307
- * `Benry::Cmdopt::Facade` -- facade object including schema and parser.
627
+ * `Benry::CmdOpt::Schema` ... command option schema.
628
+ * `Benry::CmdOpt::Parser` ... command option parser.
629
+ * `Benry::CmdOpt::Facade` ... facade object including schema and parser.
308
630
 
309
631
  ```ruby
310
632
  require 'benry/cmdopt'
311
633
 
312
634
  ## define schema
313
- schema = Benry::Cmdopt::Schema.new
635
+ schema = Benry::CmdOpt::Schema.new
314
636
  schema.add(:help , '-h, --help' , "show help message")
315
637
  schema.add(:file , '-f, --file=<FILE>' , "filename")
316
638
  schema.add(:indent, '-i, --indent[=<WIDTH>]', "enable indent", type: Integer)
317
639
 
318
640
  ## parse options
319
- parser = Benry::Cmdopt::Parser.new(schema)
641
+ parser = Benry::CmdOpt::Parser.new(schema)
320
642
  argv = ['-hi2', '--file=blabla.txt', 'aaa', 'bbb']
321
643
  opts = parser.parse(argv) do |err|
322
- $stderr.puts "ERROR: #{err.message}"
323
- exit 1
644
+ abort "ERROR: #{err.message}"
324
645
  end
325
646
  p opts #=> {:help=>true, :indent=>2, :file=>"blabla.txt"}
326
647
  p argv #=> ["aaa", "bbb"]
327
648
  ```
328
649
 
329
- Notice that `Benry::Cmdopt.new()` returns facade object.
650
+ Notice that `Benry::CmdOpt.new()` returns a facade object.
330
651
 
331
652
  ```ruby
332
653
  require 'benry/cmdopt'
333
654
 
334
- cmdopt = Benry::Cmdopt.new() # new facade object
655
+ cmdopt = Benry::CmdOpt.new() # new facade object
335
656
  cmdopt.add(:help, '-h', "help message") # same as schema.add(...)
336
657
  opts = cmdopt.parse(ARGV) # same as parser.parse(...)
337
658
  ```
338
659
 
660
+ Notice that `cmdopt.is_a?(Benry::CmdOpt)` results in false.
661
+ Use `cmdopt.is_a?(Benry::CmdOpt::Facade)` instead if necessary.
662
+
663
+
339
664
 
340
- License and Copyright
341
- =====================
665
+ ## License and Copyright
342
666
 
343
667
  $License: MIT License $
344
668
 
345
- $Copyright: copyright(c) 2021 kuwata-lab.com all rights reserved $
669
+ $Copyright: copyright(c) 2021 kwatch@gmail.com $