benry-cmdopt 1.1.0 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
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 $