Project

General

Profile

Bug #11586 ยป test.rb

ChazDomerese (Chaz Domerese), 10/12/2015 10:54 PM

 
#! /usr/bin/env ruby
#encoding: utf-8

module CLI
# A Regexp to match a command line symbol argument.
# Examples: key:value /OR/ -key[:=]value /OR/ --key[=:]value
# Requires ruby 2.0+ (conditionals)
KeyCheck = /^(--?)?([a-zA-Z_]\w*)((?(1)(?:[:=])|(?:[:])))(?(2)(.+))/
# Takes a method's argument list, and transforms the given ARGV
# based on expected parameters.
#
# @param params - Method#parameters
# @param arglist - ARGV or similar
def self.slurp(params, arglist = self.argv)
args = []
rest = []
keyreq = {}
keyset = {}
return nil if !arglist || arglist.empty?
dd = arglist.include?('--')
dcheck = proc { dd && (arglist[0] == '--') }
i = 0
loop do
# end of list, or end of current command
if !arglist[i] || dcheck.call
break
# key-value argument, or switch
elsif arglist[i] =~ KeyCheck
keyset[$2.intern] = $4
arglist.delete_at(i)
next
end
# continue through the list
i += 1
end
params.each_with_index do |(type, key), i|
breakout = false
case type # param type
when :keyreq, :key
if keyset.key?(key)
keyreq[key] = keyset.delete(key)
else
# if required key argument not found, stop processing
if type == :keyreq
breakout = true
break
end
end
# skip :keyrest, already have the rest in keyset hash
when :req
# if end of list or end of current command reached ('--')
if !arglist[0] || dcheck.call
breakout = true
break
end
args << arglist.shift
# skip :rest, already have the rest in rest array
when :opt
if dcheck.call
breakout = true
break
end
args << arglist.shift if arglist[0]
end
break if breakout
end
# remove trailing -- from arglist if present
arglist.shift if dcheck.call
if args.size < params.find_all {|param| param[0] == :req }.size
return nil
end
if !params.select {|param| param[0] == :keyreq }.all? {|param| keyreq.key?(param[1]) }
return nil
end
return [args, rest, keyreq, keyset]
end
# Run any given method with args presented from the command line
# @param method - A Method, Proc, or other callable object
# @param arglist - An Array of string objects, most likely ARGV
def self.run(method, arglist = self.argv)
params = method.parameters
a, b, c, d = CLI.slurp(params, arglist)
if !a
raise ArgumentError, "Not enough arguments given for #{method.name.to_s}"
end
rest = !!params.find {|a| a[0] == :rest }
keyrest = !!params.find {|a| a[0] == :keyrest }
a.concat(b) if rest
c.merge!(d) if keyrest
method.call(*a, **c)
end
# Preserved arglist, for checking leftover args
def self.argv
@argv ||= ARGV.dup
end
end

if $0 == __FILE__

def test(cmd, key1:, key2: false, **keyrest)
local_variables.each {|v| puts "%10s: %30s" % [v.to_s, eval(v.to_s).inspect] }
end

if ARGV.size == 0
ARGV.concat ["command", "key1:abc", "key3:123", "key2:true"]
end

puts "-- Result: -------------"
CLI.run(method(:test))
puts "------------------------"
puts "Remainder: #{CLI.argv}"

end # if $0 == __FILE__

__END__
Results on both Windows 8 64bit and ArchLinux x86_64, with 32bit Rubies:

> uru 216 && ruby test.rb
---> Now using ruby 2.1.6-p336 tagged as `216`
---> Result: -------------
cmd: "command"
key1: "abc"
key2: "true"
keyrest: {:key3=>"123"}
------------------------
Remainder: []

> uru 221 && ruby test.rb
---> Now using ruby 2.2.1-p85 tagged as `221`
---> Result: -------------
cmd: "command"
key1: "abc"
key2: nil
keyrest: {:key3=>"123"}
------------------------
Remainder: []

> uru 223 && ruby test.rb
---> Now using ruby 2.2.3-p173 tagged as `223`
---> Result: -------------
cmd: "command"
key1: "abc"
key2: "true"
keyrest: {:key3=>"123"}
------------------------
Remainder: []

    (1-1/1)