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