arg-parser 0.1

Sign up to get free protection for your applications and to get access to all the features.
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: []