cmdopt 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. data/MIT-LICENSE +21 -0
  2. data/README.txt +138 -0
  3. data/cmdopt.gemspec +34 -0
  4. data/lib/cmdopt.rb +256 -0
  5. data/setup.rb +1585 -0
  6. data/test/cmdopt_test.rb +377 -0
  7. metadata +66 -0
@@ -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
+
@@ -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 $
@@ -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
@@ -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