arg-parser 0.1

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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013, Adam Gardiner
2
+ All rights reserved.
3
+
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions are met:
6
+
7
+ * Redistributions of source code must retain the above copyright notice, this
8
+ list of conditions and the following disclaimer.
9
+ * Redistributions in binary form must reproduce the above copyright notice,
10
+ this list of conditions and the following disclaimer in the documentation
11
+ and/or other materials provided with the distribution.
12
+
13
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
17
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,80 @@
1
+ # ArgParser
2
+
3
+ ArgParser is a small library for parsing command-line arguments (or indeed any string or Array of text).
4
+ It provides a simple DSL for defining the possible arguments, which may be one of the following:
5
+ * Positional arguments, where values are specified without any keyword, and their meaning is based on the
6
+ order in which they appear in the command-line.
7
+ * Keyword arguments, which are identified by a long- or short-key preceding the value.
8
+ * Flag arguments, which are essentially boolean keyword arguments. The presence of the key implies a true
9
+ value.
10
+ * A Rest argument, which is an argument definition that takes 0 or more trailing positional arguments.
11
+
12
+ ## Usage
13
+
14
+ ArgParser is supplied as a gem, and has no dependencies. To use it, simply:
15
+ ```
16
+ gem install arg-parser
17
+ ```
18
+
19
+ ArgParser provides a DSL module that can be included into any class to provide argument parsing.
20
+
21
+ ```ruby
22
+ require 'arg-parser'
23
+
24
+ class MyClass
25
+
26
+ include ArgParser::DSL
27
+
28
+ purpose <<-EOT
29
+ This is where you specify the purpose of your program. It will be displayed in the
30
+ generated help screen if you pass /? or --help on the command-line.
31
+ EOT
32
+
33
+ positional_arg :my_positional_arg, 'This is a positional arg'
34
+ keyword_arg :my_keyword_arg, 'This is a keyword arg'
35
+ flag_arg :flag, 'This is a flag argument'
36
+ rest_arg :files, 'This is where we specify that remaining args will be collected in an array'
37
+
38
+
39
+ def run
40
+ if opts = parse_arguments
41
+ # Do something with opts.my_positional_arg, opts.my_keyword_arg,
42
+ # opts.flag, and opts.files
43
+ # ...
44
+ else
45
+ show_help? ? show_help : show_usage
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+
52
+ MyClass.new.run
53
+
54
+ ```
55
+
56
+ ## Functionality
57
+
58
+ ArgParser provides a fairly broad range of functionality for argument parsing. Following is a non-exhaustive
59
+ list of features:
60
+ * Built-in usage and help display
61
+ * Mandatory vs Optional: All arguments your program accepts can be defined as optional or mandatory.
62
+ By default, positional arguments are considered mandatory, and all others default to optional. To change
63
+ the default, simply specify `required: true` or `required: false` when defining the argument.
64
+ * Short-keys and long-keys: Arguments are defined with a long_key name which will be used to access the
65
+ parsed value in the results. However, arguments can also define a single letter or digit short-key form
66
+ which can be used as an alternate means for indicating a value. To define a short key, simply pass
67
+ `short_key: '<letter_or_digit>'` when defining an argument.
68
+ * Validation: Arguments can define validation requirements that must be satisfied. This can take several
69
+ forms:
70
+ - List of values: Pass an array containing the allowed values the argument can take.
71
+ `validation: %w{one two three}`
72
+ - Regular expression: Pass a regular expression that the argument value must satisfy.
73
+ `validation: /.*\.rb$/`
74
+ - Proc: Pass a proc that will be called to validate the supplied argument value. If the proc returns
75
+ a non-falsey value, the argument is accepted, otherwise it is rejected.
76
+ `validation: lambda{ |val, arg, hsh| val.upcase == 'TRUE' }`
77
+ * On-parse handler: A proc can be passed that will be called when the argument value is encountered
78
+ during parsing. The return value of the proc will be used as the argument result.
79
+ `on_parse: lambda{ |val, arg, hsh| val.split(',') }`
80
+
@@ -0,0 +1,5 @@
1
+ require 'arg-parser/argument'
2
+ require 'arg-parser/definition'
3
+ require 'arg-parser/parser'
4
+ require 'arg-parser/dsl'
5
+
@@ -0,0 +1,332 @@
1
+ # Namespace for classes defined by ArgParser, the command-line argument parser.
2
+ module ArgParser
3
+
4
+ # Hash containing registered handlers for :on_parse options
5
+ OnParseHandlers = {
6
+ :split_to_array => lambda{ |val, arg, hsh| val.split(',') }
7
+ }
8
+
9
+
10
+ # Abstract base class of all command-line argument types.
11
+ #
12
+ # @abstract
13
+ class Argument
14
+
15
+ # The key used to identify this argument value in the parsed command-
16
+ # line results Struct.
17
+ # @return [Symbol] the key/method by which this argument can be retrieved
18
+ # from the parse result Struct.
19
+ attr_reader :key
20
+ # @return [String] the description for this argument, which will be shown
21
+ # in the usage display.
22
+ attr_reader :description
23
+ # @return [Symbol] a single letter or digit that can be used as a short
24
+ # alternative to the full key to identify an argument value in a command-
25
+ # line.
26
+ attr_reader :short_key
27
+ # @return [Boolean] whether this argument is a required (i.e. mandatory)
28
+ # argument. Mandatory arguments that do not get specified result in a
29
+ # ParseException.
30
+ attr_accessor :required
31
+ alias_method :required?, :required
32
+ # @return [String] the default value for the argument, returned in the
33
+ # command-line parse results if no other value is specified.
34
+ attr_accessor :default
35
+ # An optional on_parse callback handler. The supplied block/Proc will be
36
+ # called after this argument has been parsed, with three arguments:
37
+ # @param [String] The value from the command-line that was entered for
38
+ # this argument.
39
+ # @param [Argument] The Argument sub-class object that represents the
40
+ # argument that was parsed.
41
+ # @param [Hash] The results Hash containing the argument keys and their
42
+ # values parsed so far.
43
+ # @return [Proc] the user supplied block to be called when the argument
44
+ # has been parsed.
45
+ attr_accessor :on_parse
46
+ # @return [String] a label to use for a new section of options in the
47
+ # argument usage display. Should be specified on the first argument in
48
+ # the group.
49
+ attr_accessor :usage_break
50
+
51
+
52
+ # Converts an argument key specification into a valid key, by stripping
53
+ # leading dashes, converting remaining dashes to underscores, and lower-
54
+ # casing all text. This is required to ensure the key name will be a
55
+ # valid accessor name on the parse results.
56
+ #
57
+ # @return [Symbol] the key by which an argument can be retrieved from
58
+ # the arguments definition, and the parse results.
59
+ def self.to_key(label)
60
+ label.to_s.gsub(/^-+/, '').gsub('-', '_').downcase.intern
61
+ end
62
+
63
+
64
+ private
65
+
66
+ def initialize(key, desc, opts = {}, &block)
67
+ @key = self.class.to_key(key)
68
+ @description = desc
69
+ @default = opts[:default]
70
+ @on_parse = block || opts[:on_parse]
71
+ if @on_parse.is_a?(Symbol)
72
+ op = opts[:on_parse]
73
+ @on_parse = case
74
+ when OnParseHandlers.has_key?(op)
75
+ OnParseHandlers[op]
76
+ when "".respond_to?(op)
77
+ lambda{ |val, arg, hsh| val.send(op) }
78
+ else
79
+ raise ArgumentError, "No on_parse handler registered for #{op.inspect}"
80
+ end
81
+ end
82
+ @usage_break = opts[:usage_break]
83
+ if sk = opts[:short_key]
84
+ if sk =~ /^-?([a-z0-9])$/i
85
+ @short_key = $1.intern
86
+ else
87
+ raise ArgumentError, "An argument short key must be a single digit or letter"
88
+ end
89
+ end
90
+ end
91
+
92
+ end
93
+
94
+
95
+ # Abstract base class of arguments that take a value (i.e. positional and
96
+ # keyword arguments).
97
+ #
98
+ # @abstract
99
+ class ValueArgument < Argument
100
+
101
+ # @return [Boolean] Flag indicating that the value for this argument is
102
+ # a sensitive value (e.g. a password) that should not be displayed.
103
+ attr_accessor :sensitive
104
+ alias_method :sensitive?, :sensitive
105
+ # @return [Array, Regexp, Proc] An optional validation that will be
106
+ # applied to the argument value for this argument, to determine if it
107
+ # is valid. The validation can take the following forms:
108
+ # @param [Array] If an Array is specified, the supplied value will be
109
+ # checked to verify it is one of the allowed values specified in the
110
+ # Array.
111
+ # @param [Regexp] If a Regexp object is supplied, the argument value
112
+ # will be tested against the Regexp to verify it is valid.
113
+ # @param [Proc] The most flexible option; the supplied value, this
114
+ # ValueArgument sub-class object, and the parse results (thus far)
115
+ # will be passed to the Proc for validation. The Proc must return a
116
+ # non-falsey value for the argument to be accepted.
117
+ attr_accessor :validation
118
+ # @return [String] A label that will be used in the usage string printed
119
+ # for this ValueArgument. If not specified, defaults to the upper-case
120
+ # value of the argument key. For example, if the argument key is :foo_bar,
121
+ # the default usage value for this argument would be FOO-BAR, as in:
122
+ # Usage:
123
+ # my-prog.rb FOO-BAR
124
+ attr_accessor :usage_value
125
+
126
+
127
+ private
128
+
129
+ def initialize(key, desc, opts = {}, &block)
130
+ super
131
+ @sensitive = opts[:sensitive]
132
+ @validation = opts[:validation]
133
+ @usage_value = opts.fetch(:usage_value, key.to_s.gsub('_', '-').upcase)
134
+ end
135
+
136
+ end
137
+
138
+
139
+ # An argument that is set by position on the command-line. PositionalArguments
140
+ # do not require a --key to be specified before the argument value; they are
141
+ # typically used when there are a small number of mandatory arguments.
142
+ #
143
+ # Positional arguments still have a key that is used to identify the parsed
144
+ # argument value in the results Struct. As such, it is not an error for a
145
+ # positional argument to be specified with its key - its just not mandatory
146
+ # for the key to be provided.
147
+ class PositionalArgument < ValueArgument
148
+
149
+ # Creates a new positional argument, which is an argument value that may
150
+ # be specified without a keyword, in which case it is matched to the
151
+ # available positional arguments by its position on the command-line.
152
+ #
153
+ # @param [Symbol] key The name that will be used for the accessor used
154
+ # to return this argument value in the parse results.
155
+ # @param [String] desc A description for this argument. Appears in the
156
+ # help output that is generated when the user specifies the --help or
157
+ # /? flags on the command-line.
158
+ # @param [Hash] opts Contains any options that are desired for this
159
+ # argument.
160
+ # @yield [val, arg, hsh] If supplied, the block passed will be invoked
161
+ # after this argument value has been parsed from the command-line.
162
+ # Blocks are usually used when the value to be returned needs to be
163
+ # converted from a String to some other type.
164
+ # @yieldparam val [String] the String value read from the command-line
165
+ # for this argument
166
+ # @yieldparam arg [PositionalArgument] this argument definition
167
+ # @yieldparam hsh [Hash] a Hash containing the argument values parsed
168
+ # so far.
169
+ # @yieldreturn [Object] the return value from the block will be used as
170
+ # the argument value parsed from the command-line for this argument.
171
+ def initialize(key, desc, opts = {}, &block)
172
+ super
173
+ @required = opts.fetch(:required, !opts.has_key?(:default))
174
+ end
175
+
176
+ # @return [String] the word that will appear in the help display for
177
+ # this argument.
178
+ def to_s
179
+ usage_value
180
+ end
181
+
182
+ # @return [String] the string for this argument position in a command-line
183
+ # usage display.
184
+ def to_use
185
+ required? ? usage_value : "[#{usage_value}]"
186
+ end
187
+
188
+ end
189
+
190
+
191
+ # An argument that is specified via a keyword prefix; typically used
192
+ # for optional arguments, although Keyword arguments can also be used for
193
+ # mandatory arguments where there is no natural ordering of arguments.
194
+ class KeywordArgument < ValueArgument
195
+
196
+ # Whether the keyword argument must be specified with a non-missing
197
+ # value.
198
+ # @return [Boolean] true if the keyword can be specified without a value.
199
+ attr_accessor :value_optional
200
+ alias_method :value_optional?, :value_optional
201
+
202
+
203
+ # Creates a KeywordArgument, which is an argument that must be specified
204
+ # on a command-line using either a long form key (i.e. --key), or
205
+ # optionally, a short-form key (i.e. -k) should one be defined for this
206
+ # argument.
207
+ # @param key [Symbol] the key that will be used to identify this argument
208
+ # value in the parse results.
209
+ # @param desc [String] the description of this argument, displayed in the
210
+ # generated help screen.
211
+ # @param opts [Hash] a hash of options that govern the behaviour of this
212
+ # argument.
213
+ # @option opts [Boolean] :required whether the keyword argument is a required
214
+ # argument that must appear in the command-line. Defaults to false.
215
+ # @option opts [Boolean] :value_optional whether the keyword argument can be
216
+ # specified without a value. For example, a keyword argument might
217
+ # be used both as a flag, and to override a default value. Specifying
218
+ # the argument without a value would signify that the option is set,
219
+ # but the default value for the option should be used. Defaults to
220
+ # false (keyword argument cannot be specified without a value).
221
+ def initialize(key, desc, opts = {}, &block)
222
+ super
223
+ @required = opts.fetch(:required, false)
224
+ @value_optional = opts.fetch(:value_optional, false)
225
+ end
226
+
227
+ def to_s
228
+ "--#{key}".gsub('_', '-')
229
+ end
230
+
231
+ def to_use
232
+ sk = short_key ? "-#{short_key}, " : ''
233
+ uv = value_optional ? "[#{usage_value}]" : usage_value
234
+ "#{sk}#{self.to_s} #{uv}"
235
+ end
236
+
237
+ end
238
+
239
+
240
+ # A boolean argument that is set if its key is encountered on the command-line.
241
+ # Flag arguments normally default to false, and become true if the argument
242
+ # key is specified. However, it is also possible to define a flag argument
243
+ # that defaults to true, in which case the option can be disabled by pre-
244
+ # pending the argument key with a 'no-' prefix, e.g. --no-export can be
245
+ # specified to disable the normally enabled --export flag.
246
+ class FlagArgument < Argument
247
+
248
+ # Creates a new flag argument, which is an argument with a boolean value.
249
+ #
250
+ # @param [Symbol] key The name that will be used for the accessor used
251
+ # to return this argument value in the parse results.
252
+ # @param [String] desc A description for this argument. Appears in the
253
+ # help output that is generated when the user specifies the --help or
254
+ # /? flags on the command-line.
255
+ # @param [Hash] opts Contains any options that are desired for this
256
+ # argument.
257
+ # @param [Block] block If supplied, the block passed will be invoked
258
+ # after this argument value has been parsed from the command-line.
259
+ # The block will be called with three arguments: this argument
260
+ # definition, the String value read from the command-line for this
261
+ # argument, and a Hash containing the argument values parsed so far.
262
+ # The return value from the block will be used as the argument value
263
+ # parsed from the command-line for this argument. Blocks are usually
264
+ # used when the value to be returned needs to be converted from a
265
+ # String to some other type.
266
+ def initialize(key, desc, opts = {}, &block)
267
+ super
268
+ end
269
+
270
+ def required
271
+ false
272
+ end
273
+
274
+ def to_s
275
+ "--#{self.default ? 'no-' : ''}#{key}".gsub('_', '-')
276
+ end
277
+
278
+ def to_use
279
+ sk = short_key ? "-#{short_key}, " : ''
280
+ "#{sk}#{self.to_s}"
281
+ end
282
+
283
+ end
284
+
285
+
286
+ # A command-line argument that takes 0 to N values from the command-line.
287
+ class RestArgument < ValueArgument
288
+
289
+ # Creates a new rest argument, which is an argument that consumes all
290
+ # remaining positional argument values.
291
+ #
292
+ # @param [Symbol] key The name that will be used for the accessor used
293
+ # to return this argument value in the parse results.
294
+ # @param [String] desc A description for this argument. Appears in the
295
+ # help output that is generated when the user specifies the --help or
296
+ # /? flags on the command-line.
297
+ # @param [Hash] opts Contains any options that are desired for this
298
+ # argument.
299
+ # @param [Block] block If supplied, the block passed will be invoked
300
+ # after this argument value has been parsed from the command-line.
301
+ # The block will be called with three arguments: this argument
302
+ # definition, the String value read from the command-line for this
303
+ # argument, and a Hash containing the argument values parsed so far.
304
+ # The return value from the block will be used as the argument value
305
+ # parsed from the command-line for this argument. Blocks are usually
306
+ # used when the value to be returned needs to be converted from a
307
+ # String to some other type.
308
+ def initialize(key, desc, opts = {}, &block)
309
+ super
310
+ @min_values = opts.fetch(:min_values, opts.fetch(:required, true) ? 1 : 0)
311
+ end
312
+
313
+ def required
314
+ @min_values > 0
315
+ end
316
+
317
+ # @return [String] the word that will appear in the help display for
318
+ # this argument.
319
+ def to_s
320
+ usage_value
321
+ end
322
+
323
+ # @return [String] The string for this argument position in a command-line.
324
+ # usage display.
325
+ def to_use
326
+ required? ? "#{usage_value} [...]" : "[#{usage_value} [...]]"
327
+ end
328
+
329
+ end
330
+
331
+ end
332
+
@@ -0,0 +1,422 @@
1
+ module ArgParser
2
+
3
+ # Exeption thrown when an attempt is made to access an argument that is not
4
+ # defined.
5
+ class NoSuchArgumentError < RuntimeError; end
6
+
7
+
8
+ # Represents the collection of possible command-line arguments for a script.
9
+ class Definition
10
+
11
+ # @return [String] A title for the script, displayed at the top of the
12
+ # usage and help outputs.
13
+ attr_accessor :title
14
+ # @return [String] A short description of the purpose of the script, for
15
+ # display when showing the usage help.
16
+ attr_accessor :purpose
17
+
18
+
19
+ # Create a new Definition, which is a collection of valid Arguments to
20
+ # be used when parsing a command-line.
21
+ def initialize
22
+ @arguments = {}
23
+ @short_keys = {}
24
+ @require_set = Hash.new{ |h,k| h[k] = [] }
25
+ @title = $0.respond_to?(:titleize) ? $0.titleize : $0
26
+ yield self if block_given?
27
+ end
28
+
29
+
30
+ # @return [Argument] the argument for the given key if it exists, or nil
31
+ # if it does not.
32
+ def has_key?(key)
33
+ k = Argument.to_key(key)
34
+ arg = @arguments[k] || @short_keys[k]
35
+ end
36
+
37
+
38
+ # @return [Argument] the argument with the specified key
39
+ # @raise [ArgumentError] if no argument has been defined with the
40
+ # specified key.
41
+ def [](key)
42
+ arg = has_key?(key)
43
+ arg or raise NoSuchArgumentError, "No argument defined for key '#{Argument.to_key(key)}'"
44
+ end
45
+
46
+
47
+ # Adds the specified argument to the command-line definition.
48
+ #
49
+ # @param arg [Argument] An Argument sub-class to be added to the command-
50
+ # line definition.
51
+ def <<(arg)
52
+ case arg
53
+ when PositionalArgument, KeywordArgument, FlagArgument, RestArgument
54
+ if @arguments[arg.key]
55
+ raise ArgumentError, "An argument with key '#{arg.key}' has already been defined"
56
+ end
57
+ if arg.short_key && @short_keys[arg.short_key]
58
+ raise ArgumentError, "An argument with short key '#{arg.short_key}' has already been defined"
59
+ end
60
+ if rest_args
61
+ raise ArgumentError, "Only one rest argument can be defined"
62
+ end
63
+ @arguments[arg.key] = arg
64
+ @short_keys[arg.short_key] = arg if arg.short_key
65
+ else
66
+ raise ArgumentError, "arg must be an instance of PositionalArgument, KeywordArgument, " +
67
+ "FlagArgument or RestArgument"
68
+ end
69
+ end
70
+
71
+
72
+ # Add a positional argument to the set of arguments in this command-line
73
+ # argument definition.
74
+ # @see PositionalArgument#initialize
75
+ def positional_arg(key, desc, opts = {}, &block)
76
+ self << ArgParser::PositionalArgument.new(key, desc, opts, &block)
77
+ end
78
+
79
+
80
+ # Add a keyword argument to the set of arguments in this command-line
81
+ # argument definition.
82
+ # @see KeywordArgument#initialize
83
+ def keyword_arg(key, desc, opts = {}, &block)
84
+ self << ArgParser::KeywordArgument.new(key, desc, opts, &block)
85
+ end
86
+
87
+
88
+ # Add a flag argument to the set of arguments in this command-line
89
+ # argument definition.
90
+ # @see FlagArgument#initialize
91
+ def flag_arg(key, desc, opts = {}, &block)
92
+ self << ArgParser::FlagArgument.new(key, desc, opts, &block)
93
+ end
94
+
95
+
96
+ # Add a rest argument to the set of arguments in this command-line
97
+ # argument definition.
98
+ # @see RestArgument#initialize
99
+ def rest_arg(key, desc, opts = {}, &block)
100
+ self << ArgParser::RestArgument.new(key, desc, opts, &block)
101
+ end
102
+
103
+
104
+ # Individual arguments are optional, but exactly one of +keys+ arguments
105
+ # is required.
106
+ def require_one_of(*keys)
107
+ @require_set[:one] << keys.map{ |k| self[k] }
108
+ end
109
+
110
+
111
+ # Individual arguments are optional, but at least one of +keys+ arguments
112
+ # is required.
113
+ def require_any_of(*keys)
114
+ @require_set[:any] << keys.map{ |k| self[k] }
115
+ end
116
+
117
+
118
+ # True if at least one argument is required out of multiple optional args.
119
+ def requires_some?
120
+ @require_set.size > 0
121
+ end
122
+
123
+
124
+ # @return [Parser] a Parser instance that can be used to parse this
125
+ # command-line Definition.
126
+ def parser
127
+ @parser ||= Parser.new(self)
128
+ end
129
+
130
+
131
+ # Parse the +args+ array of arguments using this command-line definition.
132
+ #
133
+ # @param args [Array, String] an array of arguments, or a String representing
134
+ # the command-line that is to be parsed.
135
+ # @return [OpenStruct, false] if successful, an OpenStruct object with all
136
+ # arguments defined as accessors, and the parsed or default values for each
137
+ # argument as values. If unsuccessful, returns false indicating a parse
138
+ # failure.
139
+ # @see Parser#parse, Parser#errors, Parser#show_usage, Parser#show_help
140
+ def parse(args = ARGV)
141
+ parser.parse(args)
142
+ end
143
+
144
+
145
+ # Return an array of parse errors.
146
+ # @see Parser#errors
147
+ def errors
148
+ parser.errors
149
+ end
150
+
151
+
152
+ # Whether user indicated they would like help on usage.
153
+ # @see Parser#show_usage
154
+ def show_usage?
155
+ parser.show_usage?
156
+ end
157
+
158
+
159
+ # Whether user indicated they would like help on supported arguments.
160
+ # @see Parser#show_help
161
+ def show_help?
162
+ parser.show_help?
163
+ end
164
+
165
+
166
+ # Validates the supplied +args+ Hash object, verifying that any argument
167
+ # set requirements have been satisfied. Returns an array of error
168
+ # messages for each set requirement that is not satisfied.
169
+ #
170
+ # @param args [Hash] a Hash containing the keys and values identified
171
+ # by the parser.
172
+ # @return [Array] a list of errors for any argument requirements that
173
+ # have not been satisfied.
174
+ def validate_requirements(args)
175
+ errors = []
176
+ @require_set.each do |req, sets|
177
+ sets.each do |set|
178
+ count = set.count{ |arg| args.has_key?(arg.key) }
179
+ case req
180
+ when :one
181
+ if count == 0
182
+ errors << "No argument has been specified for one of: #{set.join(', ')}"
183
+ elsif count > 1
184
+ errors << "Only one argument can been specified from: #{set.join(', ')}"
185
+ end
186
+ when :any
187
+ if count == 0
188
+ errors << "At least one of the arguments must be specified from: #{set.join(', ')}"
189
+ end
190
+ end
191
+ end
192
+ end
193
+ errors
194
+ end
195
+
196
+
197
+ # @return [Array] all arguments that have been defined.
198
+ def args
199
+ @arguments.values
200
+ end
201
+
202
+
203
+ # @return [Array] all positional arguments that have been defined
204
+ def positional_args
205
+ @arguments.values.select{ |arg| PositionalArgument === arg }
206
+ end
207
+
208
+
209
+ # @return True if any positional arguments have been defined.
210
+ def positional_args?
211
+ positional_args.size > 0
212
+ end
213
+
214
+
215
+ # @return [Array] the non-positional (i.e. keyword and flag)
216
+ # arguments that have been defined.
217
+ def non_positional_args
218
+ @arguments.values.reject{ |arg| PositionalArgument === arg || RestArgument === arg }
219
+ end
220
+
221
+
222
+ # @return True if any non-positional arguments have been defined.
223
+ def non_positional_args?
224
+ non_positional_args.size > 0
225
+ end
226
+
227
+
228
+ # @return [Array] the keyword arguments that have been defined.
229
+ def keyword_args
230
+ @arguments.values.select{ |arg| KeywordArgument === arg }
231
+ end
232
+
233
+
234
+ # @return True if any keyword arguments have been defined.
235
+ def keyword_args?
236
+ keyword_args.size > 0
237
+ end
238
+
239
+
240
+ # @return [Array] the flag arguments that have been defined
241
+ def flag_args
242
+ @arguments.values.select{ |arg| FlagArgument === arg }
243
+ end
244
+
245
+
246
+ # @return True if any flag arguments have been defined.
247
+ def flag_args?
248
+ flag_args.size > 0
249
+ end
250
+
251
+
252
+ # @return [RestArgument] the RestArgument defined for this command-line,
253
+ # or nil if no RestArgument is defined.
254
+ def rest_args
255
+ @arguments.values.find{ |arg| RestArgument === arg }
256
+ end
257
+
258
+
259
+ # @return True if a RestArgument has been defined.
260
+ def rest_args?
261
+ !!rest_args
262
+ end
263
+
264
+
265
+ # @return [Array] all the positional, keyword, and rest arguments
266
+ # that have been defined.
267
+ def value_args
268
+ @arguments.values.select{ |arg| ValueArgument === arg }
269
+ end
270
+
271
+
272
+ # @return [Integer] the number of arguments that have been defined.
273
+ def size
274
+ @arguments.size
275
+ end
276
+
277
+
278
+ # Generates a usage display string
279
+ def show_usage(out = STDERR, width = 80)
280
+ lines = ['']
281
+ pos_args = positional_args
282
+ opt_args = size - pos_args.size
283
+ usage_args = pos_args.map(&:to_use)
284
+ usage_args << (requires_some? ? 'OPTIONS' : '[OPTIONS]') if opt_args > 0
285
+ usage_args << rest_args.to_use if rest_args?
286
+ lines.concat(wrap_text("USAGE: #{RUBY_ENGINE} #{$0} #{usage_args.join(' ')}", width))
287
+ lines << ''
288
+ lines << 'Specify the /? or --help option for more detailed help'
289
+ lines << ''
290
+ lines.each{ |line| out.puts line } if out
291
+ lines
292
+ end
293
+
294
+
295
+ # Generates a more detailed help screen.
296
+ # @param out [IO] an IO object on which the help information will be
297
+ # output. Pass +nil+ if no output to any device is desired.
298
+ # @param width [Integer] the width at which to wrap text.
299
+ # @return [Array] An array of lines of text, containing the help text.
300
+ def show_help(out = STDOUT, width = 80)
301
+ lines = ['', '']
302
+ lines << title
303
+ lines << title.gsub(/./, '=')
304
+ lines << ''
305
+ if purpose
306
+ lines.concat(wrap_text(purpose, width))
307
+ lines << ''
308
+ lines << ''
309
+ end
310
+
311
+ lines << 'USAGE'
312
+ lines << '-----'
313
+ pos_args = positional_args
314
+ opt_args = size - pos_args.size
315
+ usage_args = pos_args.map(&:to_use)
316
+ usage_args << (requires_some? ? 'OPTIONS' : '[OPTIONS]') if opt_args > 0
317
+ usage_args << rest_args.to_use if rest_args?
318
+ lines.concat(wrap_text(" #{RUBY_ENGINE} #{$0} #{usage_args.join(' ')}", width))
319
+ lines << ''
320
+
321
+ if positional_args?
322
+ max = positional_args.map{ |a| a.to_s.length }.max
323
+ pos_args = positional_args
324
+ pos_args << rest_args if rest_args?
325
+ pos_args.each do |arg|
326
+ if arg.usage_break
327
+ lines << ''
328
+ lines << arg.usage_break
329
+ end
330
+ desc = arg.description
331
+ desc << "\n[Default: #{arg.default}]" unless arg.default.nil?
332
+ wrap_text(desc, width - max - 6).each_with_index do |line, i|
333
+ lines << " %-#{max}s %s" % [[arg.to_s][i], line]
334
+ end
335
+ end
336
+ lines << ''
337
+ end
338
+ if non_positional_args?
339
+ lines << ''
340
+ lines << 'OPTIONS'
341
+ lines << '-------'
342
+ max = non_positional_args.map{ |a| a.to_use.length }.max
343
+ non_positional_args.each do |arg|
344
+ if arg.usage_break
345
+ lines << ''
346
+ lines << arg.usage_break
347
+ end
348
+ desc = arg.description
349
+ desc << "\n[Default: #{arg.default}]" unless arg.default.nil?
350
+ wrap_text(desc, width - max - 6).each_with_index do |line, i|
351
+ lines << " %-#{max}s %s" % [[arg.to_use][i], line]
352
+ end
353
+ end
354
+ end
355
+ lines << ''
356
+
357
+ lines.each{ |line| line.length < width ? out.puts(line) : out.print(line) } if out
358
+ lines
359
+ end
360
+
361
+
362
+ # Utility method for wrapping lines of +text+ at +width+ characters.
363
+ #
364
+ # @param text [String] a string of text that is to be wrapped to a
365
+ # maximum width.
366
+ # @param width [Integer] the maximum length of each line of text.
367
+ # @return [Array] an Array of lines of text, each no longer than +width+
368
+ # characters.
369
+ def wrap_text(text, width)
370
+ if width > 0 && (text.length > width || text.index("\n"))
371
+ lines = []
372
+ start, nl_pos, ws_pos, wb_pos, end_pos = 0, 0, 0, 0, text.rindex(/[^\s]/)
373
+ while start < end_pos
374
+ last_start = start
375
+ nl_pos = text.index("\n", start)
376
+ ws_pos = text.rindex(/ +/, start + width)
377
+ wb_pos = text.rindex(/[\-,.;#)}\]\/\\]/, start + width - 1)
378
+ ### Debug code ###
379
+ #STDERR.puts self
380
+ #ind = ' ' * end_pos
381
+ #ind[start] = '('
382
+ #ind[start+width < end_pos ? start+width : end_pos] = ']'
383
+ #ind[nl_pos] = 'n' if nl_pos
384
+ #ind[wb_pos] = 'b' if wb_pos
385
+ #ind[ws_pos] = 's' if ws_pos
386
+ #STDERR.puts ind
387
+ ### End debug code ###
388
+ if nl_pos && nl_pos <= start + width
389
+ lines << text[start...nl_pos].strip
390
+ start = nl_pos + 1
391
+ elsif end_pos < start + width
392
+ lines << text[start..end_pos]
393
+ start = end_pos
394
+ elsif ws_pos && ws_pos > start && ((wb_pos.nil? || ws_pos > wb_pos) ||
395
+ (wb_pos && wb_pos > 5 && wb_pos - 5 < ws_pos))
396
+ lines << text[start...ws_pos]
397
+ start = text.index(/[^\s]/, ws_pos + 1)
398
+ elsif wb_pos && wb_pos > start
399
+ lines << text[start..wb_pos]
400
+ start = wb_pos + 1
401
+ else
402
+ lines << text[start...(start+width)]
403
+ start += width
404
+ end
405
+ if start <= last_start
406
+ # Detect an infinite loop, and just return the original text
407
+ STDERR.puts "Inifinite loop detected at #{__FILE__}:#{__LINE__}"
408
+ STDERR.puts " width: #{width}, start: #{start}, nl_pos: #{nl_pos}, " +
409
+ "ws_pos: #{ws_pos}, wb_pos: #{wb_pos}"
410
+ return [text]
411
+ end
412
+ end
413
+ lines
414
+ else
415
+ [text]
416
+ end
417
+ end
418
+
419
+ end
420
+
421
+ end
422
+
@@ -0,0 +1,151 @@
1
+ module ArgParser
2
+
3
+ # Namespace for DSL methods that can be imported into a class for defining
4
+ # command-line argument handling.
5
+ #
6
+ # @example
7
+ # class MyClass
8
+ # include ArgParser::DSL
9
+ #
10
+ # # These class methods are added by the DSL, and allow us to define
11
+ # # the command-line arguments we want our class to handle.
12
+ # positional_arg :command, 'The name of the sub-command to run',
13
+ # validation: ['process', 'list'] do |arg, val, hsh|
14
+ # # On parse, return the argument value as a symbol
15
+ # val.intern
16
+ # end
17
+ # rest_arg :files, 'The file(s) to process'
18
+ #
19
+ # def run
20
+ # # Parse the command-line arguments, and call the appropriate command
21
+ # args = parse_arguments
22
+ # send(args.command, *args.files)
23
+ # end
24
+ #
25
+ # def process(*files)
26
+ # ...
27
+ # end
28
+ #
29
+ # def list(*files)
30
+ # ...
31
+ # end
32
+ # end
33
+ module DSL
34
+
35
+ # Class methods added when DSL module is included into a class.
36
+ module ClassMethods
37
+
38
+ # Accessor to return a Definition object holding the command-line
39
+ # argument definitions.
40
+ def args_def
41
+ @args_def ||= ArgParser::Definition.new
42
+ end
43
+
44
+ # Returns true if any arguments have been defined
45
+ def args_defined?
46
+ @args_def && @args_def.args.size > 0
47
+ end
48
+
49
+ # Sets the title that will appear in the Usage output generated from
50
+ # the Definition.
51
+ def title(val)
52
+ args_def.title = val
53
+ end
54
+
55
+ # Sets the descriptive text that describes the purpose of the job
56
+ # represented by this class.
57
+ def purpose(desc)
58
+ args_def.purpose = desc
59
+ end
60
+
61
+ # Define a new positional argument.
62
+ # @see PositionalArgument#initialize
63
+ def positional_arg(key, desc, opts = {}, &block)
64
+ args_def.positional_arg(key, desc, opts, &block)
65
+ end
66
+
67
+ # Define a new positional argument.
68
+ # @see KeywordArgument#initialize
69
+ def keyword_arg(key, desc, opts = {}, &block)
70
+ args_def.keyword_arg(key, desc, opts, &block)
71
+ end
72
+
73
+ # Define a new flag argument.
74
+ # @see FlagArgument#initialize
75
+ def flag_arg(key, desc, opts = {}, &block)
76
+ args_def.flag_arg(key, desc, opts, &block)
77
+ end
78
+
79
+ # Define a rest argument.
80
+ # @see RestArgument#initialize
81
+ def rest_arg(key, desc, opts = {}, &block)
82
+ args_def.rest_arg(key, desc, opts, &block)
83
+ end
84
+
85
+ # Make exactly one of the specified arguments mandatory.
86
+ # @see Definition#require_one_of
87
+ def require_one_of(*keys)
88
+ args_def.require_one_of(*keys)
89
+ end
90
+
91
+ # Make one or more of the specified arguments mandatory.
92
+ # @see Definition#require_any_of
93
+ def require_any_of(*keys)
94
+ args_def.require_any_of(*keys)
95
+ end
96
+
97
+ end
98
+
99
+ # Hook used to extend the including class with class methods defined in
100
+ # the DSL ClassMethods module.
101
+ def self.included(base)
102
+ base.extend(ClassMethods)
103
+ end
104
+
105
+ # @return [Definition] The arguments Definition object defined on this
106
+ # class.
107
+ def args_def
108
+ self.class.args_def
109
+ end
110
+
111
+ # Defines a +parse_arguments+ instance method to be added to classes that
112
+ # include this module. Uses the +args_def+ argument definition stored on
113
+ # on the class to define the arguments to parse.
114
+ def parse_arguments(args = ARGV)
115
+ args_def.parse(args)
116
+ end
117
+
118
+
119
+ # Defines a +parse_errors+ instance method to be added to classes that
120
+ # include this module.
121
+ def parse_errors
122
+ args_def.errors
123
+ end
124
+
125
+
126
+ # Whether usage information should be displayed.
127
+ def show_usage?
128
+ args_def.show_usage?
129
+ end
130
+
131
+
132
+ # Whether help should be displayed.
133
+ def show_help?
134
+ args_def.show_usage?
135
+ end
136
+
137
+
138
+ # Outputs brief usgae details.
139
+ def show_usage(*args)
140
+ args_def.show_usage(*args)
141
+ end
142
+
143
+
144
+ # Outputs detailed help about available arguments.
145
+ def show_help(*args)
146
+ args_def.show_help(*args)
147
+ end
148
+
149
+ end
150
+
151
+ end
@@ -0,0 +1,237 @@
1
+ module ArgParser
2
+
3
+ # Parser for parsing a command-line
4
+ class Parser
5
+
6
+ # @return [Definition] The supported Arguments to be used when parsing
7
+ # the command-line.
8
+ attr_reader :definition
9
+ # @return [Array] An Array of error message Strings generated during
10
+ # parsing.
11
+ attr_reader :errors
12
+ # @return [Boolean] Flag set during parsing if the usage display should
13
+ # be shown. Set if there are any parse errors encountered.
14
+ def show_usage?
15
+ @show_usage
16
+ end
17
+ # @return [Boolean] Flag set during parsing if the user has requested
18
+ # the help display to be shown (via --help or /?).
19
+ def show_help?
20
+ @show_help
21
+ end
22
+
23
+
24
+ # Instantiates a new command-line parser, with the specified command-
25
+ # line definition. A Parser instance delegates unknown methods to the
26
+ # Definition, so its possible to work only with a Parser instance to
27
+ # both define and parse a command-line.
28
+ #
29
+ # @param [Definition] definition A Definition object that defines the
30
+ # possible arguments that may appear in a command-line. If no definition
31
+ # is supplied, an empty definition is created.
32
+ def initialize(definition = nil)
33
+ @definition = definition || Definition.new
34
+ @errors = []
35
+ end
36
+
37
+
38
+ # Parse the specified Array[String] of +tokens+, or ARGV if +tokens+ is
39
+ # nil. Returns false if unable to parse successfully, or an OpenStruct
40
+ # with accessors for every defined argument. Arguments whose values are
41
+ # not specified will contain the agument default value, or nil if no
42
+ # default is specified.
43
+ def parse(tokens = ARGV)
44
+ @show_usage = nil
45
+ @show_help = nil
46
+ @errors = []
47
+ begin
48
+ pos_vals, kw_vals, rest_vals = classify_tokens(tokens)
49
+ args = process_args(pos_vals, kw_vals, rest_vals) unless @show_help
50
+ rescue NoSuchArgumentError => ex
51
+ self.errors << ex.message
52
+ @show_usage = true
53
+ end
54
+ (@show_usage || @show_help) ? false : args
55
+ end
56
+
57
+
58
+ # Delegate unknown methods to the associated argument Definition object.
59
+ def method_missing(mthd, *args)
60
+ if @definition.respond_to?(mthd)
61
+ @definition.send(mthd, *args)
62
+ else
63
+ super
64
+ end
65
+ end
66
+
67
+
68
+ # Evaluate the list of values in +tokens+, and classify them as either
69
+ # keyword/value pairs, or positional arguments. Ideally this would be
70
+ # done without any reference to the defined arguments, but unfortunately
71
+ # a keyword arg cannot be distinguished from a flag arg followed by a
72
+ # positional arg without the context of what arguments are expected.
73
+ def classify_tokens(tokens)
74
+ if tokens.is_a?(String)
75
+ require 'csv'
76
+ tokens = CSV.parse(tokens, col_sep: ' ').first
77
+ end
78
+ tokens = [] unless tokens
79
+ pos_vals = []
80
+ kw_vals = {}
81
+ rest_vals = []
82
+
83
+ arg = nil
84
+ tokens.each_with_index do |token, i|
85
+ case token
86
+ when '/?', '-?', '--help'
87
+ @show_help = true
88
+ when /^-([a-z0-9]+)/i
89
+ $1.to_s.each_char do |sk|
90
+ kw_vals[arg] = nil if arg
91
+ arg = @definition[sk]
92
+ if FlagArgument === arg
93
+ kw_vals[arg] = true
94
+ arg = nil
95
+ end
96
+ end
97
+ when /^(?:--|\/)(no-)?(.+)/i
98
+ kw_vals[arg] = nil if arg
99
+ arg = @definition[$2]
100
+ if FlagArgument === arg || (KeywordArgument === arg && $1)
101
+ kw_vals[arg] = $1 ? false : true
102
+ arg = nil
103
+ end
104
+ when '--'
105
+ # All subsequent values are rest args
106
+ kw_vals[arg] = nil if arg
107
+ rest_vals = tokens[(i + 1)..-1]
108
+ break
109
+ else
110
+ if arg
111
+ kw_vals[arg] = token
112
+ else
113
+ pos_vals << token
114
+ arg = @definition.positional_args[i]
115
+ end
116
+ tokens[i] = '******' if arg && arg.sensitive?
117
+ arg = nil
118
+ end
119
+ end
120
+ kw_vals[arg] = nil if arg
121
+ [pos_vals, kw_vals, rest_vals]
122
+ end
123
+
124
+
125
+ # Process arguments using the supplied +pos_vals+ Array of positional
126
+ # argument values, and the +kw_vals+ Hash of keyword/value.
127
+ def process_args(pos_vals, kw_vals, rest_vals)
128
+ result = {}
129
+
130
+ # Process positional arguments
131
+ pos_args = @definition.positional_args
132
+ pos_args.each_with_index do |arg, i|
133
+ break if i >= pos_vals.length
134
+ result[arg.key] = process_arg_val(arg, pos_vals[i], result)
135
+ end
136
+ if pos_vals.size > pos_args.size
137
+ if @definition.rest_args?
138
+ rest_vals = pos_vals[pos_args.size..-1] + rest_vals
139
+ else
140
+ self.errors << "#{pos_vals.size} positional #{pos_vals.size == 1 ? 'argument' : 'arguments'} #{
141
+ pos_vals.size == 1 ? 'was' : 'were'} supplied, but only #{pos_args.size} #{
142
+ pos_args.size == 1 ? 'is' : 'are'} defined"
143
+ end
144
+ end
145
+
146
+ # Process key-word based arguments
147
+ kw_vals.each do |arg, val|
148
+ result[arg.key] = process_arg_val(arg, val, result)
149
+ end
150
+
151
+ # Process rest values
152
+ if arg = @definition.rest_args
153
+ result[arg.key] = process_arg_val(arg, rest_vals, result)
154
+ elsif rest_vals.size > 0
155
+ self.errors << "#{rest_vals.size} rest #{rest_vals.size == 1 ? 'value' : 'values'} #{
156
+ rest_vals.size == 1 ? 'was' : 'were'} supplied, but no rest argument is defined"
157
+ end
158
+
159
+ # Default unspecified arguments
160
+ @definition.args.select{ |arg| !result.has_key?(arg.key) }.each do |arg|
161
+ result[arg.key] = process_arg_val(arg, arg.default, result, true)
162
+ end
163
+
164
+ # Validate if any set requirements have been satisfied
165
+ self.errors.concat(@definition.validate_requirements(result))
166
+ if self.errors.size > 0
167
+ @show_usage = true
168
+ else
169
+ props = result.keys
170
+ @definition.args.each{ |arg| props << arg.key unless result.has_key?(arg.key) }
171
+ args = Struct.new(*props)
172
+ args.new(*result.values)
173
+ end
174
+ end
175
+
176
+
177
+ protected
178
+
179
+
180
+ # Process a single argument value
181
+ def process_arg_val(arg, val, hsh, is_default = false)
182
+ if is_default && arg.required? && (val.nil? || val.empty?)
183
+ self.errors << "No value was specified for required argument '#{arg}'"
184
+ return
185
+ end
186
+ if !is_default && val.nil? && KeywordArgument === arg && !arg.value_optional?
187
+ self.errors << "No value was specified for keyword argument '#{arg}'"
188
+ return
189
+ end
190
+
191
+ # Argument value validation
192
+ if ValueArgument === arg && arg.validation && val
193
+ valid = case arg.validation
194
+ when Regexp
195
+ [val].flatten.each do |v|
196
+ add_value_error(arg, val) unless v =~ arg.validation
197
+ end
198
+ when Array
199
+ [val].flatten.each do |v|
200
+ add_value_error(arg, val) unless arg.validation.include?(v)
201
+ end
202
+ when Proc
203
+ begin
204
+ arg.validation.call(val, arg, hsh)
205
+ rescue StandardError => ex
206
+ self.errors << "An error occurred in the validation handler for argument '#{arg}': #{ex}"
207
+ return
208
+ end
209
+ else
210
+ raise "Unknown validation type: #{arg.validation.class.name}"
211
+ end
212
+ end
213
+
214
+ # TODO: Argument value coercion
215
+
216
+ # Call any registered on_parse handler
217
+ begin
218
+ val = arg.on_parse.call(val, arg, hsh) if val && arg.on_parse
219
+ rescue StandardError => ex
220
+ self.errors << "An error occurred in the on_parse handler for argument '#{arg}': #{ex}"
221
+ return
222
+ end
223
+
224
+ # Return result
225
+ val
226
+ end
227
+
228
+
229
+ # Add an error for an invalid value
230
+ def add_value_error(arg, val)
231
+ self.errors << "The value '#{val}' is not valid for argument '#{arg}'"
232
+ end
233
+
234
+ end
235
+
236
+ end
237
+
@@ -0,0 +1,2 @@
1
+ require 'arg-parser'
2
+
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: arg-parser
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Adam Gardiner
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-21 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: ! " ArgParser is a simple, yet powerful command-line argument
15
+ parser, with\n support for positional, keyword, flag and rest arguments,
16
+ any of which\n may be optional or mandatory.\n"
17
+ email: adam.b.gardiner@gmail.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - README.md
23
+ - LICENSE
24
+ - lib/arg-parser/argument.rb
25
+ - lib/arg-parser/definition.rb
26
+ - lib/arg-parser/dsl.rb
27
+ - lib/arg-parser/parser.rb
28
+ - lib/arg-parser.rb
29
+ - lib/arg_parser.rb
30
+ homepage: https://github.com/agardiner/arg-parser
31
+ licenses: []
32
+ post_install_message:
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubyforge_project:
50
+ rubygems_version: 1.8.21
51
+ signing_key:
52
+ specification_version: 3
53
+ summary: ArgParser is a simple, yet powerful, command-line argument (option) parser
54
+ test_files: []