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
|
class Thor
class Arguments #:nodoc:
NUMERIC = /(\d*\.\d+|\d+)/
# Receives an array of args and returns two arrays, one with arguments
# and one with switches.
#
def self.split(args)
arguments = []
args.each do |item|
break if item =~ /^-/
arguments << item
end
return arguments, args[Range.new(arguments.size, -1)]
end
def self.parse(*args)
to_parse = args.pop
new(*args).parse(to_parse)
end
# Takes an array of Thor::Argument objects.
#
def initialize(arguments=[])
@assigns, @non_assigned_required = {}, []
@switches = arguments
arguments.each do |argument|
if argument.default != nil
@assigns[argument.human_name] = argument.default
elsif argument.required?
@non_assigned_required << argument
end
end
end
def parse(args)
@pile = args.dup
@switches.each do |argument|
break unless peek
@non_assigned_required.delete(argument)
@assigns[argument.human_name] = send(:"parse_#{argument.type}", argument.human_name)
end
check_requirement!
@assigns
end
private
def no_or_skip?(arg)
arg =~ /^--(no|skip)-([-\w]+)$/
$2
end
def last?
@pile.empty?
end
def peek
@pile.first
end
def shift
@pile.shift
end
def unshift(arg)
unless arg.kind_of?(Array)
@pile.unshift(arg)
else
@pile = arg + @pile
end
end
def current_is_value?
peek && peek.to_s !~ /^-/
end
# Runs through the argument array getting strings that contains ":" and
# mark it as a hash:
#
# [ "name:string", "age:integer" ]
#
# Becomes:
#
# { "name" => "string", "age" => "integer" }
#
def parse_hash(name)
return shift if peek.is_a?(Hash)
hash = {}
while current_is_value? && peek.include?(?:)
key, value = shift.split(':',2)
hash[key] = value
end
hash
end
# Runs through the argument array getting all strings until no string is
# found or a switch is found.
#
# ["a", "b", "c"]
#
# And returns it as an array:
#
# ["a", "b", "c"]
#
def parse_array(name)
return shift if peek.is_a?(Array)
array = []
while current_is_value?
array << shift
end
array
end
# Check if the peek is numeric format and return a Float or Integer.
# Otherwise raises an error.
#
def parse_numeric(name)
return shift if peek.is_a?(Numeric)
unless peek =~ NUMERIC && $& == peek
raise MalformattedArgumentError, "Expected numeric value for '#{name}'; got #{peek.inspect}"
end
$&.index('.') ? shift.to_f : shift.to_i
end
# Parse string:
# for --string-arg, just return the current value in the pile
# for --no-string-arg, nil
#
def parse_string(name)
if no_or_skip?(name)
nil
else
shift
end
end
# Raises an error if @non_assigned_required array is not empty.
#
def check_requirement!
unless @non_assigned_required.empty?
names = @non_assigned_required.map do |o|
o.respond_to?(:switch_name) ? o.switch_name : o.human_name
end.join("', '")
class_name = self.class.name.split('::').last.downcase
raise RequiredArgumentMissingError, "No value provided for required #{class_name} '#{names}'"
end
end
end
end
|