Skip to content

Commit

Permalink
Improve parsing to allow for gathering of unused bare-word parameters.
Browse files Browse the repository at this point in the history
  • Loading branch information
eric authored and defunkt committed Aug 1, 2009
1 parent def3af9 commit 8b12556
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 62 deletions.
8 changes: 7 additions & 1 deletion lib/choice.rb
Expand Up @@ -36,6 +36,11 @@ def options_from_hash(options_hash)
@@options << [name.to_s, option]
end
end

# Return an array representing the rest of the command line arguments
def rest
@@rest
end

# Returns a hash representing options passed in via the command line.
def choices
Expand Down Expand Up @@ -80,7 +85,8 @@ def parse #:nodoc:
begin
# Delegate parsing to our parser class, passing it our defined
# options and the passed arguments.
@@choices = LazyHash.new(Parser.parse(@@options, @@args))
@@choices, @@rest = Parser.parse(@@options, @@args)
@@choices = LazyHash.new(@@choices)
rescue Choice::Parser::ParseError
# If we get an expected exception, show the help file.
help
Expand Down
66 changes: 36 additions & 30 deletions lib/choice/parser.rb
Expand Up @@ -18,6 +18,9 @@ def parse(options, args)
# Return empty hash if the parsing adventure would be fruitless.
return {} if options.nil? || !options || args.nil? || !args.is_a?(Array)

# Operate on a copy of the inputs
args = args.dup

# If we are passed an array, make the best of it by converting it
# to a hash.
options = options.inject({}) do |hash, value|
Expand Down Expand Up @@ -83,53 +86,53 @@ def parse(options, args)
# is definitely required.
required[name] = true if obj['valid']
end


rest = []

# Go through the arguments and try to figure out whom they belong to
# at this point.
args.each_with_index do |arg, i|
while arg = args.shift
if hashes['shorts'].value?(arg)
# Set the value to the next element in the args array since
# this is a short.
value = args[i+1]

# If the next element doesn't exist or starts with a -, make this
# value true.
value = true if !value || value =~ /^-/
# If the next argument isn't a value, set this value to true
if args.empty? || args.first.match(/^-/)
value = true
else
value = args.shift
end

# Add this value to the choices hash with the key of the option's
# name. If we expect an array, tack this argument on.
name = hashes['shorts'].index(arg)
if arrayed[name]
choices[name] ||= []
choices[name] += arrayize_arguments(name, args[i+1..-1])
choices[name] << value unless value.nil?
choices[name] += arrayize_arguments(args)
else
choices[name] = value
end

elsif /^(--[^=]+)=?/ =~ arg && longs.value?($1)
elsif (m = arg.match(/^(--[^=]+)=?/)) && longs.value?(m[1])
# The joke here is we always accept both --long=VALUE and --long VALUE.

# Grab values from --long=VALUE format
if arg =~ /=/ && longs.value?((longed = arg.split('=')).first)
name = longs.index(longed.shift)
value = longed * '='
# For the arrayed options.
potential_args = args[i+1..-1]
else
name, value = arg.split('=', 2)
name = longs.index(name)

if value.nil? && args.first !~ /^-/
# Grab value otherwise if not in --long=VALUE format. Assume --long VALUE.
name = longs.index(arg)
# Value is nil if we don't have a = and the next argument is no good
value = args[i+1] =~ /^-/ ? nil : args[i+1]
# For the arrayed options.
potential_args = args[i+2..-1]
value = args.shift
end

# If we expect an array, tack this argument on.
if arrayed[name] && !value.nil?
if arrayed[name]
# If this is arrayed and the value isn't nil, set it.
choices[name] ||= []
choices[name] << value
choices[name] += arrayize_arguments(name, potential_args)
choices[name] << value unless value.nil?
choices[name] += arrayize_arguments(args)
else
# If we set the value to nil, that means nothing was set and we
# need to set the value to true. We'll find out later if that's
Expand All @@ -139,7 +142,11 @@ def parse(options, args)

else
# If we're here, we have no idea what the passed argument is. Die.
raise UnknownOption if arg =~ /^-/
if arg =~ /^-/
raise UnknownOption
else
rest << arg
end
end
end

Expand All @@ -159,10 +166,10 @@ def parse(options, args)

# Make sure the argument is valid
raise InvalidArgument unless value.to_a.all? { |v| hashes['valids'][name].include?(v) } if hashes['valids'][name]

# Cast the argument using the method defined in the constant hash.
value = value.send(CAST_METHODS[hashes['casts'][name]]) if hashes['casts'].include?(name)

# Run the value through a filter and re-set it with the return.
value = hashes['filters'][name].call(value) if hashes['filters'].include?(name)

Expand All @@ -186,19 +193,18 @@ def parse(options, args)
choices[name] = value unless choices[name]
end

# Return the choices hash.
choices
# Return the choices hash and the rest of the args
[ choices, rest ]
end

private
# Turns trailing command line arguments into an array for an arrayed value
def arrayize_arguments(name, args)
def arrayize_arguments(args)
# Go through trailing arguments and suck them in if they don't seem
# to have an owner.
array = []
potential_args = args.dup
until (arg = potential_args.shift) =~ /^-/ || arg.nil?
array << arg
until args.empty? || args.first.match(/^-/)
array << args.shift
end
array
end
Expand Down

0 comments on commit 8b12556

Please sign in to comment.