benry-cmdopt 1.1.0 → 2.0.0

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.0 $)
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
+ ## Overview
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 -->
27
26
 
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.
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)
33
44
 
34
- On the other hand, `benry/cmdopt.rb` doesn't behave this way.
35
- `-x` option is available only when `-x` is defined.
45
+ <!-- /TOC -->
46
+
47
+
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,30 +210,77 @@ 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
+ $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
+
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
 
@@ -207,116 +297,349 @@ if options[:help]
207
297
  puts "Usage: foobar [<options>] [<args>...]"
208
298
  puts ""
209
299
  puts "Options:"
210
- puts cmdopt.option_help()
211
- ## 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:
212
304
  #format = " %-20s : %s"
213
- #cmdopt.each_option_help {|opt, help| puts format % [opt, help] }
305
+ #cmdopt.each_option_and_desc {|opt, help| puts format % [opt, help] }
214
306
  end
215
307
  ```
216
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
+
217
317
 
218
- Command option parameter
219
- ------------------------
318
+ ### Command Option Parameter
220
319
 
221
320
  ```ruby
222
321
  ## required parameter
223
- cmdopt.add(:file, '-f, --file=<FILE>', "filename")
224
- cmdopt.add(:file, ' --file=<FILE>', "filename")
225
- 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
226
325
 
227
326
  ## optional parameter
228
- cmdopt.add(:file, '-f, --file[=<FILE>]', "filename")
229
- cmdopt.add(:file, ' --file[=<FILE>]', "filename")
230
- 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
231
330
  ```
232
331
 
233
- Notice that `"--file <FILE>"` style is not supported.
234
- Please use `"--file=<FILE>"` style.
332
+ Notice that `"--file <FILE>"` style is **not supported for usability reason**.
333
+ Use `"--file=<FILE>"` style instead.
235
334
 
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.)
236
338
 
237
- Argument varidation
238
- -------------------
339
+
340
+ ### Argument Validation
239
341
 
240
342
  ```ruby
241
- ## type
343
+ ## type (class)
242
344
  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])
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))
247
351
  ## callback
248
352
  cmdopt.add(:indent , '-i <N>', "indent width") {|val|
249
353
  val =~ /\A\d+\z/ or
250
- raise "integer expected." # raise without exception class.
354
+ raise "Integer expected." # raise without exception class.
251
355
  val.to_i # convert argument value.
252
356
  }
253
357
  ```
254
358
 
359
+ (For backward compatibilidy, keyword parameter `pattern:` is available
360
+ which is same as `rexp:`.)
255
361
 
256
- Available types
257
- ---------------
362
+ `type:` keyword argument accepts the following classes.
258
363
 
259
364
  * Integer (`/\A[-+]?\d+\z/`)
260
365
  * Float (`/\A[-+]?(\d+\.\d*\|\.\d+)z/`)
261
366
  * TrueClass (`/\A(true|on|yes|false|off|no)\z/`)
262
367
  * Date (`/\A\d\d\d\d-\d\d?-\d\d?\z/`)
263
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.
264
396
 
265
- Multiple parameters
266
- -------------------
397
+ ex3.rb:
267
398
 
268
399
  ```ruby
269
- cmdopt.add(:lib , '-I <NAME>', "library name") {|optdict, key, val|
270
- arr = optdict[key] || []
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") # !!!!!
430
+
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.
437
+
438
+ ```ruby
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] || []
271
459
  arr << val
272
460
  arr
461
+ ## or:
462
+ #(options[key] || []) << val
273
463
  }
464
+
465
+ options = cmdopt.parse(["-I", "foo", "-I", "bar", "-Ibaz"])
466
+ p options #=> {:lib=>["foo", "bar", "baz"]}
274
467
  ```
275
468
 
276
469
 
277
- Hidden option
278
- -------------
470
+ ### Hidden Option
279
471
 
280
- If help string of command otpion is nil, it will not included
281
- in help message.
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.
282
480
 
283
481
  ```ruby
284
482
  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
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
291
491
  ```
292
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
293
566
 
294
- Not supported
295
- -------------
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
614
+ ```
615
+
616
+
617
+ ### Not Supported
296
618
 
297
619
  * default value
298
620
  * `--no-xxx` style option
299
621
  * bash/zsh completion
622
+ * I18N of error message (may be supported in the future)
300
623
 
301
624
 
302
- Internal classes
303
- ================
304
625
 
305
- * `Benry::Cmdopt::Schema` -- command option schema.
306
- * `Benry::Cmdopt::Parser` -- command option parser.
307
- * `Benry::Cmdopt::Facade` -- facade object including schema and parser.
626
+ ## Internal Classes
627
+
628
+ * `Benry::CmdOpt::Schema` ... command option schema.
629
+ * `Benry::CmdOpt::Parser` ... command option parser.
630
+ * `Benry::CmdOpt::Facade` ... facade object including schema and parser.
308
631
 
309
632
  ```ruby
310
633
  require 'benry/cmdopt'
311
634
 
312
635
  ## define schema
313
- schema = Benry::Cmdopt::Schema.new
636
+ schema = Benry::CmdOpt::Schema.new
314
637
  schema.add(:help , '-h, --help' , "show help message")
315
638
  schema.add(:file , '-f, --file=<FILE>' , "filename")
316
639
  schema.add(:indent, '-i, --indent[=<WIDTH>]', "enable indent", type: Integer)
317
640
 
318
641
  ## parse options
319
- parser = Benry::Cmdopt::Parser.new(schema)
642
+ parser = Benry::CmdOpt::Parser.new(schema)
320
643
  argv = ['-hi2', '--file=blabla.txt', 'aaa', 'bbb']
321
644
  opts = parser.parse(argv) do |err|
322
645
  $stderr.puts "ERROR: #{err.message}"
@@ -326,20 +649,23 @@ p opts #=> {:help=>true, :indent=>2, :file=>"blabla.txt"}
326
649
  p argv #=> ["aaa", "bbb"]
327
650
  ```
328
651
 
329
- Notice that `Benry::Cmdopt.new()` returns facade object.
652
+ Notice that `Benry::CmdOpt.new()` returns a facade object.
330
653
 
331
654
  ```ruby
332
655
  require 'benry/cmdopt'
333
656
 
334
- cmdopt = Benry::Cmdopt.new() # new facade object
657
+ cmdopt = Benry::CmdOpt.new() # new facade object
335
658
  cmdopt.add(:help, '-h', "help message") # same as schema.add(...)
336
659
  opts = cmdopt.parse(ARGV) # same as parser.parse(...)
337
660
  ```
338
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
+
339
666
 
340
- License and Copyright
341
- =====================
667
+ ## License and Copyright
342
668
 
343
669
  $License: MIT License $
344
670
 
345
- $Copyright: copyright(c) 2021 kuwata-lab.com all rights reserved $
671
+ $Copyright: copyright(c) 2021 kwatch@gmail.com $