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