benry-cmdopt 1.1.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,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 $