1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
|
class Thor
class Options < Arguments #:nodoc:
LONG_RE = /^(--\w+(?:-\w+)*)$/
SHORT_RE = /^(-[a-z])$/i
EQ_RE = /^(--\w+(?:-\w+)*|-[a-z])=(.*)$/i
SHORT_SQ_RE = /^-([a-z]{2,})$/i # Allow either -x -v or -xv style for single char args
SHORT_NUM = /^(-[a-z])#{NUMERIC}$/i
# Receives a hash and makes it switches.
def self.to_switches(options)
options.map do |key, value|
case value
when true
"--#{key}"
when Array
"--#{key} #{value.map{ |v| v.inspect }.join(' ')}"
when Hash
"--#{key} #{value.map{ |k,v| "#{k}:#{v}" }.join(' ')}"
when nil, false
""
else
"--#{key} #{value.inspect}"
end
end.join(" ")
end
# Takes a hash of Thor::Option and a hash with defaults.
def initialize(hash_options={}, defaults={})
options = hash_options.values
super(options)
# Add defaults
defaults.each do |key, value|
@assigns[key.to_s] = value
@non_assigned_required.delete(hash_options[key])
end
@shorts, @switches, @extra = {}, {}, []
options.each do |option|
@switches[option.switch_name] = option
option.aliases.each do |short|
@shorts[short.to_s] ||= option.switch_name
end
end
end
def remaining
@extra
end
def parse(args)
@pile = args.dup
while peek
match, is_switch = current_is_switch?
shifted = shift
if is_switch
case shifted
when SHORT_SQ_RE
unshift($1.split('').map { |f| "-#{f}" })
next
when EQ_RE, SHORT_NUM
unshift($2)
switch = $1
when LONG_RE, SHORT_RE
switch = $1
end
switch = normalize_switch(switch)
option = switch_option(switch)
@assigns[option.human_name] = parse_peek(switch, option)
elsif match
@extra << shifted
@extra << shift while peek && peek !~ /^-/
else
@extra << shifted
end
end
check_requirement!
assigns = Thor::CoreExt::HashWithIndifferentAccess.new(@assigns)
assigns.freeze
assigns
end
def check_unknown!
# an unknown option starts with - or -- and has no more --'s afterward.
unknown = @extra.select { |str| str =~ /^--?(?:(?!--).)*$/ }
raise UnknownArgumentError, "Unknown switches '#{unknown.join(', ')}'" unless unknown.empty?
end
protected
# Returns true if the current value in peek is a registered switch.
#
def current_is_switch?
case peek
when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM
[true, switch?($1)]
when SHORT_SQ_RE
[true, $1.split('').any? { |f| switch?("-#{f}") }]
else
[false, false]
end
end
def current_is_switch_formatted?
case peek
when LONG_RE, SHORT_RE, EQ_RE, SHORT_NUM, SHORT_SQ_RE
true
else
false
end
end
def switch?(arg)
switch_option(normalize_switch(arg))
end
def switch_option(arg)
if match = no_or_skip?(arg)
@switches[arg] || @switches["--#{match}"]
else
@switches[arg]
end
end
# Check if the given argument is actually a shortcut.
#
def normalize_switch(arg)
(@shorts[arg] || arg).tr('_', '-')
end
# Parse boolean values which can be given as --foo=true, --foo or --no-foo.
#
def parse_boolean(switch)
if current_is_value?
if ["true", "TRUE", "t", "T", true].include?(peek)
shift
true
elsif ["false", "FALSE", "f", "F", false].include?(peek)
shift
false
else
true
end
else
@switches.key?(switch) || !no_or_skip?(switch)
end
end
# Parse the value at the peek analyzing if it requires an input or not.
#
def parse_peek(switch, option)
if current_is_switch_formatted? || last?
if option.boolean?
# No problem for boolean types
elsif no_or_skip?(switch)
return nil # User set value to nil
elsif option.string? && !option.required?
# Return the default if there is one, else the human name
return option.lazy_default || option.default || option.human_name
elsif option.lazy_default
return option.lazy_default
else
raise MalformattedArgumentError, "No value provided for option '#{switch}'"
end
end
@non_assigned_required.delete(option)
send(:"parse_#{option.type}", switch)
end
end
end
|