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.
- checksums.yaml +4 -4
- data/CHANGES.md +29 -3
- data/MIT-LICENSE +21 -0
- data/README.md +445 -119
- data/Rakefile.rb +6 -87
- data/benry-cmdopt.gemspec +23 -21
- data/doc/benry-cmdopt.html +650 -0
- data/doc/css/style.css +160 -0
- data/lib/benry/cmdopt.rb +568 -439
- data/task/common-task.rb +138 -0
- data/task/package-task.rb +72 -0
- data/task/readme-task.rb +125 -0
- data/task/test-task.rb +81 -0
- data/test/cmdopt_test.rb +1361 -722
- metadata +22 -28
data/README.md
CHANGED
@@ -1,38 +1,52 @@
|
|
1
|
-
Benry
|
2
|
-
====================
|
1
|
+
# Benry-CmdOpt
|
3
2
|
|
4
|
-
($Release:
|
3
|
+
($Release: 2.0.0 $)
|
5
4
|
|
6
|
-
Benry::Cmdopt is a command option parser library, like `optparse.rb`.
|
7
5
|
|
8
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
26
|
-
`benry/cmdopt.rb` (v1.1.0) contains only 361 lines (without comments).
|
25
|
+
<!-- TOC -->
|
27
26
|
|
28
|
-
*
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
35
|
-
|
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`
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
67
|
-
|
68
|
-
cmdopt
|
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
|
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
|
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::
|
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 `-
|
142
|
+
terminates current process when `-h` or `--help` specified in command-line.
|
111
143
|
It is hard to remove these options.
|
112
144
|
|
113
|
-
|
114
|
-
benry/cmdopt.rb` does nothing
|
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
|
-
|
132
|
-
benry/cmdopt.rb` does nothing
|
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::
|
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.
|
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
|
-
|
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::
|
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.
|
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.
|
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
|
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(:
|
229
|
-
cmdopt.add(:
|
230
|
-
cmdopt.add(:
|
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
|
-
|
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
|
-
|
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",
|
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 "
|
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
|
-
|
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
|
-
|
266
|
-
-------------------
|
397
|
+
ex3.rb:
|
267
398
|
|
268
399
|
```ruby
|
269
|
-
|
270
|
-
|
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
|
278
|
-
-------------
|
470
|
+
### Hidden Option
|
279
471
|
|
280
|
-
|
281
|
-
|
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::
|
286
|
-
cmdopt.add(:
|
287
|
-
cmdopt.add(:debug
|
288
|
-
|
289
|
-
|
290
|
-
|
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
|
-
|
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
|
-
|
306
|
-
|
307
|
-
* `Benry::
|
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::
|
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::
|
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::
|
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::
|
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
|
671
|
+
$Copyright: copyright(c) 2021 kwatch@gmail.com $
|