config_parser 0.1.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/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
+