cmdopt 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/MIT-LICENSE +21 -0
- data/README.txt +138 -0
- data/cmdopt.gemspec +34 -0
- data/lib/cmdopt.rb +256 -0
- data/setup.rb +1585 -0
- data/test/cmdopt_test.rb +377 -0
- metadata +66 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2011 kuwata-lab.com all rights reserved
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
data/README.txt
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
================
|
2
|
+
Cmdopt.rb README
|
3
|
+
================
|
4
|
+
|
5
|
+
Release: 0.2.0
|
6
|
+
|
7
|
+
|
8
|
+
About
|
9
|
+
-----
|
10
|
+
|
11
|
+
Cmdopt.rb is a command-option parser.
|
12
|
+
It is easier to use than optparse.rb (a standard library of Ruby).
|
13
|
+
|
14
|
+
|
15
|
+
Install
|
16
|
+
-------
|
17
|
+
|
18
|
+
::
|
19
|
+
|
20
|
+
$ gem install cmdopt
|
21
|
+
|
22
|
+
|
23
|
+
Usage
|
24
|
+
-----
|
25
|
+
|
26
|
+
Example::
|
27
|
+
|
28
|
+
## create parser
|
29
|
+
require 'cmdopt'
|
30
|
+
parser = Cmdopt::Parser.new
|
31
|
+
## define options
|
32
|
+
parser.option("-h, --help", "show help")
|
33
|
+
parser.option("-f, --file=FILE", "read file")
|
34
|
+
## parse args
|
35
|
+
defaults = {:file=> "config.json"}
|
36
|
+
begin
|
37
|
+
opts = parser.parse(ARGV, defaults)
|
38
|
+
p opts.help #=> true (if '-h' or '--help' specified)
|
39
|
+
p opts.file #=> "config.json" (if not specified)
|
40
|
+
p ARGV
|
41
|
+
rescue Cmdopt::ParseError => ex
|
42
|
+
$stderr.puts ex.message
|
43
|
+
exit(1)
|
44
|
+
end
|
45
|
+
|
46
|
+
More example::
|
47
|
+
|
48
|
+
## no argument
|
49
|
+
parser.option("-h", "show help")
|
50
|
+
parser.option(" --help", "show help")
|
51
|
+
parser.option("-h, --help", "show help")
|
52
|
+
## required argument
|
53
|
+
parser.option("-f FILE", "read file")
|
54
|
+
parser.option(" --file=FILE", "read file")
|
55
|
+
parser.option("-f, --file=FILE", "read file")
|
56
|
+
## optional argument
|
57
|
+
parser.option("-i[N]", "indent width")
|
58
|
+
parser.option(" --indent[=N]", "indent width")
|
59
|
+
parser.option("-i, --indent[=N]", "indent width")
|
60
|
+
|
61
|
+
Validation::
|
62
|
+
|
63
|
+
parser.option("-m, --mode=MODE", "set mode")\
|
64
|
+
.validation {|val| "'verbose' or 'quiet' expected." unless val =~ /^(verbose|quiet)$/ }
|
65
|
+
parser.option("-i, --indent[=N]", "indent width (default 2)")\
|
66
|
+
.validation {|val| "integer required." unless val == true || val =~ /^\d+$/ }
|
67
|
+
|
68
|
+
Action::
|
69
|
+
|
70
|
+
## change default handler
|
71
|
+
parser.option("--verbose", "quiet mode")\
|
72
|
+
.action {|val, opts| opts.mode = "verbose" } # default: opts.verbose = true
|
73
|
+
parser.option("--quiet", "quiet mode")\
|
74
|
+
.action {|val, opts| opts.mode = "quiet" } # default: opts.quiet = true
|
75
|
+
|
76
|
+
## The following definitions...
|
77
|
+
parser.option("-h, --help", "show help")
|
78
|
+
parser.option("-f, --file=FILE", "read file")
|
79
|
+
## are equivarent to:
|
80
|
+
parser.option("-h, --help", "show help")\
|
81
|
+
.action {|val, opts| opts.help = val }
|
82
|
+
parser.option("-f, --file=FILE", "read file")\
|
83
|
+
.action {|val, opts| opts.file = val }
|
84
|
+
|
85
|
+
Multiple option::
|
86
|
+
|
87
|
+
## define custom handler to store values into list
|
88
|
+
parser.option("-I path #paths", "include path (multiple OK)")\
|
89
|
+
.action {|val, opts| (opts.paths ||= []) << val }
|
90
|
+
##
|
91
|
+
opts = parser.parse(["-Ipath1", "-Ipath2", "-Ipath3"])
|
92
|
+
p opts.paths #=> ["path1", "path2", "path3"]
|
93
|
+
|
94
|
+
Attribute name::
|
95
|
+
|
96
|
+
## usually, long name or sort name is used as attribute name of opts.
|
97
|
+
parser.option("-h, --help", "show help")
|
98
|
+
opts = parser.parse(["-h"])
|
99
|
+
p opts.help #=> true (attr name == long name)
|
100
|
+
parser.option("-h", "show help")
|
101
|
+
opts = parser.parse(["-h"])
|
102
|
+
p opts.h #=> true (attr name == short name)
|
103
|
+
## it is possible to specify attribute name by '#name'.
|
104
|
+
## this is very helpful when you want not to use long name.
|
105
|
+
parser.option("-h #help", "show help")
|
106
|
+
opts = parser.parse(["-h"])
|
107
|
+
p opts.help #=> true (not 'opts.h')
|
108
|
+
|
109
|
+
Help message::
|
110
|
+
|
111
|
+
puts "Usage: command [options] [file...]"
|
112
|
+
puts parser.help # or parser.help(20, " ")
|
113
|
+
|
114
|
+
Private option::
|
115
|
+
|
116
|
+
parser.option("-D, --debug", nil) # private option: no description
|
117
|
+
p parser.help =~ /--debug/ #=> false (not included in help message)
|
118
|
+
|
119
|
+
|
120
|
+
History
|
121
|
+
-------
|
122
|
+
|
123
|
+
Release 0.2.0
|
124
|
+
~~~~~~~~~~~~~
|
125
|
+
|
126
|
+
* Public released
|
127
|
+
|
128
|
+
|
129
|
+
License
|
130
|
+
-------
|
131
|
+
|
132
|
+
$License: MIT License $
|
133
|
+
|
134
|
+
|
135
|
+
Copyright
|
136
|
+
---------
|
137
|
+
|
138
|
+
$Copyright: copyright(c) 2011 kuwata-lab.com all rights reserved $
|
data/cmdopt.gemspec
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
###
|
2
|
+
### $Release: 0.2.0 $
|
3
|
+
### $Copyright: copyright(c) 2011 kuwata-lab.com all rights reserved $
|
4
|
+
### $License: MIT License $
|
5
|
+
###
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
|
9
|
+
spec = Gem::Specification.new do |s|
|
10
|
+
## package information
|
11
|
+
s.name = "cmdopt"
|
12
|
+
s.author = "makoto kuwata"
|
13
|
+
s.email = "kwa(at)kuwata-lab.com"
|
14
|
+
s.rubyforge_project = "cmdopt"
|
15
|
+
s.version = "0.2.0"
|
16
|
+
s.platform = Gem::Platform::RUBY
|
17
|
+
s.homepage = "https://bitbucket.org/kwatch/cmdopt/wiki/Cmdopt.rb"
|
18
|
+
s.summary = "easy-to-use command option parser"
|
19
|
+
s.description = <<-'END'
|
20
|
+
Cmdopt.rb is a command-line option parser.
|
21
|
+
It is easier to use than optparse.rb (which is a standard library of Ruby).
|
22
|
+
END
|
23
|
+
s.files = %w[README.txt MIT-LICENSE setup.rb cmdopt.gemspec
|
24
|
+
lib/cmdopt.rb test/cmdopt_test.rb]
|
25
|
+
s.test_file = 'test/cmdopt_test.rb'
|
26
|
+
s.add_development_dependency('section9-unittest')
|
27
|
+
end
|
28
|
+
|
29
|
+
if $0 == __FILE__
|
30
|
+
Gem::manage_gems
|
31
|
+
Gem::Builder.new(spec).build
|
32
|
+
end
|
33
|
+
|
34
|
+
spec
|
data/lib/cmdopt.rb
ADDED
@@ -0,0 +1,256 @@
|
|
1
|
+
###
|
2
|
+
### $Release: 0.2.0 $
|
3
|
+
### $Copyright: copyright(c) 2011 kuwata-lab.com all rights reserved $
|
4
|
+
### $License: MIT License $
|
5
|
+
###
|
6
|
+
|
7
|
+
###
|
8
|
+
### cmdopt.js -- pretty good command-line option parser for Ruby
|
9
|
+
###
|
10
|
+
### See https://bitbucket.org/kwatch/cmdopt/wiki/Cmdopt.rb for details.
|
11
|
+
###
|
12
|
+
|
13
|
+
module Cmdopt
|
14
|
+
|
15
|
+
|
16
|
+
class Schema
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@items = []
|
20
|
+
@option2item = {}
|
21
|
+
end
|
22
|
+
|
23
|
+
attr_reader :items
|
24
|
+
|
25
|
+
def get(opt)
|
26
|
+
return @option2item[opt]
|
27
|
+
end
|
28
|
+
|
29
|
+
def add(options, desc, validator=nil, handler=nil)
|
30
|
+
attr = nil
|
31
|
+
if options =~ / *\#(\w+)\z/
|
32
|
+
attr, options = $1, $`
|
33
|
+
end
|
34
|
+
short, long, arg, required = _parse_options(options)
|
35
|
+
attr ||= long || short
|
36
|
+
handler ||= proc {|val, opts| opts[attr] = val }
|
37
|
+
item = SchemaItem.new
|
38
|
+
item.short = short
|
39
|
+
item.long = long
|
40
|
+
item.arg = arg
|
41
|
+
item.required = required
|
42
|
+
item.attr = attr
|
43
|
+
item.desc = desc
|
44
|
+
item.options = options
|
45
|
+
item.validator = validator
|
46
|
+
item.handler = handler
|
47
|
+
_register(item)
|
48
|
+
return item
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def _parse_options(options)
|
54
|
+
rexp1 = /^ *-(\w), *--(\w[-\w]*)(?:=(\S+)|\[=(\S+)\])?$/
|
55
|
+
rexp2 = /^ *-(\w)(?: +(\S+)|\[(\S+)\])?$/
|
56
|
+
rexp3 = /^ *--(\w[-\w]*)(?:=(\S+)|\[=(\S+)\])?$/
|
57
|
+
short = long = arg = arg2 = nil
|
58
|
+
case options
|
59
|
+
when rexp1; short, long, arg, arg2 = $1, $2, $3, $4
|
60
|
+
when rexp2; short, arg, arg2 = $1, $2, $3
|
61
|
+
when rexp3; long, arg, arg2 = $1, $2, $3
|
62
|
+
else
|
63
|
+
raise ArgumentError.new("'#{options}': invalid option definition.")
|
64
|
+
end
|
65
|
+
return short, long, arg || arg2, !!arg
|
66
|
+
end
|
67
|
+
|
68
|
+
def _register(item)
|
69
|
+
@items << item
|
70
|
+
@option2item["-#{item.short}"] = item if item.short
|
71
|
+
@option2item["--#{item.long}"] = item if item.long
|
72
|
+
end
|
73
|
+
|
74
|
+
public
|
75
|
+
|
76
|
+
def help(width=nil, indent=" ")
|
77
|
+
items = @items.select {|it| it.desc }
|
78
|
+
if ! width
|
79
|
+
width = items.collect {|it| it.options.length }.max + 1
|
80
|
+
limit = 20
|
81
|
+
width = limit if width > limit
|
82
|
+
end
|
83
|
+
fmt = "#{indent}%-#{width}s: %s\n"
|
84
|
+
return items.collect {|it| fmt % [it.options, it.desc] }.join
|
85
|
+
end
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
class SchemaItem
|
91
|
+
|
92
|
+
attr_accessor :short, :long, :arg, :required, :attr,
|
93
|
+
:desc, :options, :validator, :handler
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
class Builder
|
99
|
+
|
100
|
+
def initialize(item)
|
101
|
+
@item = item
|
102
|
+
end
|
103
|
+
|
104
|
+
def get_item
|
105
|
+
return @item
|
106
|
+
end
|
107
|
+
|
108
|
+
def validation(&block)
|
109
|
+
@item.validator = block
|
110
|
+
self
|
111
|
+
end
|
112
|
+
|
113
|
+
def action(&block)
|
114
|
+
@item.handler = block
|
115
|
+
self
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
120
|
+
|
121
|
+
class Parser
|
122
|
+
|
123
|
+
def initialize
|
124
|
+
@schema = Schema.new
|
125
|
+
end
|
126
|
+
|
127
|
+
attr_accessor :schema
|
128
|
+
|
129
|
+
def option(options, desc)
|
130
|
+
item = @schema.add(options, desc)
|
131
|
+
return Builder.new(item)
|
132
|
+
end
|
133
|
+
|
134
|
+
def help(width=nil, indent=" ")
|
135
|
+
return @schema.help(width, indent)
|
136
|
+
end
|
137
|
+
|
138
|
+
def parse(args, defaults=nil)
|
139
|
+
opts = _new_opts(defaults)
|
140
|
+
while ! args.empty?
|
141
|
+
arg = args.shift
|
142
|
+
case arg
|
143
|
+
when "--" ; break
|
144
|
+
when /^--/; _parse_long(arg, opts)
|
145
|
+
when /^-/ ; _parse_short(arg, args, opts)
|
146
|
+
else
|
147
|
+
args.unshift(arg)
|
148
|
+
break
|
149
|
+
end
|
150
|
+
end
|
151
|
+
return opts
|
152
|
+
end
|
153
|
+
|
154
|
+
private
|
155
|
+
|
156
|
+
def _new_opts(defaults=nil)
|
157
|
+
dict = {}
|
158
|
+
@schema.items.each {|item| dict[item.attr] = nil }
|
159
|
+
dict.update(defaults) if defaults
|
160
|
+
return Options.new(dict)
|
161
|
+
end
|
162
|
+
|
163
|
+
def _parse_long(arg, opts)
|
164
|
+
arg =~ /^--(\w[-\w]*)(?:=(.*))?$/ or
|
165
|
+
raise ParseError.new("#{arg}: invalid long option.")
|
166
|
+
name, val = $1, $2
|
167
|
+
item = @schema.get("--#{name}") or
|
168
|
+
raise ParseError.new("#{arg}: unknown option.")
|
169
|
+
if val.nil?
|
170
|
+
raise ParseError.new("#{arg}: argument required.") if item.required
|
171
|
+
else
|
172
|
+
raise ParseError.new("#{arg}: unexpected argument.") unless item.arg
|
173
|
+
end
|
174
|
+
val ||= true
|
175
|
+
_call_validator(item.validator, val, arg)
|
176
|
+
_call_handler(item.handler, val, opts)
|
177
|
+
end
|
178
|
+
|
179
|
+
def _parse_short(arg, args, opts)
|
180
|
+
i, n = 1, arg.length
|
181
|
+
while i < n
|
182
|
+
ch = arg[i, 1]
|
183
|
+
item = @schema.get("-#{ch}") or
|
184
|
+
raise ParseError.new("-#{ch}: unknown option.")
|
185
|
+
if ! item.arg
|
186
|
+
val = true
|
187
|
+
elsif item.required
|
188
|
+
val = arg[(i+1)..-1]
|
189
|
+
if val.empty?
|
190
|
+
! args.empty? or
|
191
|
+
raise ParseError.new("-#{ch}: argument required.")
|
192
|
+
val = args.shift
|
193
|
+
end
|
194
|
+
else
|
195
|
+
val = arg[(i+1)..-1]
|
196
|
+
val = true if val.empty?
|
197
|
+
end
|
198
|
+
if val == true ; optarg = "-#{ch}"
|
199
|
+
elsif item.required ; optarg = "-#{ch} #{val}"
|
200
|
+
else ; optarg = "-#{ch}#{val}"
|
201
|
+
end
|
202
|
+
_call_validator(item.validator, val, optarg)
|
203
|
+
_call_handler(item.handler, val, opts)
|
204
|
+
break if item.arg
|
205
|
+
i += 1
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
def _call_validator(validator, val, arg)
|
210
|
+
if validator
|
211
|
+
errmsg = validator.call(val)
|
212
|
+
raise ParseError.new("#{arg}: #{errmsg}") if errmsg
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
def _call_handler(handler, val, opts)
|
217
|
+
handler.call(val, opts) if handler
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
class ParseError < StandardError
|
224
|
+
end
|
225
|
+
|
226
|
+
|
227
|
+
class Options
|
228
|
+
|
229
|
+
def initialize(dict=nil)
|
230
|
+
if dict
|
231
|
+
dict.each {|k, v| instance_variable_set("@#{k}", v) }
|
232
|
+
(class << self; self; end).class_eval do
|
233
|
+
attr_accessor *dict.keys
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
def [](name)
|
239
|
+
instance_variable_get("@#{name}")
|
240
|
+
end
|
241
|
+
|
242
|
+
def []=(name, val)
|
243
|
+
instance_variable_set("@#{name}", val)
|
244
|
+
end
|
245
|
+
|
246
|
+
def inspect
|
247
|
+
s = self.instance_variables.sort.collect {|name|
|
248
|
+
"#{name[1..-1]}=#{instance_variable_get(name).inspect}"
|
249
|
+
}.join(', ')
|
250
|
+
return "<Cmdopt::Options #{s}>"
|
251
|
+
end
|
252
|
+
|
253
|
+
end
|
254
|
+
|
255
|
+
|
256
|
+
end
|