config_parser 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/History ADDED
@@ -0,0 +1,5 @@
1
+ == 0.1.0 / 2010-07-25
2
+
3
+ Initial release after extraction and cleanup from Configurable. Intended as a
4
+ placeholder until final cleanup. Note that the documentation is largely
5
+ inaccurate at this stage.
data/MIT-LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2010, Simon Chiang
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,84 @@
1
+ = ConfigParser
2
+
3
+ Parse command-line options into a configuration hash.
4
+
5
+ == Description
6
+
7
+ ConfigParser is an analogue of
8
+ {OptionParser}[http://www.ruby-doc.org/core/classes/OptionParser.html] that
9
+ formalizes the pattern of setting parsed options into a hash. ConfigParser
10
+ uses a similar, simplified declaration syntax that places less emphasis on the
11
+ conversion of inputs to objects, preferring instead to delegate that
12
+ responsibility to whatever consumes the hash.
13
+
14
+ == Usage
15
+
16
+ ConfigParser can be used much like OptionParser, where the parser itself can
17
+ be used as a delegate to a config hash.
18
+
19
+ parser = ConfigParser.new
20
+ parser.on '-s', '--long LONG', 'a standard option' do |value|
21
+ parser[:long] = value
22
+ end
23
+
24
+ parser.on '--[no-]switch', 'a switch' do |value|
25
+ parser[:switch] = value
26
+ end
27
+
28
+ parser.on '--flag', 'a flag' do
29
+ parser[:flag] = true
30
+ end
31
+
32
+ parser.parse('a b --long arg --switch --flag c')
33
+ # => ['a', 'b', 'c']
34
+
35
+ parser.config
36
+ # => {:long => 'arg', :switch => true, :flag => true}
37
+
38
+ parser.to_s
39
+ # => %q{
40
+ # -s, --long LONG a standard option
41
+ # --[no-]switch a switch
42
+ # --flag a flag
43
+ # }
44
+
45
+ ConfigParser formalizes this pattern of setting values into a config hash as
46
+ they occur, and adds the ability to specify default values.
47
+
48
+ parser = ConfigParser.new
49
+ parser.add(:key, 'default')
50
+
51
+ parser.parse('a b --key option c') # => ['a', 'b', 'c']
52
+ parser.config # => {:key => 'option'}
53
+
54
+ parser.parse('a b c') # => ['a', 'b', 'c']
55
+ parser.config # => {:key => 'default'}
56
+
57
+ Config keys may be mapped to options using arguments like those given to 'on',
58
+ or using an options hash. A block can be given to processes values before they
59
+ are set as configs.
60
+
61
+ parser = ConfigParser.new
62
+ parser.add(:x, nil, '-o', '--one', 'by args') {|value| value.upcase }
63
+ parser.add(:y, nil, :long => 'two', :desc => 'by hash')
64
+
65
+ parser.parse('a b --one value --two c')
66
+ # => ['a', 'b', 'c']
67
+
68
+ parser.config
69
+ # => {:x => 'VALUE', :y => true}
70
+
71
+ ConfigParser integrates well with libraries like
72
+ {Configurable}[http://tap.rubyforge.org/configurable] that are designed to set
73
+ configurations via a config hash.
74
+
75
+ == Installation
76
+
77
+ ConfigParser is available as a gem via {Gemcutter}[http://rubygems.org/gems/config_parser].
78
+
79
+ % gem install config_parser
80
+
81
+ == Info
82
+
83
+ Copyright (c) 2010, Simon Chiang
84
+ License:: {MIT-Style}[link:files/MIT-LICENSE.html]
@@ -0,0 +1,393 @@
1
+ require 'config_parser/list'
2
+ autoload(:Shellwords, 'shellwords')
3
+
4
+ # ConfigParser is the Configurable equivalent of
5
+ # {OptionParser}[http://www.ruby-doc.org/core/classes/OptionParser.html]
6
+ # and uses a similar, simplified (see below) syntax to declare options.
7
+ #
8
+ # opts = {}
9
+ # parser = ConfigParser.new do |psr|
10
+ # psr.on "-s", "--long LONG", "a standard option" do |value|
11
+ # opts[:long] = value
12
+ # end
13
+ #
14
+ # psr.on "--[no-]switch", "a switch" do |value|
15
+ # opts[:switch] = value
16
+ # end
17
+ #
18
+ # psr.on "--flag", "a flag" do
19
+ # # note: no value is parsed; the block
20
+ # # only executes if the flag is found
21
+ # opts[:flag] = true
22
+ # end
23
+ # end
24
+ #
25
+ # parser.parse("a b --long arg --switch --flag c") # => ['a', 'b', 'c']
26
+ # opts # => {:long => 'arg', :switch => true, :flag => true}
27
+ #
28
+ # ConfigParser formalizes this pattern of setting values in a hash as they
29
+ # occur, and adds the ability to specify default values. The syntax is
30
+ # not quite as friendly as for ordinary options, but meshes well with
31
+ # Configurable classes:
32
+ #
33
+ # psr = ConfigParser.new
34
+ # psr.define(:key, 'default', :desc => 'a standard option')
35
+ #
36
+ # psr.parse('a b --key option c') # => ['a', 'b', 'c']
37
+ # psr.config # => {:key => 'option'}
38
+ #
39
+ # psr.parse('a b c') # => ['a', 'b', 'c']
40
+ # psr.config # => {:key => 'default'}
41
+ #
42
+ # And now directly from a Configurable class, the equivalent of the
43
+ # original example:
44
+ #
45
+ # class ConfigClass
46
+ # include Configurable
47
+ #
48
+ # config :long, 'default', :short => 's' # a standard option
49
+ # config :switch, false, &c.switch # a switch
50
+ # config :flag, false, &c.flag # a flag
51
+ # end
52
+ #
53
+ # psr = ConfigParser.new
54
+ # psr.add(ConfigClass.configurations)
55
+ #
56
+ # psr.parse("a b --long arg --switch --flag c") # => ['a', 'b', 'c']
57
+ # psr.config # => {:long => 'arg', :switch => true, :flag => true}
58
+ #
59
+ # psr.parse("a b --long=arg --no-switch c") # => ['a', 'b', 'c']
60
+ # psr.config # => {:long => 'arg', :switch => false, :flag => false}
61
+ #
62
+ # psr.parse("a b -sarg c") # => ['a', 'b', 'c']
63
+ # psr.config # => {:long => 'arg', :switch => false, :flag => false}
64
+ #
65
+ # As you might expect, config attributes are used by ConfigParser to
66
+ # correctly build a corresponding option. In configurations like :switch,
67
+ # the block implies the {:type => :switch} attribute and so the
68
+ # config is made into a switch-style option by ConfigParser.
69
+ #
70
+ # Use the to_s method to convert a ConfigParser into command line
71
+ # documentation:
72
+ #
73
+ # "\nconfigurations:\n#{psr.to_s}"
74
+ # # => %q{
75
+ # # configurations:
76
+ # # -s, --long LONG a standard option
77
+ # # --[no-]switch a switch
78
+ # # --flag a flag
79
+ # # }
80
+ #
81
+ # ==== Simplifications
82
+ #
83
+ # ConfigParser simplifies the OptionParser syntax for 'on'. ConfigParser does
84
+ # not support automatic conversion of values, gets rid of 'optional' arguments
85
+ # for options, and only supports a single description string. Hence:
86
+ #
87
+ # psr = ConfigParser.new
88
+ #
89
+ # # incorrect, raises error as this will look
90
+ # # like multiple descriptions are specified
91
+ # psr.on("--delay N",
92
+ # Float,
93
+ # "Delay N seconds before executing") # !> ArgumentError
94
+ #
95
+ # # correct
96
+ # psr.on("--delay N", "Delay N seconds before executing") do |value|
97
+ # value.to_f
98
+ # end
99
+ #
100
+ # # this ALWAYS requires the argument and raises
101
+ # # an error because multiple descriptions are
102
+ # # specified
103
+ # psr.on("-i", "--inplace [EXTENSION]",
104
+ # "Edit ARGV files in place",
105
+ # " (make backup if EXTENSION supplied)") # !> ArgumentError
106
+ #
107
+ # # correct
108
+ # psr.on("-i", "--inplace EXTENSION",
109
+ # "Edit ARGV files in place\n (make backup if EXTENSION supplied)")
110
+ #
111
+ #
112
+ class ConfigParser
113
+ include Utils
114
+
115
+ # Returns an array of the options registered with self, in the order in
116
+ # which they were added. Separators are also stored in the registry.
117
+ attr_reader :registry
118
+
119
+ # A hash of (flag, Option) pairs mapping command line flags like '-s' or
120
+ # '--long' to the Option that handles them.
121
+ attr_reader :options
122
+
123
+ # The hash receiving configurations produced by parse.
124
+ attr_accessor :config
125
+
126
+ # The argument to stop processing options
127
+ attr_accessor :option_break
128
+
129
+ # Set to true to preserve the break argument
130
+ attr_accessor :preserve_option_break
131
+
132
+ # Initializes a new ConfigParser and passes it to the block, if given.
133
+ def initialize(config={}, opts={})
134
+ opts = {
135
+ :option_break => OPTION_BREAK,
136
+ :preserve_option_break => false
137
+ }.merge(opts)
138
+
139
+ @registry = []
140
+ @options = {}
141
+ @config = config
142
+ @option_break = opts[:option_break]
143
+ @preserve_option_break = opts[:preserve_option_break]
144
+
145
+ yield(self) if block_given?
146
+ end
147
+
148
+ # Returns the config value for key.
149
+ def [](key)
150
+ config[key]
151
+ end
152
+
153
+ # Sets the config value for key.
154
+ def []=(key, value)
155
+ config[key] = value
156
+ end
157
+
158
+ # Adds a separator string to self, used in to_s.
159
+ def separator(str)
160
+ @registry << str
161
+ end
162
+
163
+ # Registers the option with self by adding opt to options and mapping the
164
+ # opt flags. Raises an error for conflicting flags.
165
+ #
166
+ # If override is specified, options with conflicting flags are removed and
167
+ # no error is raised. Note that this may remove multiple options.
168
+ def register(option, override=false)
169
+ return if option.nil?
170
+
171
+ if override
172
+ existing = option.flags.collect {|flag| @options.delete(flag) }
173
+ @registry -= existing
174
+ end
175
+
176
+ unless @registry.include?(option)
177
+ @registry << option
178
+ end
179
+
180
+ option.flags.each do |flag|
181
+ current = @options[flag]
182
+
183
+ if current && current != option
184
+ raise ArgumentError, "already mapped to a different option: #{flag}"
185
+ end
186
+
187
+ @options[flag] = option
188
+ end
189
+
190
+ option
191
+ end
192
+
193
+ # Constructs an Option using args and registers it with self. Args may
194
+ # contain (in any order) a short switch, a long switch, and a description
195
+ # string. Either the short or long switch may signal that the option
196
+ # should take an argument by providing an argument name.
197
+ #
198
+ # psr = ConfigParser.new
199
+ #
200
+ # # this option takes an argument
201
+ # psr.on('-s', '--long ARG_NAME', 'description') do |value|
202
+ # # ...
203
+ # end
204
+ #
205
+ # # so does this one
206
+ # psr.on('-o ARG_NAME', 'description') do |value|
207
+ # # ...
208
+ # end
209
+ #
210
+ # # this option does not
211
+ # psr.on('-f', '--flag') do
212
+ # # ...
213
+ # end
214
+ #
215
+ # A switch-style option can be specified by prefixing the long switch with
216
+ # '--[no-]'. Switch options will pass true to the block for the positive
217
+ # form and false for the negative form.
218
+ #
219
+ # psr.on('--[no-]switch') do |value|
220
+ # # ...
221
+ # end
222
+ #
223
+ # Args may also contain a trailing hash defining all or part of the option:
224
+ #
225
+ # psr.on('-k', :long => '--key', :desc => 'description')
226
+ # # ...
227
+ # end
228
+ #
229
+ def on(*args, &block)
230
+ register new_option(args, &block)
231
+ end
232
+
233
+ # Same as on, but overrides options with overlapping switches.
234
+ def on!(*args, &block)
235
+ register new_option(args, &block), true
236
+ end
237
+
238
+ # Defines and registers a config-style option with self. Define does not
239
+ # take a block; the default value will be added to config, and any parsed
240
+ # value will override the default. Normally the key will be turned into
241
+ # the long switch; specify an alternate long, a short, description, etc
242
+ # using attributes.
243
+ #
244
+ # psr = ConfigParser.new
245
+ # psr.define(:one, 'default')
246
+ # psr.define(:two, 'default', :long => '--long', :short => '-s')
247
+ #
248
+ # psr.parse("--one one --long two")
249
+ # psr.config # => {:one => 'one', :two => 'two'}
250
+ #
251
+ # Define support several types of configurations that define a special
252
+ # block to handle the values parsed from the command line. See the
253
+ # 'setup_<type>' methods in Utils. Any type with a corresponding setup
254
+ # method is valid:
255
+ #
256
+ # psr = ConfigParser.new
257
+ # psr.define(:flag, false, :type => :flag)
258
+ # psr.define(:switch, false, :type => :switch)
259
+ # psr.define(:list, [], :type => :list)
260
+ #
261
+ # psr.parse("--flag --switch --list one --list two --list three")
262
+ # psr.config # => {:flag => true, :switch => true, :list => ['one', 'two', 'three']}
263
+ #
264
+ # New, valid types may be added by implementing new setup_<type> methods
265
+ # following this pattern:
266
+ #
267
+ # module SpecialType
268
+ # def setup_special(key, default_value, attributes)
269
+ # # modify attributes if necessary
270
+ # attributes[:long] = "--#{key}"
271
+ # attributes[:arg_name] = 'ARG_NAME'
272
+ #
273
+ # # return a block handling the input
274
+ # lambda {|input| config[key] = input.reverse }
275
+ # end
276
+ # end
277
+ #
278
+ # psr = ConfigParser.new.extend SpecialType
279
+ # psr.define(:opt, false, :type => :special)
280
+ #
281
+ # psr.parse("--opt value")
282
+ # psr.config # => {:opt => 'eulav'}
283
+ #
284
+ # The :hidden type causes no configuration to be defined. Raises an error if
285
+ # key is already set by a different option.
286
+ def add(key, default=nil, *args, &block)
287
+ attrs = args.last.kind_of?(Hash) ? args.pop : {}
288
+ attrs = attrs.merge(:key => key, :default => default)
289
+ args << attrs
290
+
291
+ on(*args, &block)
292
+ end
293
+
294
+ # Parses options from argv in a non-destructive manner and returns an
295
+ # array of arguments remaining after options have been removed. If a
296
+ # string argv is provided, it will be splits into an array using
297
+ # Shellwords.
298
+ #
299
+ # ==== Options
300
+ #
301
+ # clear_config:: clears the currently parsed configs (true)
302
+ # add_defaults:: adds the default values to config (true)
303
+ # ignore_unknown_options:: causes unknown options to be ignored (false)
304
+ #
305
+ def parse(argv=ARGV)
306
+ argv = argv.dup unless argv.kind_of?(String)
307
+ parse!(argv)
308
+ end
309
+
310
+ # Same as parse, but removes parsed args from argv.
311
+ def parse!(argv=ARGV)
312
+ argv = Shellwords.shellwords(argv) if argv.kind_of?(String)
313
+
314
+ args = []
315
+ remainder = scan(argv) {|arg| args << arg }
316
+ args.concat(remainder)
317
+ argv.replace(args)
318
+
319
+ argv
320
+ end
321
+
322
+ def scan(argv=ARGV)
323
+ while !argv.empty?
324
+ arg = argv.shift
325
+
326
+ # determine if the arg is an option
327
+ unless arg.kind_of?(String) && arg[0] == ?-
328
+ yield(arg)
329
+ next
330
+ end
331
+
332
+ # add the remaining args and break
333
+ # for the option break
334
+ if option_break === arg
335
+ argv.unshift(arg) if preserve_option_break
336
+ break
337
+ end
338
+
339
+ flag, value = arg, nil
340
+
341
+ # try the flag directly
342
+ unless option = @options[flag]
343
+
344
+ # then try --opt=value syntax
345
+ flag, value = flag.split('=', 2)
346
+
347
+ # then try -ovalue syntax
348
+ if value.nil? && flag[1] != ?-
349
+ flag, value = flag[0, 2], flag[2, flag.length - 2]
350
+ end
351
+
352
+ unless option = @options[flag]
353
+ raise "unknown option: #{flag}"
354
+ end
355
+ end
356
+
357
+ option.parse(flag, value, argv, config)
358
+ end
359
+
360
+ argv
361
+ end
362
+
363
+ # Converts the options and separators in self into a help string suitable for
364
+ # display on the command line.
365
+ def to_s
366
+ @registry.collect do |option|
367
+ option.to_s.rstrip
368
+ end.join("\n") + "\n"
369
+ end
370
+
371
+ protected
372
+
373
+ def option_class(attrs) # :nodoc:
374
+ type = attrs[:type] || guess_type(attrs)
375
+
376
+ case type
377
+ when :option then Option
378
+ when :flag then Flag
379
+ when :switch then Switch
380
+ when :list then List
381
+ when Class then type
382
+ else raise "unknown option type: #{type}"
383
+ end
384
+ end
385
+
386
+ # helper to parse an option from an argv. new_option is used
387
+ # by on and on! to generate options
388
+ def new_option(argv, &block) # :nodoc:
389
+ attrs = argv.last.kind_of?(Hash) ? argv.pop : {}
390
+ attrs = attrs.merge parse_attrs(argv)
391
+ option_class(attrs).new(attrs, &block)
392
+ end
393
+ end
@@ -0,0 +1,121 @@
1
+ require 'config_parser/utils'
2
+
3
+ class ConfigParser
4
+ class Flag
5
+ include Utils
6
+
7
+ # The config key
8
+ attr_reader :key
9
+
10
+ # The default value
11
+ attr_reader :default
12
+
13
+ # The short flag mapping to self
14
+ attr_reader :short
15
+
16
+ # The long flag mapping to self
17
+ attr_reader :long
18
+
19
+ # The description printed by to_s
20
+ attr_reader :desc
21
+
22
+ # A callback for processing values
23
+ attr_reader :callback
24
+
25
+ def initialize(attrs={}, &callback)
26
+ @key = attrs[:key]
27
+ @default = attrs[:default]
28
+ @short = shortify(attrs[:short])
29
+ @long = longify(attrs.has_key?(:long) ? attrs[:long] : key)
30
+ @desc = attrs[:desc]
31
+ @callback = callback
32
+ end
33
+
34
+ # Returns an array of non-nil flags mapping to self (ie [long, short]).
35
+ def flags
36
+ [long, short].compact
37
+ end
38
+
39
+ # Assigns true into config and raises an error if a value is provided
40
+ # (flags take none). The callback will be called if specified to provide
41
+ # the assigned value.
42
+ #
43
+ # Note this is the entry point for handling different types of
44
+ # configuration flags.
45
+ #
46
+ #--
47
+ # Implementation Note
48
+ #
49
+ # The compact syntax for short flags is handled through parse by
50
+ # unshifting remaining shorts (ie value) back onto argv. This allows
51
+ # shorts that consume a value to take the remainder as needed. As an
52
+ # example to clarify, assume -x -y are flags where -x takes a value and -y
53
+ # does not. These are equivalent:
54
+ #
55
+ # -x value -y
56
+ # -xvalue -y
57
+ # -y -xvalue
58
+ # -yxvalue
59
+ #
60
+ # Whereas this is not:
61
+ #
62
+ # -xyvalue # x receives 'yvalue' not 'value'
63
+ #
64
+ # Parse handles the compact short syntax splitting '-yxvalue' into '-y',
65
+ # 'xvalue'. Then '-y' determines whether or not it needs a values; if not
66
+ # '-xvalue' gets unshifted to argv and parsing continues as if '-y
67
+ # -xvalue' were the original arguments.
68
+ def parse(flag, value, argv=[], config={})
69
+ unless value.nil?
70
+ if flag == short
71
+ argv.unshift "-#{value}"
72
+ else
73
+ raise "value specified for flag: #{flag}"
74
+ end
75
+ end
76
+
77
+ assign(config, callback ? callback.call : default.nil? ? false : default)
78
+ end
79
+
80
+ # Assign the value to the config hash, if key is set. Returns value.
81
+ def assign(config, value)
82
+ config[key] = value if key
83
+ value
84
+ end
85
+
86
+ # Formats self as a help string for use on the command line.
87
+ def to_s
88
+ lines = wrap(desc.to_s, 43)
89
+
90
+ header = header_str
91
+ header = header.length > 36 ? header.ljust(80) : (LINE_FORMAT % [header, lines.shift])
92
+
93
+ if lines.empty?
94
+ header
95
+ else
96
+ lines.collect! {|line| LINE_FORMAT % [nil, line] }
97
+ "#{header}\n#{lines.join("\n")}"
98
+ end
99
+ end
100
+
101
+ private
102
+
103
+ def header_str # :nodoc:
104
+ " #{short_str}#{long_str}"
105
+ end
106
+
107
+ # helper returning short formatted for to_s
108
+ def short_str # :nodoc:
109
+ case
110
+ when short && long then "#{short}, "
111
+ when short then "#{short}"
112
+ else ' '
113
+ end
114
+ end
115
+
116
+ # helper returning long formatted for to_s
117
+ def long_str # :nodoc:
118
+ long
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,38 @@
1
+ require 'config_parser/option'
2
+
3
+ class ConfigParser
4
+ class List < Option
5
+
6
+ # The default split character for multiple values
7
+ DEFAULT_SPLIT = ','
8
+
9
+ # The maximum number of values that may be specified.
10
+ attr_reader :limit
11
+
12
+ # The sequence on which to split single values into multiple values. Set
13
+ # to nil to prevent split.
14
+ attr_reader :split
15
+
16
+ def initialize(attrs={})
17
+ super
18
+ @limit = attrs[:n]
19
+ @split = attrs.has_key?(:split) ? attrs[:split] : DEFAULT_SPLIT
20
+ end
21
+
22
+ # List assigns configs by pushing the value onto an array, rather than
23
+ # directly setting it onto config. As usual, no value is assigned if key
24
+ # is not set. Returns value (the input, not the array).
25
+ def assign(config, value)
26
+ if key
27
+ array = (config[key] ||= [])
28
+ array.concat(split ? value.split(split) : [value])
29
+
30
+ if limit && array.length > limit
31
+ raise "too many assignments: #{key.inspect}"
32
+ end
33
+ end
34
+
35
+ value
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,40 @@
1
+ require 'config_parser/switch'
2
+
3
+ class ConfigParser
4
+
5
+ # Represents an option registered with ConfigParser.
6
+ class Option < Flag
7
+ DEFAULT_ARGNAME = 'VALUE'
8
+
9
+ # The argument name printed by to_s.
10
+ attr_reader :arg_name
11
+
12
+ def initialize(attrs={})
13
+ super
14
+ @arg_name = attrs[:arg_name] || (key ? key.to_s.upcase : DEFAULT_ARGNAME)
15
+ end
16
+
17
+ # Parse the flag and value. If no value is provided and a value is
18
+ # required, then a value is shifted off of argv. Calls the callback
19
+ # with the value, if specified, and assigns the result.
20
+ def parse(flag, value, argv=[], config={})
21
+ if value.nil?
22
+ unless value = next_arg(argv)
23
+ raise "no value provided for: #{flag}"
24
+ end
25
+ end
26
+ assign(config, callback ? callback.call(value) : value)
27
+ end
28
+
29
+ private
30
+
31
+ def next_arg(argv) # :nodoc:
32
+ arg = argv[0]
33
+ (arg.kind_of?(String) && arg[0] == ?-) ? default : argv.shift
34
+ end
35
+
36
+ def header_str # :nodoc:
37
+ " #{short_str}#{long_str} #{arg_name}"
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,43 @@
1
+ require 'config_parser/flag'
2
+
3
+ class ConfigParser
4
+
5
+ # Switch represents a special type of Option where both positive (--flag)
6
+ # and negative (--no-flag) flags map to self.
7
+ class Switch < Flag
8
+
9
+ # The negative long flag, determined from long if not set otherwise.
10
+ attr_reader :negative_long
11
+
12
+ def initialize(attrs={})
13
+ attrs[:default] = true unless attrs.has_key?(:default)
14
+ super
15
+
16
+ raise ArgumentError, "no long specified" unless long
17
+ @negative_long = attrs[:negative_long] || prefix_long(long, 'no-')
18
+ end
19
+
20
+ # Returns an array of non-nil flags mapping to self (ie [long,
21
+ # negative_long, short]).
22
+ def flags
23
+ [long, negative_long, short].compact
24
+ end
25
+
26
+ # Assigns true into config for positive flags and false for negative
27
+ # flags. If specified, the callback is called with the boolean to
28
+ # determine the assigned value. Raises an error if a value is provided
29
+ # (switches take none).
30
+ def parse(flag, value, argv=[], config={})
31
+ raise "value specified for switch: #{flag}" if value
32
+ value = (flag == negative_long ? !default : default)
33
+ assign(config, callback ? callback.call(value) : value)
34
+ end
35
+
36
+ private
37
+
38
+ # helper returning long formatted for to_s
39
+ def long_str # :nodoc:
40
+ long ? prefix_long(long, '[no-]') : ''
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,135 @@
1
+ class ConfigParser
2
+
3
+ # A medly of methods used throughout the ConfigParser classes.
4
+ module Utils
5
+ module_function
6
+
7
+ # A format string used by to_s
8
+ LINE_FORMAT = "%-36s %-43s"
9
+
10
+ # The default option break
11
+ OPTION_BREAK = "--"
12
+
13
+ # Matches a long flag
14
+ LONG_FLAG = /\A--.+\z/
15
+
16
+ # Matches a short flag
17
+ SHORT_FLAG = /\A-.\z/
18
+
19
+ # Matches a switch declaration (ex: '--[no-]opt', '--nest:[no-]opt').
20
+ # After the match:
21
+ #
22
+ # $1:: the nesting prefix ('nest')
23
+ # $2:: the long flag name ('opt')
24
+ #
25
+ SWITCH = /\A--(.*?)\[no-\](.+)\z/
26
+
27
+ # Turns the input into a short flag by prefixing '-' (as needed). Raises
28
+ # an error if the input doesn't result in a short flag. Nils are
29
+ # returned directly.
30
+ #
31
+ # shortify('-o') # => '-o'
32
+ # shortify(:o) # => '-o'
33
+ #
34
+ def shortify(str)
35
+ return nil if str.nil?
36
+
37
+ str = str.to_s
38
+ str = "-#{str}" unless str[0] == ?-
39
+
40
+ unless str =~ SHORT_FLAG
41
+ raise ArgumentError, "invalid short flag: #{str}"
42
+ end
43
+
44
+ str
45
+ end
46
+
47
+ # Turns the input into a long flag by prefixing '--' (as needed). Raises
48
+ # an error if the input doesn't result in a long flag. Nils are
49
+ # returned directly.
50
+ #
51
+ # longify('--opt') # => '--opt'
52
+ # longify(:opt) # => '--opt'
53
+ #
54
+ def longify(str)
55
+ return nil if str.nil?
56
+
57
+ str = str.to_s
58
+ str = "--#{str}" unless str[0] == ?-
59
+
60
+ unless str =~ LONG_FLAG
61
+ raise ArgumentError, "invalid long flag: #{str}"
62
+ end
63
+
64
+ str
65
+ end
66
+
67
+ # Adds a prefix onto the last nested segment of a long option.
68
+ #
69
+ # prefix_long('--opt', 'no-') # => '--no-opt'
70
+ # prefix_long('--nested:opt', 'no-') # => '--nested:no-opt'
71
+ #
72
+ def prefix_long(switch, prefix, split_char=':')
73
+ switch = switch[2, switch.length-2] if switch =~ /^--/
74
+ switch = switch.split(split_char)
75
+ switch[-1] = "#{prefix}#{switch[-1]}"
76
+ "--#{switch.join(':')}"
77
+ end
78
+
79
+ # A wrapping algorithm slightly modified from:
80
+ # http://blog.macromates.com/2006/wrapping-text-with-regular-expressions/
81
+ def wrap(line, cols=80, tabsize=2)
82
+ line = line.gsub(/\t/, " " * tabsize) unless tabsize == nil
83
+ line.gsub(/(.{1,#{cols}})( +|$\r?\n?)|(.{1,#{cols}})/, "\\1\\3\n").split(/\s*?\n/)
84
+ end
85
+
86
+ def parse_attrs(argv)
87
+ attrs={}
88
+
89
+ argv.each do |arg|
90
+ if arg[0] != ?-
91
+ attrs[:desc] = arg
92
+ next
93
+ end
94
+
95
+ flag, arg_name = arg.split(/\s+/, 2)
96
+
97
+ if arg_name
98
+ attrs[:arg_name] = arg_name
99
+ end
100
+
101
+ case flag
102
+ when SWITCH
103
+ attrs[:long] = "--#{$1}#{$2}"
104
+ attrs[:negative_long] = "--#{$1}no-#{$2}"
105
+
106
+ if arg_name
107
+ raise ArgumentError, "arg_name specified for switch: #{arg_name}"
108
+ end
109
+
110
+ when LONG_FLAG
111
+ attrs[:long] = flag
112
+
113
+ when SHORT_FLAG
114
+ attrs[:short] = flag
115
+
116
+ else
117
+ raise ArgumentError.new("invalid flag: #{arg.inspect}")
118
+ end
119
+ end
120
+
121
+ attrs
122
+ end
123
+
124
+ def guess_type(attrs) # :nodoc:
125
+ case
126
+ when attrs[:negative_long]
127
+ :switch
128
+ when attrs[:arg_name] || attrs[:default]
129
+ :option
130
+ else
131
+ :flag
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,7 @@
1
+ class ConfigParser
2
+ MAJOR = 0
3
+ MINOR = 1
4
+ TINY = 0
5
+
6
+ VERSION="#{MAJOR}.#{MINOR}.#{TINY}"
7
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: config_parser
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Simon Chiang
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-07-25 00:00:00 -06:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: lazydoc
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 0
30
+ version: "1.0"
31
+ type: :runtime
32
+ version_requirements: *id001
33
+ description:
34
+ email: simon.a.chiang@gmail.com
35
+ executables: []
36
+
37
+ extensions: []
38
+
39
+ extra_rdoc_files:
40
+ - History
41
+ - README
42
+ - MIT-LICENSE
43
+ files:
44
+ - lib/config_parser.rb
45
+ - lib/config_parser/flag.rb
46
+ - lib/config_parser/list.rb
47
+ - lib/config_parser/option.rb
48
+ - lib/config_parser/switch.rb
49
+ - lib/config_parser/utils.rb
50
+ - lib/config_parser/version.rb
51
+ - History
52
+ - README
53
+ - MIT-LICENSE
54
+ has_rdoc: true
55
+ homepage: ""
56
+ licenses: []
57
+
58
+ post_install_message:
59
+ rdoc_options:
60
+ - --main
61
+ - README
62
+ - -S
63
+ - -N
64
+ - --title
65
+ - ConfigParser
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ segments:
73
+ - 0
74
+ version: "0"
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ segments:
80
+ - 0
81
+ version: "0"
82
+ requirements: []
83
+
84
+ rubyforge_project: ""
85
+ rubygems_version: 1.3.6
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: Parse command-line options into a configuration hash
89
+ test_files: []
90
+