config_parser 0.1.0 → 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/History +4 -0
- data/README +32 -21
- data/lib/config_parser.rb +99 -202
- data/lib/config_parser/flag.rb +52 -22
- data/lib/config_parser/list.rb +38 -15
- data/lib/config_parser/option.rb +20 -11
- data/lib/config_parser/switch.rb +18 -16
- data/lib/config_parser/utils.rb +94 -15
- data/lib/config_parser/version.rb +1 -1
- metadata +3 -3
data/History
CHANGED
data/README
CHANGED
@@ -7,14 +7,14 @@ Parse command-line options into a configuration hash.
|
|
7
7
|
ConfigParser is an analogue of
|
8
8
|
{OptionParser}[http://www.ruby-doc.org/core/classes/OptionParser.html] that
|
9
9
|
formalizes the pattern of setting parsed options into a hash. ConfigParser
|
10
|
-
uses a similar, simplified declaration syntax
|
11
|
-
|
12
|
-
|
10
|
+
uses a similar, simplified declaration syntax and provides an API that
|
11
|
+
integrates well with libraries like
|
12
|
+
{Configurable}[http://tap.rubyforge.org/configurable].
|
13
13
|
|
14
14
|
== Usage
|
15
15
|
|
16
|
-
ConfigParser can be used much like OptionParser, where the parser
|
17
|
-
|
16
|
+
ConfigParser can be used much like OptionParser, where the parser can set
|
17
|
+
values into a config hash.
|
18
18
|
|
19
19
|
parser = ConfigParser.new
|
20
20
|
parser.on '-s', '--long LONG', 'a standard option' do |value|
|
@@ -46,31 +46,42 @@ ConfigParser formalizes this pattern of setting values into a config hash as
|
|
46
46
|
they occur, and adds the ability to specify default values.
|
47
47
|
|
48
48
|
parser = ConfigParser.new
|
49
|
-
parser.add
|
49
|
+
parser.add :flag, false # false as a default makes a --flag
|
50
|
+
parser.add :switch, true # true makes a --[no-]switch
|
51
|
+
parser.add :list, [] # an array makes a list-style option
|
52
|
+
parser.add :opt, 'default' # all others make an ordinary option
|
50
53
|
|
51
|
-
parser.parse('a b
|
52
|
-
parser.config
|
53
|
-
|
54
|
-
|
55
|
-
|
54
|
+
parser.parse('a b c') # => ['a', 'b', 'c']
|
55
|
+
parser.config
|
56
|
+
# => {
|
57
|
+
# :flag => false,
|
58
|
+
# :switch => true,
|
59
|
+
# :list => [],
|
60
|
+
# :opt => 'default'
|
61
|
+
# }
|
62
|
+
|
63
|
+
args = %w{a b --flag --no-switch --list one --list two,three --opt value c}
|
64
|
+
parser.parse(args) # => ['a', 'b', 'c']
|
65
|
+
parser.config
|
66
|
+
# => {
|
67
|
+
# :flag => true,
|
68
|
+
# :switch => false,
|
69
|
+
# :list => ['one', 'two', 'three'],
|
70
|
+
# :opt => 'value'
|
71
|
+
# }
|
56
72
|
|
57
|
-
|
58
|
-
|
59
|
-
are set as configs.
|
73
|
+
Options can be defined using arguments just like on, or with an attributes
|
74
|
+
hash. A block can be given to process values before they are set as configs.
|
60
75
|
|
61
76
|
parser = ConfigParser.new
|
62
|
-
parser.add(:x, nil, '
|
77
|
+
parser.add(:x, nil, '--one', 'by args') {|value| value.upcase }
|
63
78
|
parser.add(:y, nil, :long => 'two', :desc => 'by hash')
|
64
79
|
|
65
|
-
parser.parse('a b --one value --two c')
|
80
|
+
parser.parse('a b --one value --two value c')
|
66
81
|
# => ['a', 'b', 'c']
|
67
82
|
|
68
83
|
parser.config
|
69
|
-
# => {:x => 'VALUE', :y =>
|
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.
|
84
|
+
# => {:x => 'VALUE', :y => 'value'}
|
74
85
|
|
75
86
|
== Installation
|
76
87
|
|
data/lib/config_parser.rb
CHANGED
@@ -1,114 +1,11 @@
|
|
1
1
|
require 'config_parser/list'
|
2
2
|
autoload(:Shellwords, 'shellwords')
|
3
3
|
|
4
|
-
# ConfigParser is the
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
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
|
-
#
|
4
|
+
# ConfigParser is an option parser that formalizes the pattern of setting
|
5
|
+
# parsed options into a hash. ConfigParser provides a similar declaration
|
6
|
+
# syntax as
|
7
|
+
# {OptionParser}[http://www.ruby-doc.org/core/classes/OptionParser.html] but
|
8
|
+
# additionally supports option declaration using an attributes hash.
|
112
9
|
class ConfigParser
|
113
10
|
include Utils
|
114
11
|
|
@@ -120,20 +17,24 @@ class ConfigParser
|
|
120
17
|
# '--long' to the Option that handles them.
|
121
18
|
attr_reader :options
|
122
19
|
|
123
|
-
# The hash receiving
|
20
|
+
# The hash receiving configs.
|
124
21
|
attr_accessor :config
|
125
22
|
|
126
23
|
# The argument to stop processing options
|
127
24
|
attr_accessor :option_break
|
128
25
|
|
129
|
-
# Set to true to preserve the break
|
26
|
+
# Set to true to preserve the option break
|
130
27
|
attr_accessor :preserve_option_break
|
131
28
|
|
29
|
+
# Set to true to assign config defaults on parse
|
30
|
+
attr_accessor :assign_defaults
|
31
|
+
|
132
32
|
# Initializes a new ConfigParser and passes it to the block, if given.
|
133
33
|
def initialize(config={}, opts={})
|
134
34
|
opts = {
|
135
35
|
:option_break => OPTION_BREAK,
|
136
|
-
:preserve_option_break => false
|
36
|
+
:preserve_option_break => false,
|
37
|
+
:assign_defaults => true
|
137
38
|
}.merge(opts)
|
138
39
|
|
139
40
|
@registry = []
|
@@ -141,6 +42,7 @@ class ConfigParser
|
|
141
42
|
@config = config
|
142
43
|
@option_break = opts[:option_break]
|
143
44
|
@preserve_option_break = opts[:preserve_option_break]
|
45
|
+
@assign_defaults = opts[:assign_defaults]
|
144
46
|
|
145
47
|
yield(self) if block_given?
|
146
48
|
end
|
@@ -160,8 +62,9 @@ class ConfigParser
|
|
160
62
|
@registry << str
|
161
63
|
end
|
162
64
|
|
163
|
-
# Registers the option with self by adding
|
164
|
-
#
|
65
|
+
# Registers the option with self by adding it to the registry and mapping
|
66
|
+
# the option flags into options. Raises an error for conflicting flags.
|
67
|
+
# Returns self.
|
165
68
|
#
|
166
69
|
# If override is specified, options with conflicting flags are removed and
|
167
70
|
# no error is raised. Note that this may remove multiple options.
|
@@ -187,102 +90,86 @@ class ConfigParser
|
|
187
90
|
@options[flag] = option
|
188
91
|
end
|
189
92
|
|
190
|
-
|
93
|
+
self
|
94
|
+
end
|
95
|
+
|
96
|
+
# Unregisters the option by removing it from the registry and options.
|
97
|
+
# Returns self.
|
98
|
+
def unregister(option)
|
99
|
+
@registry.delete(option)
|
100
|
+
@options.delete_if {|key, value| option == value }
|
101
|
+
self
|
191
102
|
end
|
192
103
|
|
193
|
-
# Constructs an Option using args and registers it with self.
|
104
|
+
# Constructs an Option using args and registers it with self. The args may
|
194
105
|
# contain (in any order) a short switch, a long switch, and a description
|
195
|
-
# string.
|
196
|
-
#
|
106
|
+
# string. A block may be provided to process values for the option.
|
107
|
+
#
|
108
|
+
# The option type (flag, switch, list, or option) is guessed from the args,
|
109
|
+
# and affects what is passed to the block.
|
197
110
|
#
|
198
111
|
# psr = ConfigParser.new
|
199
112
|
#
|
200
|
-
# #
|
201
|
-
#
|
113
|
+
# # options take an argument on the long
|
114
|
+
# # and receive the arg in the block
|
115
|
+
# psr.on('-s', '--long ARG_NAME', 'description') do |arg|
|
202
116
|
# # ...
|
203
117
|
# end
|
204
118
|
#
|
205
|
-
# #
|
206
|
-
# psr.on('-o ARG_NAME'
|
119
|
+
# # the argname can be specified on a short
|
120
|
+
# psr.on('-o ARG_NAME') do |arg|
|
207
121
|
# # ...
|
208
122
|
# end
|
209
|
-
#
|
210
|
-
# #
|
211
|
-
#
|
123
|
+
#
|
124
|
+
# # use an argname with commas to make a list,
|
125
|
+
# # an array of values is passed to the block
|
126
|
+
# psr.on('--list A,B,C') do |args|
|
212
127
|
# # ...
|
213
128
|
# end
|
214
129
|
#
|
215
|
-
#
|
216
|
-
#
|
217
|
-
#
|
130
|
+
# # flags specify no argument, and the
|
131
|
+
# # block takes no argument
|
132
|
+
# psr.on('-f', '--flag') do
|
133
|
+
# # ...
|
134
|
+
# end
|
218
135
|
#
|
219
|
-
#
|
136
|
+
# # switches look like this; they get true
|
137
|
+
# # or false in the block
|
138
|
+
# psr.on('--[no-]switch') do |bool|
|
220
139
|
# # ...
|
221
140
|
# end
|
222
141
|
#
|
223
|
-
#
|
142
|
+
# If this is too ambiguous (and at times it is), provide a trailing hash
|
143
|
+
# defining all or part of the option:
|
224
144
|
#
|
225
|
-
# psr.on('-k', :long => '--key', :
|
145
|
+
# psr.on('-k', 'description', :long => '--key', :type => :list) do |args|
|
226
146
|
# # ...
|
227
147
|
# end
|
228
|
-
#
|
148
|
+
#
|
149
|
+
# The trailing hash wins if there is any overlap in the parsed attributes
|
150
|
+
# and those provided by the hash.
|
229
151
|
def on(*args, &block)
|
230
|
-
|
152
|
+
option = new_option(args, &block)
|
153
|
+
register option
|
154
|
+
option
|
231
155
|
end
|
232
156
|
|
233
|
-
# Same as on, but overrides options with overlapping
|
157
|
+
# Same as on, but overrides options with overlapping flags.
|
234
158
|
def on!(*args, &block)
|
235
|
-
|
159
|
+
option = new_option(args, &block)
|
160
|
+
register option, true
|
161
|
+
option
|
236
162
|
end
|
237
163
|
|
238
|
-
#
|
239
|
-
#
|
240
|
-
#
|
241
|
-
# the long switch; specify an alternate long, a short, description, etc
|
242
|
-
# using attributes.
|
164
|
+
# An alternate syntax for on, where the key and default attributes are set
|
165
|
+
# by the first two args. Like on, add can define option attributes using a
|
166
|
+
# series of args or with a trailing hash.
|
243
167
|
#
|
244
|
-
#
|
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'}
|
168
|
+
# These are equivalent:
|
250
169
|
#
|
251
|
-
#
|
252
|
-
#
|
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
|
170
|
+
# add(:opt, 'value', '-s', '--long', :desc => 'description')
|
171
|
+
# on('-s', '--long', :desc => 'description', :key => :opt, :default => 'value')
|
277
172
|
#
|
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
173
|
def add(key, default=nil, *args, &block)
|
287
174
|
attrs = args.last.kind_of?(Hash) ? args.pop : {}
|
288
175
|
attrs = attrs.merge(:key => key, :default => default)
|
@@ -291,17 +178,25 @@ class ConfigParser
|
|
291
178
|
on(*args, &block)
|
292
179
|
end
|
293
180
|
|
294
|
-
#
|
295
|
-
#
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
181
|
+
# Removes options by key. Any options with the specified key are removed.
|
182
|
+
# Returns self.
|
183
|
+
def rm(key)
|
184
|
+
options.values.each do |option|
|
185
|
+
if option.key == key
|
186
|
+
unregister(option)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
self
|
191
|
+
end
|
192
|
+
|
193
|
+
# Parses options from argv in a non-destructive manner. Parsing stops if an
|
194
|
+
# argument matching option_break is reached. If preserve_option_break is
|
195
|
+
# specified then the option break is preserved in the remaining arguments.
|
196
|
+
# Returns an array of arguments remaining after options have been removed.
|
304
197
|
#
|
198
|
+
# If a string argv is provided, it will be splits into an array using
|
199
|
+
# Shellwords.
|
305
200
|
def parse(argv=ARGV)
|
306
201
|
argv = argv.dup unless argv.kind_of?(String)
|
307
202
|
parse!(argv)
|
@@ -311,21 +206,20 @@ class ConfigParser
|
|
311
206
|
def parse!(argv=ARGV)
|
312
207
|
argv = Shellwords.shellwords(argv) if argv.kind_of?(String)
|
313
208
|
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
209
|
+
if assign_defaults
|
210
|
+
registry.each do |option|
|
211
|
+
next unless option.respond_to?(:assign)
|
212
|
+
option.assign(config)
|
213
|
+
end
|
214
|
+
end
|
318
215
|
|
319
|
-
|
320
|
-
end
|
321
|
-
|
322
|
-
def scan(argv=ARGV)
|
216
|
+
args = []
|
323
217
|
while !argv.empty?
|
324
218
|
arg = argv.shift
|
325
219
|
|
326
220
|
# determine if the arg is an option
|
327
|
-
unless
|
328
|
-
|
221
|
+
unless option?(arg)
|
222
|
+
args << arg
|
329
223
|
next
|
330
224
|
end
|
331
225
|
|
@@ -357,11 +251,14 @@ class ConfigParser
|
|
357
251
|
option.parse(flag, value, argv, config)
|
358
252
|
end
|
359
253
|
|
254
|
+
args.concat(argv)
|
255
|
+
argv.replace(args)
|
256
|
+
|
360
257
|
argv
|
361
258
|
end
|
362
259
|
|
363
|
-
# Converts the options and separators in self into a help string suitable
|
364
|
-
# display on the command line.
|
260
|
+
# Converts the options and separators in self into a help string suitable
|
261
|
+
# for display on the command line.
|
365
262
|
def to_s
|
366
263
|
@registry.collect do |option|
|
367
264
|
option.to_s.rstrip
|
@@ -387,7 +284,7 @@ class ConfigParser
|
|
387
284
|
# by on and on! to generate options
|
388
285
|
def new_option(argv, &block) # :nodoc:
|
389
286
|
attrs = argv.last.kind_of?(Hash) ? argv.pop : {}
|
390
|
-
attrs =
|
287
|
+
attrs = parse_attrs(argv).merge(attrs)
|
391
288
|
option_class(attrs).new(attrs, &block)
|
392
289
|
end
|
393
290
|
end
|
data/lib/config_parser/flag.rb
CHANGED
@@ -1,12 +1,19 @@
|
|
1
1
|
require 'config_parser/utils'
|
2
2
|
|
3
3
|
class ConfigParser
|
4
|
+
|
5
|
+
# Represents a boolean flag-style option. Flag handles the parsing of
|
6
|
+
# specific flags, and provides hooks for processing the various types of
|
7
|
+
# options (Switch, Option, List).
|
4
8
|
class Flag
|
5
9
|
include Utils
|
6
10
|
|
7
11
|
# The config key
|
8
12
|
attr_reader :key
|
9
13
|
|
14
|
+
# The config nesting keys
|
15
|
+
attr_reader :nest_keys
|
16
|
+
|
10
17
|
# The default value
|
11
18
|
attr_reader :default
|
12
19
|
|
@@ -23,25 +30,30 @@ class ConfigParser
|
|
23
30
|
attr_reader :callback
|
24
31
|
|
25
32
|
def initialize(attrs={}, &callback)
|
26
|
-
@key
|
27
|
-
@
|
28
|
-
@
|
29
|
-
@
|
30
|
-
@
|
31
|
-
@
|
33
|
+
@key = attrs[:key]
|
34
|
+
@nest_keys = attrs[:nest_keys]
|
35
|
+
@default = attrs[:default]
|
36
|
+
@short = shortify(attrs[:short])
|
37
|
+
@long = longify(attrs.has_key?(:long) ? attrs[:long] : key)
|
38
|
+
@desc = attrs[:desc]
|
39
|
+
@callback = callback
|
32
40
|
end
|
33
41
|
|
34
|
-
# Returns an array of
|
42
|
+
# Returns an array of flags mapping to self (ie [long, short]).
|
35
43
|
def flags
|
36
44
|
[long, short].compact
|
37
45
|
end
|
38
46
|
|
39
|
-
#
|
40
|
-
#
|
41
|
-
# the
|
47
|
+
# Parse handles the parsing of flags, which happens in three steps:
|
48
|
+
#
|
49
|
+
# * determine the value (occurs in parse)
|
50
|
+
# * process the value
|
51
|
+
# * assign the result into config
|
42
52
|
#
|
43
|
-
#
|
44
|
-
#
|
53
|
+
# Flag uses !default as the value (such that the flag indicates true if
|
54
|
+
# the default is false) then passes the value to process, and then assign.
|
55
|
+
# Raises and error if provided a value directly (flags always determine
|
56
|
+
# their value based on the default).
|
45
57
|
#
|
46
58
|
#--
|
47
59
|
# Implementation Note
|
@@ -56,7 +68,7 @@ class ConfigParser
|
|
56
68
|
# -xvalue -y
|
57
69
|
# -y -xvalue
|
58
70
|
# -yxvalue
|
59
|
-
#
|
71
|
+
#
|
60
72
|
# Whereas this is not:
|
61
73
|
#
|
62
74
|
# -xyvalue # x receives 'yvalue' not 'value'
|
@@ -65,22 +77,42 @@ class ConfigParser
|
|
65
77
|
# 'xvalue'. Then '-y' determines whether or not it needs a values; if not
|
66
78
|
# '-xvalue' gets unshifted to argv and parsing continues as if '-y
|
67
79
|
# -xvalue' were the original arguments.
|
68
|
-
def parse(flag, value, argv=[], config={})
|
80
|
+
def parse(flag, value=nil, argv=[], config={})
|
69
81
|
unless value.nil?
|
70
82
|
if flag == short
|
71
83
|
argv.unshift "-#{value}"
|
72
84
|
else
|
73
|
-
raise "value specified for flag: #{
|
85
|
+
raise "value specified for #{flag}: #{value.inspect}"
|
74
86
|
end
|
75
87
|
end
|
76
88
|
|
77
|
-
|
89
|
+
value = (default.nil? ? true : !default)
|
90
|
+
assign(config, process(value))
|
78
91
|
end
|
79
92
|
|
80
|
-
#
|
81
|
-
|
82
|
-
|
83
|
-
value
|
93
|
+
# Process the value by calling the callback, if specified, with the value
|
94
|
+
# and returns the result. Returns value if no callback is specified.
|
95
|
+
def process(value)
|
96
|
+
callback ? callback.call(value) : value
|
97
|
+
end
|
98
|
+
|
99
|
+
# Assign the value to the config hash, if key is set. Returns config.
|
100
|
+
def assign(config, value=default)
|
101
|
+
if key
|
102
|
+
nest_config = nest(config)
|
103
|
+
nest_config[key] = value
|
104
|
+
end
|
105
|
+
|
106
|
+
config
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns the nested config hash for config, as specified by nest_keys.
|
110
|
+
def nest(config)
|
111
|
+
nest_keys.each do |key|
|
112
|
+
config = (config[key] ||= {})
|
113
|
+
end if nest_keys
|
114
|
+
|
115
|
+
config
|
84
116
|
end
|
85
117
|
|
86
118
|
# Formats self as a help string for use on the command line.
|
@@ -104,7 +136,6 @@ class ConfigParser
|
|
104
136
|
" #{short_str}#{long_str}"
|
105
137
|
end
|
106
138
|
|
107
|
-
# helper returning short formatted for to_s
|
108
139
|
def short_str # :nodoc:
|
109
140
|
case
|
110
141
|
when short && long then "#{short}, "
|
@@ -113,7 +144,6 @@ class ConfigParser
|
|
113
144
|
end
|
114
145
|
end
|
115
146
|
|
116
|
-
# helper returning long formatted for to_s
|
117
147
|
def long_str # :nodoc:
|
118
148
|
long
|
119
149
|
end
|
data/lib/config_parser/list.rb
CHANGED
@@ -1,38 +1,61 @@
|
|
1
1
|
require 'config_parser/option'
|
2
2
|
|
3
3
|
class ConfigParser
|
4
|
+
|
5
|
+
# List represents a special type of Option where multiple values may be
|
6
|
+
# assigned to the same key.
|
4
7
|
class List < Option
|
5
8
|
|
6
9
|
# The default split character for multiple values
|
7
|
-
|
10
|
+
DELIMITER = ','
|
8
11
|
|
9
|
-
# The maximum number of values that may be specified.
|
12
|
+
# The maximum number of values that may be specified; nil for unlimited.
|
10
13
|
attr_reader :limit
|
11
14
|
|
12
|
-
# The
|
13
|
-
#
|
14
|
-
attr_reader :
|
15
|
+
# The delimiter on which to split single values into multiple values; use
|
16
|
+
# nil to prevent splitting.
|
17
|
+
attr_reader :delimiter
|
15
18
|
|
16
19
|
def initialize(attrs={})
|
17
20
|
super
|
18
|
-
|
19
|
-
@
|
21
|
+
|
22
|
+
@delimiter = attrs.has_key?(:delimiter) ? attrs[:delimiter] : DELIMITER
|
23
|
+
@limit = attrs[:limit]
|
24
|
+
@default = split(@default)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Splits the value into multiple values, and then process as usual.
|
28
|
+
def process(value)
|
29
|
+
super split(value)
|
20
30
|
end
|
21
31
|
|
22
|
-
#
|
23
|
-
# directly setting
|
24
|
-
#
|
25
|
-
def assign(config,
|
32
|
+
# Assigns the values to config by concatenating onto an array, rather than
|
33
|
+
# directly setting into config. As usual, no value is assigned if key is
|
34
|
+
# not set.
|
35
|
+
def assign(config, values=default)
|
26
36
|
if key
|
27
|
-
|
28
|
-
array
|
37
|
+
nest_config = nest(config)
|
38
|
+
array = (nest_config[key] ||= [])
|
39
|
+
array.concat(values)
|
29
40
|
|
30
41
|
if limit && array.length > limit
|
31
|
-
raise "too many assignments
|
42
|
+
raise "too many assignments for #{key.inspect}"
|
32
43
|
end
|
33
44
|
end
|
34
45
|
|
35
|
-
|
46
|
+
config
|
47
|
+
end
|
48
|
+
|
49
|
+
# Splits string values along the delimiter, if specified. Returns array
|
50
|
+
# values directly, and an empty array for nil. All other values are
|
51
|
+
# arrayified like [obj].
|
52
|
+
def split(obj)
|
53
|
+
case obj
|
54
|
+
when Array then obj
|
55
|
+
when String then delimiter ? obj.split(delimiter) : [obj]
|
56
|
+
when nil then []
|
57
|
+
else [obj]
|
58
|
+
end
|
36
59
|
end
|
37
60
|
end
|
38
61
|
end
|
data/lib/config_parser/option.rb
CHANGED
@@ -2,37 +2,46 @@ require 'config_parser/switch'
|
|
2
2
|
|
3
3
|
class ConfigParser
|
4
4
|
|
5
|
-
#
|
5
|
+
# An Option represents a Flag that takes a value.
|
6
6
|
class Option < Flag
|
7
|
+
|
8
|
+
# The default argument name
|
7
9
|
DEFAULT_ARGNAME = 'VALUE'
|
8
10
|
|
11
|
+
# Matches optional argnames
|
12
|
+
OPTIONAL = /\A\[.*\]\z/
|
13
|
+
|
9
14
|
# The argument name printed by to_s.
|
10
15
|
attr_reader :arg_name
|
11
16
|
|
17
|
+
# Set to true to make the argument optional
|
18
|
+
attr_reader :optional
|
19
|
+
|
12
20
|
def initialize(attrs={})
|
13
21
|
super
|
14
22
|
@arg_name = attrs[:arg_name] || (key ? key.to_s.upcase : DEFAULT_ARGNAME)
|
23
|
+
@optional = (attrs.has_key?(:optional) ? attrs[:optional] : (arg_name =~ OPTIONAL ? true : false))
|
15
24
|
end
|
16
25
|
|
17
26
|
# Parse the flag and value. If no value is provided and a value is
|
18
|
-
# required, then a value is shifted off of argv.
|
19
|
-
#
|
20
|
-
def parse(flag, value, argv=[], config={})
|
27
|
+
# required, then a value is shifted off of argv. The value is then
|
28
|
+
# processed and assigned into config.
|
29
|
+
def parse(flag, value=nil, argv=[], config={})
|
21
30
|
if value.nil?
|
22
31
|
unless value = next_arg(argv)
|
23
|
-
|
32
|
+
if optional
|
33
|
+
value = default
|
34
|
+
else
|
35
|
+
raise "no value provided for: #{flag}"
|
36
|
+
end
|
24
37
|
end
|
25
38
|
end
|
26
|
-
|
39
|
+
|
40
|
+
assign(config, process(value))
|
27
41
|
end
|
28
42
|
|
29
43
|
private
|
30
44
|
|
31
|
-
def next_arg(argv) # :nodoc:
|
32
|
-
arg = argv[0]
|
33
|
-
(arg.kind_of?(String) && arg[0] == ?-) ? default : argv.shift
|
34
|
-
end
|
35
|
-
|
36
45
|
def header_str # :nodoc:
|
37
46
|
" #{short_str}#{long_str} #{arg_name}"
|
38
47
|
end
|
data/lib/config_parser/switch.rb
CHANGED
@@ -6,38 +6,40 @@ class ConfigParser
|
|
6
6
|
# and negative (--no-flag) flags map to self.
|
7
7
|
class Switch < Flag
|
8
8
|
|
9
|
-
# The negative
|
10
|
-
attr_reader :
|
9
|
+
# The negative mapping prefix, defaults to 'no'
|
10
|
+
attr_reader :prefix
|
11
|
+
|
12
|
+
# The negative long flag, determined from long and prefix.
|
13
|
+
attr_reader :nolong
|
11
14
|
|
12
15
|
def initialize(attrs={})
|
13
16
|
attrs[:default] = true unless attrs.has_key?(:default)
|
14
17
|
super
|
15
18
|
|
16
19
|
raise ArgumentError, "no long specified" unless long
|
17
|
-
@
|
20
|
+
@prefix = attrs[:prefix] || 'no'
|
21
|
+
@nolong = prefix_long(long, "#{prefix}-")
|
18
22
|
end
|
19
23
|
|
20
|
-
# Returns an array of
|
21
|
-
# negative_long, short]).
|
24
|
+
# Returns an array of flags mapping to self (ie [long, nolong, short]).
|
22
25
|
def flags
|
23
|
-
[long,
|
26
|
+
[long, nolong, short].compact
|
24
27
|
end
|
25
28
|
|
26
|
-
# Assigns
|
27
|
-
# flags.
|
28
|
-
#
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
value = (flag ==
|
33
|
-
assign(config,
|
29
|
+
# Assigns default into config for positive flags and !default for negative
|
30
|
+
# flags. The boolean value is then processed and assigned into config.
|
31
|
+
# Raises an error if a value is provided (switches take none).
|
32
|
+
def parse(flag, value=nil, argv=[], config={})
|
33
|
+
raise "value specified for #{flag}: #{value.inspect}" if value
|
34
|
+
|
35
|
+
value = (flag == nolong ? !default : default)
|
36
|
+
assign(config, process(value))
|
34
37
|
end
|
35
38
|
|
36
39
|
private
|
37
40
|
|
38
|
-
# helper returning long formatted for to_s
|
39
41
|
def long_str # :nodoc:
|
40
|
-
|
42
|
+
prefix_long(long, "[#{prefix}-]")
|
41
43
|
end
|
42
44
|
end
|
43
45
|
end
|
data/lib/config_parser/utils.rb
CHANGED
@@ -10,19 +10,30 @@ class ConfigParser
|
|
10
10
|
# The default option break
|
11
11
|
OPTION_BREAK = "--"
|
12
12
|
|
13
|
+
# Matches an option (long or short)
|
14
|
+
OPTION = /\A-./
|
15
|
+
|
13
16
|
# Matches a long flag
|
14
17
|
LONG_FLAG = /\A--.+\z/
|
15
18
|
|
16
19
|
# Matches a short flag
|
17
20
|
SHORT_FLAG = /\A-.\z/
|
18
21
|
|
19
|
-
# Matches a switch
|
20
|
-
#
|
21
|
-
#
|
22
|
+
# Matches a switch option (ex: '--[no-]opt', '--nest:[no-]opt'). After the
|
23
|
+
# match:
|
24
|
+
#
|
22
25
|
# $1:: the nesting prefix ('nest')
|
23
|
-
# $2:: the
|
26
|
+
# $2:: the nolong prefix ('no')
|
27
|
+
# $3:: the long flag name ('opt')
|
24
28
|
#
|
25
|
-
SWITCH = /\A--(.*?)\[
|
29
|
+
SWITCH = /\A--(.*?)\[(.*?)-\](.+)\z/
|
30
|
+
|
31
|
+
# Matches a nest option (ex: '--nest:opt'). After the match:
|
32
|
+
#
|
33
|
+
# $1:: the nesting prefix ('nest')
|
34
|
+
# $2:: the long option ('long')
|
35
|
+
#
|
36
|
+
NEST = /\A--(.*):(.+)\z/
|
26
37
|
|
27
38
|
# Turns the input into a short flag by prefixing '-' (as needed). Raises
|
28
39
|
# an error if the input doesn't result in a short flag. Nils are
|
@@ -35,7 +46,7 @@ class ConfigParser
|
|
35
46
|
return nil if str.nil?
|
36
47
|
|
37
48
|
str = str.to_s
|
38
|
-
str = "-#{str}" unless str
|
49
|
+
str = "-#{str}" unless option?(str)
|
39
50
|
|
40
51
|
unless str =~ SHORT_FLAG
|
41
52
|
raise ArgumentError, "invalid short flag: #{str}"
|
@@ -55,7 +66,7 @@ class ConfigParser
|
|
55
66
|
return nil if str.nil?
|
56
67
|
|
57
68
|
str = str.to_s
|
58
|
-
str = "--#{str}" unless str
|
69
|
+
str = "--#{str}" unless option?(str)
|
59
70
|
|
60
71
|
unless str =~ LONG_FLAG
|
61
72
|
raise ArgumentError, "invalid long flag: #{str}"
|
@@ -76,6 +87,17 @@ class ConfigParser
|
|
76
87
|
"--#{switch.join(':')}"
|
77
88
|
end
|
78
89
|
|
90
|
+
# Returns true if the object is a string and matches OPTION.
|
91
|
+
def option?(obj)
|
92
|
+
obj.kind_of?(String) && obj =~ OPTION ? true : false
|
93
|
+
end
|
94
|
+
|
95
|
+
# Shifts and returns the first argument off of argv if it is an argument
|
96
|
+
# (rather than an option) or returns the default value.
|
97
|
+
def next_arg(argv)
|
98
|
+
option?(argv.at(0)) ? nil : argv.shift
|
99
|
+
end
|
100
|
+
|
79
101
|
# A wrapping algorithm slightly modified from:
|
80
102
|
# http://blog.macromates.com/2006/wrapping-text-with-regular-expressions/
|
81
103
|
def wrap(line, cols=80, tabsize=2)
|
@@ -83,16 +105,39 @@ class ConfigParser
|
|
83
105
|
line.gsub(/(.{1,#{cols}})( +|$\r?\n?)|(.{1,#{cols}})/, "\\1\\3\n").split(/\s*?\n/)
|
84
106
|
end
|
85
107
|
|
108
|
+
# Parses the argv into an attributes hash for initializing an option.
|
109
|
+
# Heuristics are used to infer what an argument implies.
|
110
|
+
#
|
111
|
+
# Argument Implies
|
112
|
+
# -s :short => '-s'
|
113
|
+
# --long :long => '--long'
|
114
|
+
# --long ARG :long => '--long', :arg_name => 'ARG'
|
115
|
+
# --[no-]long :long => '--long', :prefix => 'no', :type => :switch
|
116
|
+
# --nest:long :long => '--nest:long', :nest_keys => ['nest']
|
117
|
+
# 'some string' :desc => 'some string'
|
118
|
+
#
|
119
|
+
# Usually you overlay these patterns, for example:
|
120
|
+
#
|
121
|
+
# -s ARG :short => '-s', :arg_name => 'ARG'
|
122
|
+
# --nest:[no-]long :long => '--nest:long', :nest_keys => ['nest'], :prefix => 'no', :type => :switch
|
123
|
+
#
|
124
|
+
# The goal of this method is to get things right most of the time, not to
|
125
|
+
# be clean, simple, or robust. Some errors in declarations (like an
|
126
|
+
# arg_name with a switch) can be detected... others not so much.
|
86
127
|
def parse_attrs(argv)
|
87
128
|
attrs={}
|
88
129
|
|
89
130
|
argv.each do |arg|
|
90
|
-
|
131
|
+
unless option?(arg)
|
91
132
|
attrs[:desc] = arg
|
92
133
|
next
|
93
134
|
end
|
94
135
|
|
95
136
|
flag, arg_name = arg.split(/\s+/, 2)
|
137
|
+
|
138
|
+
if flag =~ NEST
|
139
|
+
attrs[:nest_keys] = $1.split(':')
|
140
|
+
end
|
96
141
|
|
97
142
|
if arg_name
|
98
143
|
attrs[:arg_name] = arg_name
|
@@ -100,9 +145,9 @@ class ConfigParser
|
|
100
145
|
|
101
146
|
case flag
|
102
147
|
when SWITCH
|
103
|
-
attrs[:long] = "--#{$1}#{$
|
104
|
-
attrs[:
|
105
|
-
|
148
|
+
attrs[:long] = "--#{$1}#{$3}"
|
149
|
+
attrs[:prefix] = $2
|
150
|
+
|
106
151
|
if arg_name
|
107
152
|
raise ArgumentError, "arg_name specified for switch: #{arg_name}"
|
108
153
|
end
|
@@ -121,15 +166,49 @@ class ConfigParser
|
|
121
166
|
attrs
|
122
167
|
end
|
123
168
|
|
124
|
-
|
169
|
+
# Guesses an option type based on the attrs.
|
170
|
+
#
|
171
|
+
# if... then...
|
172
|
+
# prefix => :switch
|
173
|
+
# arg_name => guess_type_by_arg_name || guess_type_by_value
|
174
|
+
# default => (guess_type_by_value)
|
175
|
+
# all else => :flag
|
176
|
+
#
|
177
|
+
# A guess is just a guess; for certainty specify the type manually.
|
178
|
+
def guess_type(attrs)
|
125
179
|
case
|
126
|
-
when attrs
|
180
|
+
when attrs.has_key?(:prefix)
|
127
181
|
:switch
|
128
|
-
when attrs
|
129
|
-
:
|
182
|
+
when attrs.has_key?(:arg_name)
|
183
|
+
guess_type_by_arg_name(attrs[:arg_name]) || guess_type_by_value(attrs[:default])
|
184
|
+
when attrs.has_key?(:default)
|
185
|
+
guess_type_by_value(attrs[:default])
|
130
186
|
else
|
131
187
|
:flag
|
132
188
|
end
|
133
189
|
end
|
190
|
+
|
191
|
+
# Guesses :list if the arg_name has a comma, or nil.
|
192
|
+
def guess_type_by_arg_name(arg_name)
|
193
|
+
arg_name.to_s.include?(',') ? :list : nil
|
194
|
+
end
|
195
|
+
|
196
|
+
# Guesses an option type based on a value.
|
197
|
+
#
|
198
|
+
# if... then...
|
199
|
+
# true => :switch
|
200
|
+
# false => :flag
|
201
|
+
# Array => :list
|
202
|
+
# all else => :option
|
203
|
+
#
|
204
|
+
# A guess is just a guess; for certainty specify the type manually.
|
205
|
+
def guess_type_by_value(value)
|
206
|
+
case value
|
207
|
+
when true then :switch
|
208
|
+
when false then :flag
|
209
|
+
when Array then :list
|
210
|
+
else :option
|
211
|
+
end
|
212
|
+
end
|
134
213
|
end
|
135
214
|
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
7
|
+
- 2
|
8
8
|
- 0
|
9
|
-
version: 0.
|
9
|
+
version: 0.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Simon Chiang
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-08-01 00:00:00 -06:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|