benry-cmdopt 1.1.0 → 2.0.1
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 +35 -3
- data/MIT-LICENSE +21 -0
- data/README.md +447 -123
- data/Rakefile.rb +6 -87
- data/benry-cmdopt.gemspec +23 -21
- data/doc/benry-cmdopt.html +648 -0
- data/doc/css/style.css +168 -0
- data/lib/benry/cmdopt.rb +568 -439
- data/task/common-task.rb +139 -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/lib/benry/cmdopt.rb
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
+
# frozen_string_literal: true
|
2
3
|
|
3
4
|
###
|
4
|
-
### $Release$
|
5
|
-
### $Copyright$
|
6
|
-
### $License$
|
5
|
+
### $Release: 2.0.1 $
|
6
|
+
### $Copyright: copyright(c) 2021 kwatch@gmail.com $
|
7
|
+
### $License: MIT License $
|
7
8
|
###
|
8
9
|
|
9
10
|
require 'date'
|
@@ -11,516 +12,644 @@ require 'set'
|
|
11
12
|
|
12
13
|
|
13
14
|
module Benry
|
15
|
+
end
|
14
16
|
|
15
17
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
## cmdopt.add(:help , '-h, --help' , "print help message")
|
23
|
-
## cmdopt.add(:version, ' --version', "print version")
|
24
|
-
## ## parse
|
25
|
-
## options = cmdopt.parse(ARGV) do |err|
|
26
|
-
## $stderr.puts "ERROR: #{err.message}"
|
27
|
-
## exit(1)
|
28
|
-
## end
|
29
|
-
## p options # ex: {:help => true, :version => true}
|
30
|
-
## p ARGV # options are removed from ARGV
|
31
|
-
## ## help
|
32
|
-
## if options[:help]
|
33
|
-
## puts "Usage: foobar [<options>] [<args>...]"
|
34
|
-
## puts ""
|
35
|
-
## puts "Options:"
|
36
|
-
## puts cmdopt.option_help()
|
37
|
-
## ## or
|
38
|
-
## #format = " %-20s : %s"
|
39
|
-
## #cmdopt.each_option_help {|opt, help| puts format % [opt, help] }
|
40
|
-
## end
|
41
|
-
##
|
42
|
-
## Command option parameter:
|
43
|
-
## ## required
|
44
|
-
## cmdopt.add(:file, '-f, --file=<FILE>', "filename")
|
45
|
-
## cmdopt.add(:file, ' --file=<FILE>', "filename")
|
46
|
-
## cmdopt.add(:file, '-f <FILE>' , "filename")
|
47
|
-
## ## optional
|
48
|
-
## cmdopt.add(:file, '-f, --file[=<FILE>]', "filename")
|
49
|
-
## cmdopt.add(:file, ' --file[=<FILE>]', "filename")
|
50
|
-
## cmdopt.add(:file, '-f[<FILE>]' , "filename")
|
51
|
-
##
|
52
|
-
## Validation:
|
53
|
-
## ## type
|
54
|
-
## cmdopt.add(:indent , '-i <N>', "indent width", type: Integer)
|
55
|
-
## ## pattern
|
56
|
-
## cmdopt.add(:indent , '-i <N>', "indent width", pattern: /\A\d+\z/)
|
57
|
-
## ## enum
|
58
|
-
## cmdopt.add(:indent , '-i <N>', "indent width", enum: [2, 4, 8])
|
59
|
-
## ## callback
|
60
|
-
## cmdopt.add(:indent , '-i <N>', "indent width") {|val|
|
61
|
-
## val =~ /\A\d+\z/ or
|
62
|
-
## raise "integer expected." # raise without exception class.
|
63
|
-
## val.to_i # convert argument value.
|
64
|
-
## }
|
65
|
-
##
|
66
|
-
## Available types:
|
67
|
-
## * Integer (`/\A[-+]?\d+\z/`)
|
68
|
-
## * Float (`/\A[-+]?(\d+\.\d*\|\.\d+)z/`)
|
69
|
-
## * TrueClass (`/\A(true|on|yes|false|off|no)\z/`)
|
70
|
-
## * Date (`/\A\d\d\d\d-\d\d?-\d\d?\z/`)
|
71
|
-
##
|
72
|
-
## Multiple parameters:
|
73
|
-
## cmdopt.add(:lib , '-I <NAME>', "library name") {|optdict, key, val|
|
74
|
-
## arr = optdict[key] || []
|
75
|
-
## arr << val
|
76
|
-
## arr
|
77
|
-
## }
|
78
|
-
##
|
79
|
-
## Hidden option:
|
80
|
-
## ### if help string is nil, that option is removed from help message.
|
81
|
-
## require 'benry/cmdopt'
|
82
|
-
## cmdopt = Benry::Cmdopt.new
|
83
|
-
## cmdopt.add(:verbose, '-v, --verbose', "verbose mode")
|
84
|
-
## cmdopt.add(:debug , '-d[<LEVEL>]' , nil, type: Integer) # hidden
|
85
|
-
## puts cmdopt.option_help()
|
86
|
-
## ### output ('-d' doesn't appear because help string is nil)
|
87
|
-
## # -v, --verbose : verbose mode
|
88
|
-
##
|
89
|
-
## Not supported:
|
90
|
-
## * default value
|
91
|
-
## * `--no-xxx` style option
|
92
|
-
## * bash/zsh completion
|
93
|
-
##
|
94
|
-
module Cmdopt
|
95
|
-
|
96
|
-
|
97
|
-
VERSION = '$Release: 1.1.0 $'.split()[1]
|
98
|
-
|
99
|
-
|
100
|
-
def self.new
|
101
|
-
#; [!7kkqv] creates Facade object.
|
102
|
-
return Facade.new
|
103
|
-
end
|
104
|
-
|
105
|
-
|
106
|
-
class Facade
|
107
|
-
|
108
|
-
def initialize
|
109
|
-
@schema = SCHEMA_CLASS.new
|
110
|
-
end
|
18
|
+
##
|
19
|
+
## Command option parser.
|
20
|
+
##
|
21
|
+
## See: https://github.com/kwatch/benry-ruby/tree/main/benry-cmdopt
|
22
|
+
##
|
23
|
+
module Benry::CmdOpt
|
111
24
|
|
112
|
-
def add(key, optdef, help, type: nil, pattern: nil, enum: nil, &callback)
|
113
|
-
#; [!vmb3r] defines command option.
|
114
|
-
@schema.add(key, optdef, help, type: type, pattern: pattern, enum: enum, &callback)
|
115
|
-
#; [!tu4k3] returns self.
|
116
|
-
self
|
117
|
-
end
|
118
25
|
|
119
|
-
|
120
|
-
#; [!dm4p8] returns option help message.
|
121
|
-
return @schema.option_help(width_or_format, all: all)
|
122
|
-
end
|
26
|
+
VERSION = '$Release: 2.0.1 $'.split()[1]
|
123
27
|
|
124
|
-
def each_option_help(&block)
|
125
|
-
#; [!bw9qx] yields each option definition string and help message.
|
126
|
-
@schema.each_option_help(&block)
|
127
|
-
self
|
128
|
-
end
|
129
28
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
end
|
29
|
+
def self.new()
|
30
|
+
#; [!7kkqv] creates Facade object.
|
31
|
+
return Facade.new
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
class Facade
|
138
36
|
|
37
|
+
def initialize()
|
38
|
+
@schema = SCHEMA_CLASS.new
|
139
39
|
end
|
140
40
|
|
41
|
+
attr_reader :schema
|
141
42
|
|
142
|
-
|
43
|
+
def add(key, optdef, desc, *rest, type: nil, rexp: nil, pattern: nil, enum: nil, range: nil, value: nil, detail: nil, tag: nil, &callback)
|
44
|
+
rexp ||= pattern # for backward compatibility
|
45
|
+
#; [!vmb3r] defines command option.
|
46
|
+
#; [!71cvg] type, rexp, enum, and range are can be passed as positional args as well as keyword args.
|
47
|
+
@schema.add(key, optdef, desc, *rest, type: type, rexp: rexp, enum: enum, range: range, value: value, detail: detail, tag: tag, &callback)
|
48
|
+
#; [!tu4k3] returns self.
|
49
|
+
self
|
50
|
+
end
|
143
51
|
|
144
|
-
|
145
|
-
|
146
|
-
|
52
|
+
def option_help(width_or_format=nil, all: false)
|
53
|
+
#; [!dm4p8] returns option help message.
|
54
|
+
return @schema.option_help(width_or_format, all: all)
|
55
|
+
end
|
147
56
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
@items << item
|
194
|
-
item
|
57
|
+
#; [!s61vo] '#to_s' is an alias to '#option_help()'.
|
58
|
+
alias to_s option_help
|
59
|
+
|
60
|
+
def each_option_and_desc(all: false, &block)
|
61
|
+
#; [!wght5] returns enumerator object if block not given.
|
62
|
+
return @schema.each_option_and_desc(all: all) unless block_given?()
|
63
|
+
#; [!bw9qx] yields each option definition string and help message.
|
64
|
+
#; [!kunfw] yields all items (including hidden items) if `all: true` specified.
|
65
|
+
@schema.each_option_and_desc(all: all, &block)
|
66
|
+
self
|
67
|
+
end
|
68
|
+
alias each_option_help each_option_and_desc # for backward compatibility
|
69
|
+
|
70
|
+
def parse(argv, all: true, &error_handler)
|
71
|
+
#; [!7gc2m] parses command options.
|
72
|
+
#; [!no4xu] returns option values as dict.
|
73
|
+
#; [!areof] handles only OptionError when block given.
|
74
|
+
#; [!peuva] returns nil when OptionError handled.
|
75
|
+
#; [!za9at] parses options only before args when `all: false`.
|
76
|
+
parser = PARSER_CLASS.new(@schema)
|
77
|
+
return parser.parse(argv, all: all, &error_handler)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
|
83
|
+
class Schema
|
84
|
+
|
85
|
+
def initialize()
|
86
|
+
@items = []
|
87
|
+
end
|
88
|
+
|
89
|
+
def dup()
|
90
|
+
#; [!lxb0o] copies self object.
|
91
|
+
other = self.class.new
|
92
|
+
other.instance_variable_set(:@items, @items.dup)
|
93
|
+
return other
|
94
|
+
end
|
95
|
+
|
96
|
+
def copy_from(other, except: [])
|
97
|
+
#; [!6six3] copy schema items from others.
|
98
|
+
#; [!vt88s] copy schema items except items specified by 'except:' kwarg.
|
99
|
+
except = [except].flatten()
|
100
|
+
other.each do |item|
|
101
|
+
@items << item unless except.include?(item.key)
|
195
102
|
end
|
103
|
+
self
|
104
|
+
end
|
196
105
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
case
|
202
|
-
when
|
203
|
-
when
|
204
|
-
when
|
106
|
+
def add(key, optdef, desc, *rest, type: nil, rexp: nil, pattern: nil, enum: nil, range: nil, value: nil, detail: nil, tag: nil, &callback)
|
107
|
+
rexp ||= pattern # for backward compatibility
|
108
|
+
#; [!kuhf9] type, rexp, enum, and range are can be passed as positional args as well as keyword args.
|
109
|
+
rest.each do |x|
|
110
|
+
case x
|
111
|
+
when Class ; type ||= x
|
112
|
+
when Regexp ; rexp ||= x
|
113
|
+
when Array, Set ; enum ||= x
|
114
|
+
when Range ; range ||= x
|
205
115
|
else
|
206
|
-
|
207
|
-
|
208
|
-
#; [!v7z4x] skips option help if help message is not specified.
|
209
|
-
#; [!to1th] includes all option help when `all` is true.
|
210
|
-
buf = []
|
211
|
-
width = nil
|
212
|
-
each_option_help do |opt, help|
|
213
|
-
#buf << format % [opt, help] << "\n" if help || all
|
214
|
-
if help
|
215
|
-
#; [!848rm] supports multi-lines help message.
|
216
|
-
n = 0
|
217
|
-
help.each_line do |line|
|
218
|
-
if (n += 1) == 1
|
219
|
-
buf << format % [opt, line.chomp] << "\n"
|
220
|
-
else
|
221
|
-
width ||= (format % ['', '']).length
|
222
|
-
buf << (' ' * width) << line.chomp << "\n"
|
223
|
-
end
|
224
|
-
end
|
225
|
-
elsif all
|
226
|
-
buf << format % [opt, ''] << "\n"
|
227
|
-
end
|
116
|
+
#; [!e3emy] raises error when positional arg is not one of class, regexp, array, nor range.
|
117
|
+
raise _error("#{x.inspect}: Expected one of class, regexp, array or range, but got #{x.class.name}.")
|
228
118
|
end
|
229
|
-
return buf.join()
|
230
119
|
end
|
120
|
+
#; [!rhhji] raises SchemaError when key is not a Symbol.
|
121
|
+
key.nil? || key.is_a?(Symbol) or
|
122
|
+
raise _error("add(#{key.inspect}, #{optdef.inspect}): The first arg should be a Symbol as an option key.")
|
123
|
+
#; [!vq6eq] raises SchemaError when help message is missing."
|
124
|
+
desc.nil? || desc.is_a?(String) or
|
125
|
+
raise _error("add(#{key.inspect}, #{optdef.inspect}): Help message required as 3rd argument.")
|
126
|
+
#; [!7hi2d] takes command option definition string.
|
127
|
+
short, long, param, required = parse_optdef(optdef)
|
128
|
+
#; [!p9924] option key is omittable only when long option specified.
|
129
|
+
#; [!jtp7z] raises SchemaError when key is nil and no long option.
|
130
|
+
key || long or
|
131
|
+
raise _error("add(#{key.inspect}, #{optdef.inspect}): Long option required when option key (1st arg) not specified.")
|
132
|
+
#; [!rpl98] when long option is 'foo-bar' then key name is ':foo_bar'.
|
133
|
+
key ||= long.gsub(/-/, '_').intern
|
134
|
+
#; [!97sn0] raises SchemaError when ',' is missing between short and long options.
|
135
|
+
if long.nil? && param =~ /\A--/
|
136
|
+
raise _error("add(#{key.inspect}, #{optdef.inspect}): Missing ',' between short option and long options.")
|
137
|
+
end
|
138
|
+
#; [!yht0v] keeps command option definitions.
|
139
|
+
item = SchemaItem.new(key, optdef, desc, short, long, param, required,
|
140
|
+
type: type, rexp: rexp, enum: enum, range: range, value: value, detail: detail, tag: tag, &callback)
|
141
|
+
@items << item
|
142
|
+
item
|
143
|
+
end
|
231
144
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
145
|
+
def option_help(width_or_format=nil, all: false)
|
146
|
+
#; [!0aq0i] can take integer as width.
|
147
|
+
#; [!pcsah] can take format string.
|
148
|
+
#; [!dndpd] detects option width automatically when nothing specified.
|
149
|
+
case width_or_format
|
150
|
+
when nil ; format = _default_format()
|
151
|
+
when Integer; format = " %-#{width_or_format}s : %s"
|
152
|
+
when String ; format = width_or_format
|
153
|
+
else
|
154
|
+
raise ArgumentError.new("#{width_or_format.inspect}: Width (integer) or format (string) expected.")
|
155
|
+
end
|
156
|
+
#; [!v7z4x] skips option help if help message is not specified.
|
157
|
+
#; [!to1th] includes all option help when `all` is true.
|
158
|
+
#; [!a4qe4] option should not be hidden if description is empty string.
|
159
|
+
sb = []
|
160
|
+
width = nil; indent = nil
|
161
|
+
each_option_and_desc(all: all) do |opt, desc, detail|
|
162
|
+
sb << format % [opt, desc || ""] << "\n"
|
163
|
+
#; [!848rm] supports multi-lines help message.
|
164
|
+
if detail
|
165
|
+
width ||= (format % ['', '']).length
|
166
|
+
indent ||= ' ' * width
|
167
|
+
sb << detail.gsub(/^/, indent)
|
168
|
+
sb << "\n" unless detail.end_with?("\n")
|
236
169
|
end
|
237
|
-
#; [!zbxyv] returns self.
|
238
|
-
self
|
239
170
|
end
|
171
|
+
return sb.join()
|
172
|
+
end
|
240
173
|
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
174
|
+
#; [!rrapd] '#to_s' is an alias to '#option_help()'.
|
175
|
+
alias to_s option_help
|
176
|
+
|
177
|
+
def each_option_and_desc(all: false, &block)
|
178
|
+
#; [!03sux] returns enumerator object if block not given.
|
179
|
+
return to_enum(:each_option_and_desc, all: all) unless block_given?()
|
180
|
+
#; [!4b911] yields each optin definition str and help message.
|
181
|
+
@items.each do |item|
|
182
|
+
#; [!cl8zy] when 'all' flag is false, not yield hidden items.
|
183
|
+
#; [!tc4bk] when 'all' flag is true, yields even hidden items.
|
184
|
+
yield item.optdef, item.desc, item.detail if all || ! item.hidden?
|
245
185
|
end
|
186
|
+
#; [!zbxyv] returns self.
|
187
|
+
self
|
188
|
+
end
|
189
|
+
alias each_option_help each_option_and_desc # for backward compatibility
|
246
190
|
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
end
|
191
|
+
def each(&block) # :nodoc:
|
192
|
+
#; [!y4k1c] yields each option item.
|
193
|
+
@items.each(&block)
|
194
|
+
end
|
252
195
|
|
253
|
-
|
196
|
+
def empty?(all: true)
|
197
|
+
#; [!um8am] returns false if any item exists, else returns true.
|
198
|
+
#; [!icvm1] ignores hidden items if 'all: false' kwarg specified.
|
199
|
+
@items.each {|item| return false if all || ! item.hidden? }
|
200
|
+
return true
|
201
|
+
end
|
254
202
|
|
255
|
-
|
256
|
-
|
257
|
-
|
203
|
+
def get(key)
|
204
|
+
#; [!3wjfp] finds option item object by key.
|
205
|
+
#; [!0spll] returns nil if key not found.
|
206
|
+
return @items.find {|item| item.key == key }
|
207
|
+
end
|
258
208
|
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
short, long, param1, param2 = $1, $2, $3, $4
|
267
|
-
when /\A[ \t]*-(\w)(?:[ \t]+(\S+)|\[(\S+)\])?\z/
|
268
|
-
short, long, param1, param2 = $1, nil, $2, $3
|
269
|
-
when /\A[ \t]*--(\w[-\w]*)(?:=(\S*?)|\[=(\S*?)\])?\z/
|
270
|
-
short, long, param1, param2 = nil, $1, $2, $3
|
271
|
-
when /(--\w[-\w])*[ \t]+(\S+)/
|
272
|
-
raise error("#{optdef}: invalid option definition (use '#{$1}=#{$2}' instead of '#{$1} #{$2}').")
|
273
|
-
else
|
274
|
-
raise error("#{optdef}: invalid option definition.")
|
275
|
-
end
|
276
|
-
return short, long, param1 || param2, !!param2
|
277
|
-
end
|
209
|
+
def delete(key)
|
210
|
+
#; [!l86rb] deletes option item corresponding to key.
|
211
|
+
#; [!rq0aa] returns deleted item.
|
212
|
+
item = get(key)
|
213
|
+
@items.delete_if {|item| item.key == key }
|
214
|
+
return item
|
215
|
+
end
|
278
216
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
217
|
+
def find_short_option(short)
|
218
|
+
#; [!b4js1] returns option definition matched to short name.
|
219
|
+
#; [!s4d1y] returns nil when nothing found.
|
220
|
+
return @items.find {|item| item.short == short }
|
221
|
+
end
|
222
|
+
|
223
|
+
def find_long_option(long)
|
224
|
+
#; [!atmf9] returns option definition matched to long name.
|
225
|
+
#; [!6haoo] returns nil when nothing found.
|
226
|
+
return @items.find {|item| item.long == long }
|
227
|
+
end
|
228
|
+
|
229
|
+
private
|
230
|
+
|
231
|
+
def _error(msg)
|
232
|
+
return SchemaError.new(msg)
|
233
|
+
end
|
234
|
+
|
235
|
+
def parse_optdef(optdef)
|
236
|
+
#; [!qw0ac] parses command option definition string.
|
237
|
+
#; [!ae733] parses command option definition which has a required param.
|
238
|
+
#; [!4h05c] parses command option definition which has an optional param.
|
239
|
+
#; [!b7jo3] raises SchemaError when command option definition is invalid.
|
240
|
+
case optdef
|
241
|
+
when /\A[ \t]*-(\w),[ \t]*--(\w[-\w]*)(?:=(\S*?)|\[=(\S*?)\])?\z/
|
242
|
+
short, long, param1, param2 = $1, $2, $3, $4
|
243
|
+
when /\A[ \t]*-(\w)(?:[ \t]+(\S+)|\[(\S+)\])?\z/
|
244
|
+
short, long, param1, param2 = $1, nil, $2, $3
|
245
|
+
when /\A[ \t]*--(\w[-\w]*)(?:=(\S*?)|\[=(\S*?)\])?\z/
|
246
|
+
short, long, param1, param2 = nil, $1, $2, $3
|
247
|
+
when /(--\w[-\w])*[ \t]+(\S+)/
|
248
|
+
raise _error("#{optdef}: Invalid option definition (use '#{$1}=#{$2}' instead of '#{$1} #{$2}').")
|
249
|
+
else
|
250
|
+
raise _error("#{optdef}: Invalid option definition.")
|
291
251
|
end
|
252
|
+
required = param1 ? true : param2 ? false : nil
|
253
|
+
return short, long, (param1 || param2), required
|
254
|
+
end
|
292
255
|
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
256
|
+
def _default_format(min_width=nil, max_width=35)
|
257
|
+
#; [!bmr7d] changes min_with according to options.
|
258
|
+
min_width ||= _preferred_option_width()
|
259
|
+
#; [!hr45y] detects preffered option width.
|
260
|
+
w = 0
|
261
|
+
each_option_help do |opt, _|
|
262
|
+
w = opt.length if w < opt.length
|
300
263
|
end
|
264
|
+
w = min_width if w < min_width
|
265
|
+
w = max_width if w > max_width
|
266
|
+
#; [!kkh9t] returns format string.
|
267
|
+
return " %-#{w}s : %s"
|
268
|
+
end
|
301
269
|
|
270
|
+
def _preferred_option_width()
|
271
|
+
#; [!kl91t] shorten option help min width when only single options which take no arg.
|
272
|
+
#; [!0koqb] widen option help min width when any option takes an arg.
|
273
|
+
#; [!kl91t] widen option help min width when long option exists.
|
274
|
+
long_p = @items.any? {|x| x.desc && x.long && x.param }
|
275
|
+
short_p = @items.all? {|x| x.desc && !x.long && !x.param }
|
276
|
+
return short_p ? 8 : long_p ? 20 : 14
|
302
277
|
end
|
303
278
|
|
279
|
+
end
|
304
280
|
|
305
|
-
class SchemaItem # avoid Struct
|
306
281
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
282
|
+
class SchemaItem # avoid Struct
|
283
|
+
|
284
|
+
def initialize(key, optdef, desc, short, long, param, required, type: nil, rexp: nil, pattern: nil, enum: nil, range: nil, detail: nil, value: nil, tag: nil, &callback)
|
285
|
+
rexp ||= pattern # for backward compatibility
|
286
|
+
_init_validation(param, required, type, rexp, enum, range, value)
|
287
|
+
@key = key unless nil == key
|
288
|
+
@optdef = optdef unless nil == optdef
|
289
|
+
@desc = desc unless nil == desc
|
290
|
+
@short = short unless nil == short
|
291
|
+
@long = long unless nil == long
|
292
|
+
@param = param unless nil == param
|
293
|
+
@required = required unless nil == required
|
294
|
+
@type = type unless nil == type
|
295
|
+
@rexp = rexp unless nil == rexp
|
296
|
+
@enum = enum unless nil == enum
|
297
|
+
@range = range unless nil == range
|
298
|
+
@detail = detail unless nil == detail
|
299
|
+
@value = value unless nil == value
|
300
|
+
@tag = tag unless nil == tag
|
301
|
+
@callback = callback unless nil == callback
|
302
|
+
#; [!nn4cp] freezes enum object.
|
303
|
+
@enum.freeze() if @enum
|
304
|
+
end
|
320
305
|
|
321
|
-
|
322
|
-
|
306
|
+
attr_reader :key, :optdef, :desc, :short, :long, :param, :type, :rexp, :enum, :range, :detail, :value, :tag, :callback
|
307
|
+
alias pattern rexp # for backward compatibility
|
308
|
+
alias help desc # for backward compatibility
|
323
309
|
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
#; [!j4fuz] calls type-specific callback when type specified.
|
331
|
-
if @type && val != true
|
332
|
-
proc_ = PARAM_TYPES[@type]
|
333
|
-
val = proc_.call(val)
|
334
|
-
end
|
335
|
-
#; [!5jrdf] raises RuntimeError when value not in enum.
|
336
|
-
if @enum && val != true
|
337
|
-
@enum.include?(val) or
|
338
|
-
raise "expected one of #{@enum.join('/')}."
|
339
|
-
end
|
340
|
-
#; [!jn9z3] calls callback when callback specified.
|
341
|
-
#; [!iqalh] calls callback with different number of args according to arity.
|
342
|
-
if @callback
|
343
|
-
n_args = @callback.arity
|
344
|
-
val = n_args == 1 ? @callback.call(val) \
|
345
|
-
: @callback.call(optdict, @key, val)
|
346
|
-
end
|
347
|
-
#; [!x066l] returns new value.
|
348
|
-
return val
|
349
|
-
end
|
310
|
+
def required?()
|
311
|
+
#; [!svxny] returns nil if option takes no arguments.
|
312
|
+
#; [!uwbgc] returns false if argument is optional.
|
313
|
+
#; [!togcx] returns true if argument is required.
|
314
|
+
return ! @param ? nil : !! @required
|
315
|
+
end
|
350
316
|
|
317
|
+
def arg_requireness()
|
318
|
+
#; [!kmo28] returns :none if option takes no arguments.
|
319
|
+
#; [!owpba] returns :optional if argument is optional.
|
320
|
+
#; [!s8gxl] returns :required if argument is required.
|
321
|
+
return :none if ! @param
|
322
|
+
return :required if @required
|
323
|
+
return :optional
|
351
324
|
end
|
352
325
|
|
326
|
+
def hidden?()
|
327
|
+
#; [!h0uxs] returns true if desc is nil.
|
328
|
+
#; [!su00g] returns true if key starts with '_'.
|
329
|
+
#; [!28vzx] returns false if else.
|
330
|
+
return @desc == nil || @key.to_s.start_with?('_')
|
331
|
+
end
|
353
332
|
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
val.
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
333
|
+
def validate_and_convert(val, optdict)
|
334
|
+
#; [!h0s0o] raises RuntimeError when value not matched to pattern.
|
335
|
+
if @rexp && val != true
|
336
|
+
val =~ @rexp or
|
337
|
+
raise "Pattern unmatched."
|
338
|
+
end
|
339
|
+
#; [!j4fuz] calls type-specific callback when type specified.
|
340
|
+
if @type && val != true
|
341
|
+
proc_ = PARAM_TYPES[@type]
|
342
|
+
val = proc_.call(val)
|
343
|
+
end
|
344
|
+
#; [!5jrdf] raises RuntimeError when value not in enum.
|
345
|
+
if @enum && val != true
|
346
|
+
@enum.include?(val) or
|
347
|
+
raise "Expected one of #{@enum.join('/')}."
|
348
|
+
end
|
349
|
+
#; [!5falp] raise RuntimeError when value not in range.
|
350
|
+
#; [!a0rej] supports endless range.
|
351
|
+
if @range && val != true
|
352
|
+
r = @range
|
353
|
+
r.begin == nil || r.begin <= val or (
|
354
|
+
raise "Positive value (>= 0) expected." if r.begin == 0
|
355
|
+
raise "Positive value (>= 1) expected." if r.begin == 1
|
356
|
+
raise "Too small (min: #{r.begin.inspect})"
|
357
|
+
)
|
358
|
+
r.end == nil || val <= r.end or
|
359
|
+
raise "Too large (max: #{r.end.inspect})"
|
360
|
+
end
|
361
|
+
#; [!jn9z3] calls callback when callback specified.
|
362
|
+
#; [!iqalh] calls callback with different number of args according to arity.
|
363
|
+
if @callback
|
364
|
+
n_args = @callback.arity
|
365
|
+
val = n_args == 1 ? @callback.call(val) \
|
366
|
+
: @callback.call(optdict, @key, val)
|
367
|
+
end
|
368
|
+
#; [!eafem] returns default value (if specified) instead of true value.
|
369
|
+
return @value if val == true && @value != nil
|
370
|
+
#; [!x066l] returns new value.
|
371
|
+
return val
|
372
|
+
end
|
373
|
+
|
374
|
+
private
|
375
|
+
|
376
|
+
def _error(msg)
|
377
|
+
return SchemaError.new(msg)
|
378
|
+
end
|
379
|
+
|
380
|
+
def _init_validation(param, required, type, rexp, enum, range, value)
|
381
|
+
#; [!wy2iv] when 'type:' specified...
|
382
|
+
if type
|
383
|
+
#; [!7xmr5] raises SchemaError when type is not registered.
|
384
|
+
PARAM_TYPES.key?(type) or
|
385
|
+
raise _error("#{type.inspect}: Unregistered type.")
|
386
|
+
#; [!s2aaj] raises SchemaError when option has no params but type specified.
|
387
|
+
#; [!sz8x2] not raise error when no params but value specified.
|
388
|
+
#; [!70ogf] not raise error when no params but TrueClass specified.
|
389
|
+
param || value != nil || type == TrueClass or
|
390
|
+
raise _error("#{type.inspect}: Type specified in spite of option has no params.")
|
391
|
+
end
|
392
|
+
#; [!6y8s2] when 'rexp:' specified...
|
393
|
+
if rexp
|
394
|
+
#; [!bi2fh] raises SchemaError when pattern is not a regexp.
|
395
|
+
rexp.is_a?(Regexp) or
|
396
|
+
raise _error("#{rexp.inspect}: Regexp pattern expected.")
|
397
|
+
#; [!01fmt] raises SchmeaError when option has no params but pattern specified.
|
398
|
+
param or
|
399
|
+
raise _error("#{rexp.inspect}: Regexp pattern specified in spite of option has no params.")
|
400
|
+
end
|
401
|
+
#; [!5nrvq] when 'enum:' specified...
|
402
|
+
if enum
|
403
|
+
#; [!melyd] raises SchemaError when enum is not an Array nor Set.
|
404
|
+
enum.is_a?(Array) || enum.is_a?(Set) or
|
405
|
+
raise _error("#{enum.inspect}: Array or set expected.")
|
406
|
+
#; [!xqed8] raises SchemaError when enum specified for no param option.
|
407
|
+
param or
|
408
|
+
raise _error("#{enum.inspect}: Enum specified in spite of option has no params.")
|
409
|
+
#; [!zuthh] raises SchemaError when enum element value is not instance of type class.
|
410
|
+
enum.each do |x|
|
411
|
+
x.is_a?(type) or
|
412
|
+
raise _error("#{enum.inspect}: Enum element value should be instance of #{type.name}, but #{x.inspect} is not.")
|
413
|
+
end if type
|
414
|
+
end
|
415
|
+
#; [!hk4nw] when 'range:' specified...
|
416
|
+
if range
|
417
|
+
#; [!z20ky] raises SchemaError when range is not a Range object.
|
418
|
+
range.is_a?(Range) or
|
419
|
+
raise _error("#{range.inspect}: Range object expected.")
|
420
|
+
#; [!gp025] raises SchemaError when range specified with `type: TrueClass`.
|
421
|
+
if type == TrueClass
|
422
|
+
raise _error("#{range.inspect}: Range is not available with `type: TrueClass`.")
|
423
|
+
#; [!7njd5] range beginning/end value should be expected type.
|
381
424
|
else
|
382
|
-
|
425
|
+
#; [!uymig] range object can be endless.
|
426
|
+
type_ = type || String
|
427
|
+
ok1 = range.begin == nil || range.begin.is_a?(type_)
|
428
|
+
ok2 = range.end == nil || range.end.is_a?(type_)
|
429
|
+
ok1 && ok2 or
|
430
|
+
raise _error("#{range.inspect}: Range value should be #{type_.name}, but not.")
|
383
431
|
end
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
#; [!
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
432
|
+
end
|
433
|
+
#; [!a0g52] when 'value:' specified...
|
434
|
+
if value != nil
|
435
|
+
#; [!435t6] raises SchemaError when 'value:' is specified on argument-required option.
|
436
|
+
! required or
|
437
|
+
raise _error("#{value.inspect}: 'value:' is meaningless when option has required argument (hint: change to optional argument instead).")
|
438
|
+
if type == TrueClass
|
439
|
+
#; [!6vwqv] raises SchemaError when type is TrueClass but value is not true nor false.
|
440
|
+
value == true || value == false or
|
441
|
+
raise _error("#{value.inspect}: Value should be true or false when `type: TrueClass` specified.")
|
442
|
+
elsif type
|
443
|
+
#; [!c6i2o] raises SchemaError when value is not a kind of type.
|
444
|
+
value.is_a?(type) or
|
445
|
+
raise _error("Type mismatched between `type: #{type.name}` and `value: #{value.inspect}`.")
|
446
|
+
else
|
447
|
+
#; [!lnhp6] not raise error when type is not specified.
|
448
|
+
end
|
449
|
+
if enum
|
450
|
+
#; [!6xb8o] value should be included in enum values.
|
451
|
+
enum.include?(value) or
|
452
|
+
raise _error("#{value}: Value should be included in enum values, but not.")
|
395
453
|
end
|
396
|
-
|
397
|
-
|
454
|
+
end
|
455
|
+
end
|
398
456
|
|
457
|
+
end
|
399
458
|
|
400
|
-
class Parser
|
401
459
|
|
402
|
-
|
403
|
-
|
460
|
+
PARAM_TYPES = {
|
461
|
+
String => proc {|val|
|
462
|
+
val
|
463
|
+
},
|
464
|
+
Integer => proc {|val|
|
465
|
+
#; [!6t8cs] converts value into integer.
|
466
|
+
#; [!nzwc9] raises error when failed to convert value into integer.
|
467
|
+
val =~ /\A[-+]?\d+\z/ or
|
468
|
+
raise "Integer expected."
|
469
|
+
val.to_i
|
470
|
+
},
|
471
|
+
Float => proc {|val|
|
472
|
+
#; [!gggy6] converts value into float.
|
473
|
+
#; [!t4elj] raises error when faield to convert value into float.
|
474
|
+
val =~ /\A[-+]?(\d+\.\d*|\.\d+)\z/ or
|
475
|
+
raise "Float expected."
|
476
|
+
val.to_f
|
477
|
+
},
|
478
|
+
TrueClass => proc {|val|
|
479
|
+
#; [!47kx4] converts 'true'/'on'/'yes' into true.
|
480
|
+
#; [!3n810] converts 'false'/'off'/'no' into false.
|
481
|
+
#; [!h8ayh] raises error when failed to convert value into true nor false.
|
482
|
+
case val
|
483
|
+
when /\A(?:true|on|yes)\z/i ; true
|
484
|
+
when /\A(?:false|off|no)\z/i ; false
|
485
|
+
else
|
486
|
+
raise "Boolean expected."
|
487
|
+
end
|
488
|
+
},
|
489
|
+
Date => proc {|val|
|
490
|
+
#; [!sru5j] converts 'YYYY-MM-DD' into date object.
|
491
|
+
#; [!h9q9y] raises error when failed to convert into date object.
|
492
|
+
#; [!i4ui8] raises error when specified date not exist.
|
493
|
+
val =~ /\A(\d\d\d\d)-(\d\d?)-(\d\d?)\z/ or
|
494
|
+
raise "Invalid date format (ex: '2000-01-01')"
|
495
|
+
begin
|
496
|
+
Date.new($1.to_i, $2.to_i, $3.to_i)
|
497
|
+
rescue ArgumentError => ex
|
498
|
+
raise "Date not exist."
|
404
499
|
end
|
500
|
+
},
|
501
|
+
}
|
405
502
|
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
503
|
+
|
504
|
+
class Parser
|
505
|
+
|
506
|
+
def initialize(schema)
|
507
|
+
@schema = schema
|
508
|
+
end
|
509
|
+
|
510
|
+
def parse(argv, all: true, &error_handler)
|
511
|
+
optdict = new_options_dict()
|
512
|
+
index = 0
|
513
|
+
while index < argv.length
|
514
|
+
#; [!5s5b6] treats '-' as an argument, not an option.
|
515
|
+
if argv[index] =~ /\A-/ && argv[index] != "-"
|
516
|
+
optstr = argv.delete_at(index)
|
517
|
+
#; [!q8356] parses options even after arguments when `all: true`.
|
518
|
+
elsif all
|
519
|
+
index += 1
|
520
|
+
next
|
521
|
+
#; [!ryra3] doesn't parse options after arguments when `all: false`.
|
522
|
+
else
|
523
|
+
break
|
524
|
+
end
|
525
|
+
#; [!y04um] skips rest options when '--' found in argv.
|
526
|
+
if optstr == '--'
|
527
|
+
break
|
528
|
+
elsif optstr =~ /\A--/
|
529
|
+
#; [!uh7j8] parses long options.
|
530
|
+
parse_long_option(optstr, optdict)
|
531
|
+
else
|
532
|
+
#; [!nwnjc] parses short options.
|
533
|
+
parse_short_options(optstr, optdict) { argv.delete_at(index) }
|
420
534
|
end
|
421
|
-
#; [!3wmsy] returns command option values as a dict.
|
422
|
-
return optdict
|
423
|
-
rescue OptionError => ex
|
424
|
-
#; [!qpuxh] handles only OptionError when block given.
|
425
|
-
raise unless block_given?()
|
426
|
-
yield ex
|
427
|
-
#; [!dhpw1] returns nil when OptionError handled.
|
428
|
-
nil
|
429
535
|
end
|
536
|
+
#; [!3wmsy] returns command option values as a dict.
|
537
|
+
return optdict
|
538
|
+
rescue OptionError => ex
|
539
|
+
#; [!qpuxh] handles only OptionError when block given.
|
540
|
+
raise unless block_given?()
|
541
|
+
yield ex
|
542
|
+
#; [!dhpw1] returns nil when OptionError handled.
|
543
|
+
nil
|
544
|
+
end
|
545
|
+
|
546
|
+
def _error(msg)
|
547
|
+
return OptionError.new(msg)
|
548
|
+
end
|
430
549
|
|
431
|
-
|
432
|
-
|
550
|
+
protected
|
551
|
+
|
552
|
+
def parse_long_option(optstr, optdict)
|
553
|
+
#; [!3i994] raises OptionError when invalid long option format.
|
554
|
+
optstr =~ /\A--(\w[-\w]*)(?:=(.*))?\z/ or
|
555
|
+
raise _error("#{optstr}: Invalid long option.")
|
556
|
+
name = $1; val = $2
|
557
|
+
#; [!1ab42] invokes error handler method when unknown long option.
|
558
|
+
#; [!er7h4] default behavior is to raise OptionError when unknown long option.
|
559
|
+
item = @schema.find_long_option(name) or
|
560
|
+
return handle_unknown_long_option(optstr, name, val)
|
561
|
+
#; [!2jd9w] raises OptionError when no arguments specified for arg required long option.
|
562
|
+
#; [!qyq8n] raises optionError when an argument specified for no arg long option.
|
563
|
+
case item.arg_requireness()
|
564
|
+
when :none # no arguments
|
565
|
+
val == nil or raise _error("#{optstr}: Unexpected argument.")
|
566
|
+
when :required # argument required
|
567
|
+
val or raise _error("#{optstr}: Argument required.")
|
568
|
+
when :optional # optonal argument
|
569
|
+
# do nothing
|
570
|
+
else
|
571
|
+
raise "** internal error"
|
433
572
|
end
|
573
|
+
#; [!o596x] validates argument value.
|
574
|
+
val ||= true
|
575
|
+
begin
|
576
|
+
val = item.validate_and_convert(val, optdict)
|
577
|
+
rescue RuntimeError => ex
|
578
|
+
raise _error("#{optstr}: #{ex.message}")
|
579
|
+
end
|
580
|
+
optdict[item.key] = val
|
581
|
+
end
|
434
582
|
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
val
|
583
|
+
def parse_short_options(optstr, optdict, &block)
|
584
|
+
n = optstr.length
|
585
|
+
i = 0
|
586
|
+
while (i += 1) < n
|
587
|
+
char = optstr[i]
|
588
|
+
#; [!4eh49] raises OptionError when unknown short option specified.
|
589
|
+
item = @schema.find_short_option(char) or
|
590
|
+
raise _error("-#{char}: Unknown option.")
|
591
|
+
#
|
592
|
+
case item.arg_requireness()
|
593
|
+
when :none # no arguments
|
594
|
+
val = true
|
595
|
+
when :required # argument required
|
596
|
+
#; [!utdbf] raises OptionError when argument required but not specified.
|
597
|
+
#; [!f63hf] short option arg can be specified without space separator.
|
598
|
+
val = i+1 < n ? optstr[(i+1)..-1] : yield or
|
599
|
+
raise _error("-#{char}: Argument required.")
|
600
|
+
i = n
|
601
|
+
when :optional # optonal argument
|
602
|
+
#; [!yjq6b] optional arg should be specified without space separator.
|
603
|
+
#; [!wape4] otpional arg can be omit.
|
604
|
+
val = i+1 < n ? optstr[(i+1)..-1] : true
|
605
|
+
i = n
|
451
606
|
else
|
452
|
-
|
607
|
+
raise "** internal error"
|
453
608
|
end
|
454
|
-
#; [!
|
455
|
-
val ||= true
|
609
|
+
#; [!yu0kc] validates short option argument.
|
456
610
|
begin
|
457
611
|
val = item.validate_and_convert(val, optdict)
|
458
612
|
rescue RuntimeError => ex
|
459
|
-
|
460
|
-
|
461
|
-
optdict[item.key] = val
|
462
|
-
end
|
463
|
-
|
464
|
-
def parse_short_options(optstr, optdict, argv)
|
465
|
-
n = optstr.length
|
466
|
-
i = 0
|
467
|
-
while (i += 1) < n
|
468
|
-
char = optstr[i]
|
469
|
-
#; [!4eh49] raises OptionError when unknown short option specified.
|
470
|
-
item = @schema.find_short_option(char) or
|
471
|
-
raise error("-#{char}: unknown option.")
|
472
|
-
#
|
473
|
-
if !item.param
|
474
|
-
val = true
|
475
|
-
elsif !item.optional_param?
|
476
|
-
#; [!utdbf] raises OptionError when argument required but not specified.
|
477
|
-
#; [!f63hf] short option arg can be specified without space separator.
|
478
|
-
val = i+1 < n ? optstr[(i+1)..-1] : argv.shift or
|
479
|
-
raise error("-#{char}: argument required.")
|
480
|
-
i = n
|
613
|
+
if val == true
|
614
|
+
raise _error("-#{char}: #{ex.message}")
|
481
615
|
else
|
482
|
-
|
483
|
-
|
484
|
-
val = i+1 < n ? optstr[(i+1)..-1] : true
|
485
|
-
i = n
|
616
|
+
sp = item.required? ? ' ' : ''
|
617
|
+
raise _error("-#{char}#{sp}#{val}: #{ex.message}")
|
486
618
|
end
|
487
|
-
#; [!yu0kc] validates short option argument.
|
488
|
-
begin
|
489
|
-
val = item.validate_and_convert(val, optdict)
|
490
|
-
rescue RuntimeError => ex
|
491
|
-
if val == true
|
492
|
-
raise error("-#{char}: #{ex.message}")
|
493
|
-
else
|
494
|
-
s = item.optional_param? ? '' : ' '
|
495
|
-
raise error("-#{char}#{s}#{val}: #{ex.message}")
|
496
|
-
end
|
497
|
-
end
|
498
|
-
optdict[item.key] = val
|
499
619
|
end
|
620
|
+
optdict[item.key] = val
|
500
621
|
end
|
622
|
+
end
|
501
623
|
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
end
|
506
|
-
|
624
|
+
def new_options_dict()
|
625
|
+
#; [!vm6h0] returns new hash object.
|
626
|
+
return OPTIONS_CLASS.new
|
507
627
|
end
|
508
628
|
|
629
|
+
def handle_unknown_long_option(optstr, name, val)
|
630
|
+
#; [!0q78a] raises OptionError.
|
631
|
+
raise _error("#{optstr}: Unknown long option.")
|
632
|
+
end
|
509
633
|
|
510
|
-
|
511
|
-
SCHEMA_CLASS = Schema
|
512
|
-
PARSER_CLASS = Parser
|
634
|
+
end
|
513
635
|
|
514
636
|
|
515
|
-
|
516
|
-
|
637
|
+
OPTIONS_CLASS = Hash
|
638
|
+
SCHEMA_CLASS = Schema
|
639
|
+
PARSER_CLASS = Parser
|
517
640
|
|
518
641
|
|
519
|
-
|
520
|
-
|
642
|
+
class SchemaError < StandardError
|
643
|
+
end
|
521
644
|
|
522
645
|
|
646
|
+
class OptionError < StandardError
|
523
647
|
end
|
524
648
|
|
525
649
|
|
526
650
|
end
|
651
|
+
|
652
|
+
|
653
|
+
module Benry
|
654
|
+
Cmdopt = CmdOpt # for backawrd compatibility
|
655
|
+
end
|