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.
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