cft_smartcloud 0.2.2 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/CHANGELOG +11 -0
- data/README.md +106 -0
- data/VERSION +1 -1
- data/bin/cft_smartcloud +33 -7
- data/bin/smartcloud +33 -7
- data/cft_smartcloud.gemspec +84 -20
- data/lib/config/config.yml +2 -0
- data/lib/curl_client.rb +42 -0
- data/lib/rest-client-1.6.6-master/.gitignore +6 -0
- data/lib/{rest-client-1.6.3 → rest-client-1.6.6-master}/README.rdoc +10 -1
- data/lib/{rest-client-1.6.3 → rest-client-1.6.6-master}/Rakefile +0 -0
- data/lib/rest-client-1.6.6-master/VERSION +1 -0
- data/lib/{rest-client-1.6.3 → rest-client-1.6.6-master}/bin/restclient +5 -4
- data/lib/{rest-client-1.6.3 → rest-client-1.6.6-master}/history.md +22 -0
- data/lib/{rest-client-1.6.3 → rest-client-1.6.6-master}/lib/rest-client.rb +0 -0
- data/lib/{rest-client-1.6.3 → rest-client-1.6.6-master}/lib/rest_client.rb +0 -0
- data/lib/{rest-client-1.6.3 → rest-client-1.6.6-master}/lib/restclient/abstract_response.rb +0 -0
- data/lib/{rest-client-1.6.3 → rest-client-1.6.6-master}/lib/restclient/exceptions.rb +0 -0
- data/lib/rest-client-1.6.6-master/lib/restclient/net_http_ext.rb +55 -0
- data/lib/{rest-client-1.6.3 → rest-client-1.6.6-master}/lib/restclient/payload.rb +17 -2
- data/lib/{rest-client-1.6.3 → rest-client-1.6.6-master}/lib/restclient/raw_response.rb +0 -0
- data/lib/{rest-client-1.6.3 → rest-client-1.6.6-master}/lib/restclient/request.rb +21 -19
- data/lib/{rest-client-1.6.3 → rest-client-1.6.6-master}/lib/restclient/resource.rb +0 -0
- data/lib/{rest-client-1.6.3 → rest-client-1.6.6-master}/lib/restclient/response.rb +0 -0
- data/lib/{rest-client-1.6.3 → rest-client-1.6.6-master}/lib/restclient.rb +0 -0
- data/lib/rest-client-1.6.6-master/rest-client.gemspec +76 -0
- data/lib/rest-client-1.6.6-master/spec/abstract_response_spec.rb +85 -0
- data/lib/rest-client-1.6.6-master/spec/base.rb +16 -0
- data/lib/rest-client-1.6.6-master/spec/exceptions_spec.rb +98 -0
- data/lib/rest-client-1.6.6-master/spec/integration/certs/equifax.crt +19 -0
- data/lib/rest-client-1.6.6-master/spec/integration/certs/verisign.crt +14 -0
- data/lib/rest-client-1.6.6-master/spec/integration/request_spec.rb +25 -0
- data/lib/rest-client-1.6.6-master/spec/integration_spec.rb +38 -0
- data/lib/rest-client-1.6.6-master/spec/master_shake.jpg +0 -0
- data/lib/rest-client-1.6.6-master/spec/payload_spec.rb +234 -0
- data/lib/rest-client-1.6.6-master/spec/raw_response_spec.rb +17 -0
- data/lib/rest-client-1.6.6-master/spec/request2_spec.rb +40 -0
- data/lib/rest-client-1.6.6-master/spec/request_spec.rb +536 -0
- data/lib/rest-client-1.6.6-master/spec/resource_spec.rb +134 -0
- data/lib/rest-client-1.6.6-master/spec/response_spec.rb +169 -0
- data/lib/rest-client-1.6.6-master/spec/restclient_spec.rb +73 -0
- data/lib/slop-2.3.1/.gemtest +0 -0
- data/lib/slop-2.3.1/.gitignore +6 -0
- data/lib/slop-2.3.1/.yardopts +6 -0
- data/lib/slop-2.3.1/CHANGES.md +137 -0
- data/lib/slop-2.3.1/LICENSE +20 -0
- data/lib/slop-2.3.1/README.md +293 -0
- data/lib/slop-2.3.1/Rakefile +6 -0
- data/lib/slop-2.3.1/lib/slop.rb +1022 -0
- data/lib/slop-2.3.1/slop.gemspec +11 -0
- data/lib/slop-2.3.1/test/commands_test.rb +151 -0
- data/lib/slop-2.3.1/test/helper.rb +13 -0
- data/lib/slop-2.3.1/test/option_test.rb +198 -0
- data/lib/slop-2.3.1/test/slop_test.rb +574 -0
- data/lib/smartcloud.rb +186 -116
- data/lib/terminal-table-1.4.4/History.rdoc +53 -0
- data/lib/terminal-table-1.4.4/Manifest +24 -0
- data/lib/terminal-table-1.4.4/README.rdoc +240 -0
- data/lib/terminal-table-1.4.4/Rakefile +15 -0
- data/lib/terminal-table-1.4.4/Todo.rdoc +14 -0
- data/lib/terminal-table-1.4.4/examples/examples.rb +80 -0
- data/lib/terminal-table-1.4.4/lib/terminal-table/cell.rb +88 -0
- data/lib/terminal-table-1.4.4/lib/terminal-table/core_ext.rb +8 -0
- data/lib/terminal-table-1.4.4/lib/terminal-table/import.rb +4 -0
- data/lib/terminal-table-1.4.4/lib/terminal-table/row.rb +48 -0
- data/lib/terminal-table-1.4.4/lib/terminal-table/separator.rb +14 -0
- data/lib/terminal-table-1.4.4/lib/terminal-table/style.rb +61 -0
- data/lib/terminal-table-1.4.4/lib/terminal-table/table.rb +217 -0
- data/lib/terminal-table-1.4.4/lib/terminal-table/table_helper.rb +9 -0
- data/lib/terminal-table-1.4.4/lib/terminal-table/version.rb +6 -0
- data/lib/terminal-table-1.4.4/lib/terminal-table.rb +27 -0
- data/lib/terminal-table-1.4.4/spec/cell_spec.rb +54 -0
- data/lib/terminal-table-1.4.4/spec/core_ext_spec.rb +18 -0
- data/lib/terminal-table-1.4.4/spec/import_spec.rb +11 -0
- data/lib/terminal-table-1.4.4/spec/spec.opts +1 -0
- data/lib/terminal-table-1.4.4/spec/spec_helper.rb +8 -0
- data/lib/terminal-table-1.4.4/spec/table_spec.rb +525 -0
- data/lib/terminal-table-1.4.4/tasks/docs.rake +13 -0
- data/lib/terminal-table-1.4.4/tasks/gemspec.rake +3 -0
- data/lib/terminal-table-1.4.4/tasks/spec.rake +25 -0
- data/lib/terminal-table-1.4.4/terminal-table.gemspec +30 -0
- data/responses/addresses +26 -0
- data/responses/addresses.blank +2 -0
- data/responses/instances +74 -0
- data/responses/keys +33 -0
- data/responses/locations +142 -0
- data/responses/offerings_image +3780 -0
- data/responses/storage +379 -0
- metadata +86 -22
- data/README.rdoc +0 -75
- data/lib/rest-client-1.6.3/VERSION +0 -1
- data/lib/rest-client-1.6.3/lib/restclient/net_http_ext.rb +0 -21
@@ -0,0 +1,1022 @@
|
|
1
|
+
class Slop
|
2
|
+
include Enumerable
|
3
|
+
|
4
|
+
# @return [String] The current version string
|
5
|
+
VERSION = '2.3.1'
|
6
|
+
|
7
|
+
# Slops standard Error class. All exception classes should
|
8
|
+
# inherit from this class
|
9
|
+
class Error < StandardError; end
|
10
|
+
|
11
|
+
# Raised when an option expects an argument and none is given
|
12
|
+
class MissingArgumentError < Error; end
|
13
|
+
|
14
|
+
# Raised when an option is required but not given
|
15
|
+
class MissingOptionError < Error; end
|
16
|
+
|
17
|
+
# Raised when an option specifies the `:match` attribute and this
|
18
|
+
# options argument does not match this regexp
|
19
|
+
class InvalidArgumentError < Error; end
|
20
|
+
|
21
|
+
# Raised when the `:strict` option is enabled and an unknown
|
22
|
+
# or unspecified option is used
|
23
|
+
class InvalidOptionError < Error; end
|
24
|
+
|
25
|
+
# Each option specified in `Slop#opt` creates an instance of this class
|
26
|
+
class Option < Struct.new(:short_flag, :long_flag, :description, :tail, :match, :help, :required, :forced, :count)
|
27
|
+
|
28
|
+
# @param [Slop] slop The Slop object this Option belongs to
|
29
|
+
#
|
30
|
+
# @param [String, #to_s] short The short flag representing this Option
|
31
|
+
# without prefix (ie: `a`)
|
32
|
+
#
|
33
|
+
# @param [String, #to_s] long The long flag representing this Option
|
34
|
+
# without the prefix (ie: `foo`)
|
35
|
+
#
|
36
|
+
# @param [String] description This options description
|
37
|
+
#
|
38
|
+
# @param [Boolean] argument True if this option takes an argument
|
39
|
+
#
|
40
|
+
# @option options [Boolean] :optional
|
41
|
+
# * When true, this option takes an optional argument, ie an argument
|
42
|
+
# does not **have** to be supplied.
|
43
|
+
#
|
44
|
+
# @option options [Boolean] :argument
|
45
|
+
# * True if this option takes an argument.
|
46
|
+
#
|
47
|
+
# @option options [Object] :default
|
48
|
+
# * The default value for this option when no argument is given
|
49
|
+
#
|
50
|
+
# @option options [Proc, #call] :callback
|
51
|
+
# * The callback object, used instead of passing a block to this option
|
52
|
+
#
|
53
|
+
# @option options [String, #to_s] :delimiter (',')
|
54
|
+
# * A delimiter string when processing this option as a list
|
55
|
+
#
|
56
|
+
# @option options [Integer] :limit (0)
|
57
|
+
# * A limit, used when processing this option as a list
|
58
|
+
#
|
59
|
+
# @option options [Boolean] :tail (false)
|
60
|
+
# * When true, this option will be grouped at the bottom of the help
|
61
|
+
# text instead of in order of processing
|
62
|
+
#
|
63
|
+
# @option options [Regexp] :match
|
64
|
+
# * A regular expression this option should match
|
65
|
+
#
|
66
|
+
# @option options [String, #to_s] :unless
|
67
|
+
# * Used by `omit_exec` for omitting execution of this options callback
|
68
|
+
# if another option exists
|
69
|
+
#
|
70
|
+
# @option options [Boolean, String] :help (true)
|
71
|
+
# * If this option is a string, it'll be appended to the long flag
|
72
|
+
# help text (before the description). When false, no help information
|
73
|
+
# will be displayed for this option
|
74
|
+
#
|
75
|
+
# @option options [Boolean] :required (false)
|
76
|
+
# * When true, this option is considered mandatory. That is, when not
|
77
|
+
# supplied, Slop will raise a `MissingOptionError`
|
78
|
+
def initialize(slop, short, long, description, argument, options, &blk)
|
79
|
+
@slop = slop
|
80
|
+
|
81
|
+
self.short_flag = short
|
82
|
+
self.long_flag = long
|
83
|
+
self.description = description
|
84
|
+
|
85
|
+
@argument = argument
|
86
|
+
@options = options
|
87
|
+
|
88
|
+
self.tail = @options[:tail]
|
89
|
+
self.match = @options[:match]
|
90
|
+
self.help = @options.fetch(:help, true)
|
91
|
+
self.required = @options[:required]
|
92
|
+
|
93
|
+
@delimiter = @options.fetch(:delimiter, ',')
|
94
|
+
@limit = @options.fetch(:limit, 0)
|
95
|
+
@argument_type = @options[:as].to_s.downcase
|
96
|
+
@argument_value = nil
|
97
|
+
|
98
|
+
self.forced = false
|
99
|
+
self.count = 0
|
100
|
+
|
101
|
+
@callback = blk if block_given?
|
102
|
+
@callback ||= @options[:callback]
|
103
|
+
|
104
|
+
if long_flag && long_flag.size > @slop.longest_flag
|
105
|
+
@slop.longest_flag = long_flag.size
|
106
|
+
@slop.longest_flag += help.size if help.respond_to?(:to_str)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# @return [Boolean] true if this option expects an argument
|
111
|
+
def expects_argument?
|
112
|
+
@argument || @options[:argument] || @options[:optional] == false
|
113
|
+
end
|
114
|
+
|
115
|
+
# @return [Boolean] true if this option accepts an optional argument
|
116
|
+
def accepts_optional_argument?
|
117
|
+
@options[:optional]
|
118
|
+
end
|
119
|
+
|
120
|
+
# @return [String] either the long or short flag for this option
|
121
|
+
def key
|
122
|
+
long_flag || short_flag
|
123
|
+
end
|
124
|
+
|
125
|
+
# Set this options argument value.
|
126
|
+
#
|
127
|
+
# If this options argument type is expected to be an Array, this
|
128
|
+
# method will split the value and concat elements into the original
|
129
|
+
# argument value
|
130
|
+
#
|
131
|
+
# @param [Object] value The value to set this options argument to
|
132
|
+
def argument_value=(value)
|
133
|
+
if @argument_type == 'array'
|
134
|
+
@argument_value ||= []
|
135
|
+
|
136
|
+
if value.respond_to?(:to_str)
|
137
|
+
@argument_value.concat value.split(@delimiter, @limit)
|
138
|
+
end
|
139
|
+
else
|
140
|
+
@argument_value = value
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# @return [Object] the argument value after it's been cast
|
145
|
+
# according to the `:as` option
|
146
|
+
def argument_value
|
147
|
+
return @argument_value if forced
|
148
|
+
# Check for count first to prefer 0 over nil
|
149
|
+
return count if @argument_type == 'count'
|
150
|
+
|
151
|
+
value = @argument_value || @options[:default]
|
152
|
+
return if value.nil?
|
153
|
+
|
154
|
+
case @argument_type
|
155
|
+
when 'array'; @argument_value unless !expects_argument?
|
156
|
+
when 'range'; value_to_range value unless !expects_argument?
|
157
|
+
when 'float'; value.to_s.to_f unless !expects_argument?
|
158
|
+
when 'string', 'str'; value.to_s unless !expects_argument?
|
159
|
+
when 'symbol', 'sym'; value.to_s.to_sym unless !expects_argument?
|
160
|
+
when 'integer', 'int'; value.to_s.to_i unless !expects_argument?
|
161
|
+
else
|
162
|
+
value
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Force an argument value, used when the desired argument value
|
167
|
+
# is negative (false or nil)
|
168
|
+
#
|
169
|
+
# @param [Object] value
|
170
|
+
def force_argument_value(value)
|
171
|
+
@argument_value = value
|
172
|
+
self.forced = true
|
173
|
+
end
|
174
|
+
|
175
|
+
# Execute the block or callback object associated with this Option
|
176
|
+
#
|
177
|
+
# @param [Object] The object to be sent to `:call`
|
178
|
+
def call(obj=nil)
|
179
|
+
@callback.call(obj) if @callback.respond_to?(:call)
|
180
|
+
end
|
181
|
+
|
182
|
+
# @param [Array] items The original array of objects passed to `Slop.new`
|
183
|
+
# @return [Boolean] true if this options `:unless` argument exists
|
184
|
+
# inside *items*
|
185
|
+
def omit_exec?(items)
|
186
|
+
string = @options[:unless].to_s.sub(/\A--?/, '')
|
187
|
+
items.any? { |i| i.to_s.sub(/\A--?/, '') == string }
|
188
|
+
end
|
189
|
+
|
190
|
+
# This option in a nice pretty string, including a short flag, long
|
191
|
+
# flag, and description (if they exist).
|
192
|
+
#
|
193
|
+
# @see Slop#help
|
194
|
+
# @return [String]
|
195
|
+
def to_s
|
196
|
+
out = " "
|
197
|
+
out += short_flag ? "-#{short_flag}, " : ' ' * 4
|
198
|
+
|
199
|
+
if long_flag
|
200
|
+
out += "--#{long_flag}"
|
201
|
+
if help.respond_to? :to_str
|
202
|
+
out += " #{help}"
|
203
|
+
size = long_flag.size + help.size + 1
|
204
|
+
else
|
205
|
+
size = long_flag.size
|
206
|
+
end
|
207
|
+
diff = @slop.longest_flag - size
|
208
|
+
out += " " * (diff + 6)
|
209
|
+
else
|
210
|
+
out += " " * (@slop.longest_flag + 8)
|
211
|
+
end
|
212
|
+
|
213
|
+
"#{out}#{description}"
|
214
|
+
end
|
215
|
+
|
216
|
+
# @return [String]
|
217
|
+
def inspect
|
218
|
+
"#<Slop::Option short_flag=#{short_flag.inspect} " +
|
219
|
+
"long_flag=#{long_flag.inspect} argument=#{@argument.inspect} " +
|
220
|
+
"description=#{description.inspect}>"
|
221
|
+
end
|
222
|
+
|
223
|
+
private
|
224
|
+
|
225
|
+
def value_to_range(value)
|
226
|
+
case value.to_s
|
227
|
+
when /\A(-?\d+?)(\.\.\.?|-|,)(-?\d+)\z/
|
228
|
+
Range.new($1.to_i, $3.to_i, $2 == '...')
|
229
|
+
when /\A-?\d+\z/
|
230
|
+
value.to_i
|
231
|
+
else
|
232
|
+
value
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|
237
|
+
|
238
|
+
# Used to hold a list of Option objects. This class inherits from Array
|
239
|
+
# and overwrites `Array#[]` so we can fetch Option objects via their
|
240
|
+
# short or long flags
|
241
|
+
class Options < Array
|
242
|
+
|
243
|
+
# Fetch an Option object. This method overrides Array#[] to provide
|
244
|
+
# a nicer interface for fetching options via their short or long flag.
|
245
|
+
# The reason we don't use a Hash here is because an option cannot be
|
246
|
+
# identified by a single label. Instead this method tests against
|
247
|
+
# a short flag first, followed by a long flag. When passing this
|
248
|
+
# method an Integer, it will work as an Array usually would, fetching
|
249
|
+
# the Slop::Option at this index.
|
250
|
+
#
|
251
|
+
# @param [Object] flag The short/long flag representing the option
|
252
|
+
# @example
|
253
|
+
# opts = Slop.parse { on :v, "Verbose mode" }
|
254
|
+
# opts.options[:v] #=> Option
|
255
|
+
# opts.options[:v].description #=> "Verbose mode"
|
256
|
+
# @return [Option] the option assoiated with this flag
|
257
|
+
def [](flag)
|
258
|
+
if flag.is_a? Integer
|
259
|
+
super
|
260
|
+
else
|
261
|
+
find do |option|
|
262
|
+
[option.short_flag, option.long_flag].include? flag.to_s
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
# Parses the items from a CLI format into a friendly object
|
269
|
+
#
|
270
|
+
# @param [Array] items Items to parse into options.
|
271
|
+
# @example Specifying three options to parse:
|
272
|
+
# opts = Slops.parse do
|
273
|
+
# on :v, :verbose, 'Enable verbose mode'
|
274
|
+
# on :n, :name, 'Your name'
|
275
|
+
# on :a, :age, 'Your age'
|
276
|
+
# end
|
277
|
+
# @return [Slop] Returns an instance of Slop
|
278
|
+
def self.parse(items=ARGV, options={}, &block)
|
279
|
+
initialize_and_parse items, false, options, &block
|
280
|
+
end
|
281
|
+
|
282
|
+
# Identical to {Slop.parse}, but removes parsed options from the
|
283
|
+
# original Array
|
284
|
+
#
|
285
|
+
# @return [Slop] Returns an instance of Slop
|
286
|
+
def self.parse!(items=ARGV, options={}, &block)
|
287
|
+
initialize_and_parse items, true, options, &block
|
288
|
+
end
|
289
|
+
|
290
|
+
# Build options from an optspec string
|
291
|
+
#
|
292
|
+
# @param [String] optspec The option spec string
|
293
|
+
# @param [Array] options A list of options to forward to Slop.new
|
294
|
+
# @return [Slop] A new instance of Slop
|
295
|
+
def self.optspec(optspec, *options)
|
296
|
+
if optspec[/^--+$/]
|
297
|
+
banner, optspec = optspec.split(/^--+$/, 2)
|
298
|
+
end
|
299
|
+
|
300
|
+
lines = optspec.split("\n").reject(&:empty?)
|
301
|
+
opts = Slop.new(banner, *options)
|
302
|
+
|
303
|
+
lines.each do |line|
|
304
|
+
opt, description = line.split(' ', 2)
|
305
|
+
short, long = opt.split(',').map { |s| s.sub(/\A--?/, '') }
|
306
|
+
argument = long && long[/\=$/]
|
307
|
+
long.sub!(/\=$/, '') if argument
|
308
|
+
opts.on short, long, description, argument
|
309
|
+
end
|
310
|
+
|
311
|
+
opts
|
312
|
+
end
|
313
|
+
|
314
|
+
# @return [Options]
|
315
|
+
attr_reader :options
|
316
|
+
|
317
|
+
# @return [Hash]
|
318
|
+
attr_reader :commands
|
319
|
+
|
320
|
+
# @overload banner=(string)
|
321
|
+
# Set the banner
|
322
|
+
# @param [String] string The text to set the banner to
|
323
|
+
attr_writer :banner
|
324
|
+
|
325
|
+
# @overload summary=(string)
|
326
|
+
# Set the summary
|
327
|
+
# @param [String] string The text to set the summary to
|
328
|
+
attr_writer :summary
|
329
|
+
|
330
|
+
# @overload description=(string)
|
331
|
+
# Set the description
|
332
|
+
# @param [String] string The text to set the description to
|
333
|
+
attr_writer :description
|
334
|
+
|
335
|
+
# @return [Integer] The length of the longest flag slop knows of
|
336
|
+
attr_accessor :longest_flag
|
337
|
+
|
338
|
+
# @return [Array] A list of aliases this command uses
|
339
|
+
attr_accessor :aliases
|
340
|
+
|
341
|
+
# @option opts [Boolean] :help
|
342
|
+
# * Automatically add the `help` option
|
343
|
+
#
|
344
|
+
# @option opts [Boolean] :strict
|
345
|
+
# * Raises when a non listed option is found, false by default
|
346
|
+
#
|
347
|
+
# @option opts [Boolean] :multiple_switches
|
348
|
+
# * Allows `-abc` to be processed as the options 'a', 'b', 'c' and will
|
349
|
+
# force their argument values to true. By default Slop with parse this
|
350
|
+
# as 'a' with the argument 'bc'
|
351
|
+
#
|
352
|
+
# @option opts [String] :banner
|
353
|
+
# * The banner text used for the help
|
354
|
+
#
|
355
|
+
# @option opts [Proc, #call] :on_empty
|
356
|
+
# * Any object that respondes to `call` which is executed when Slop has
|
357
|
+
# no items to parse
|
358
|
+
#
|
359
|
+
# @option opts [IO, #puts] :io ($stderr)
|
360
|
+
# * An IO object for writing to when :help => true is used
|
361
|
+
#
|
362
|
+
# @option opts [Boolean] :exit_on_help (true)
|
363
|
+
# * When false and coupled with the :help option, Slop will not exit
|
364
|
+
# inside of the `help` option
|
365
|
+
#
|
366
|
+
# @option opts [Boolean] :ignore_case (false)
|
367
|
+
# * Ignore options case
|
368
|
+
#
|
369
|
+
# @option opts [Proc, #call] :on_noopts
|
370
|
+
# * Trigger an event when no options are found
|
371
|
+
#
|
372
|
+
# @option opts [Boolean] :autocreate (false)
|
373
|
+
# * Autocreate options depending on the Array passed to {#parse}
|
374
|
+
#
|
375
|
+
# @option opts [Boolean] :arguments (false)
|
376
|
+
# * Set to true to enable all specified options to accept arguments
|
377
|
+
# by default
|
378
|
+
#
|
379
|
+
# @option opts [Array] :aliases ([])
|
380
|
+
# * Primary uses by commands to implement command aliases
|
381
|
+
#
|
382
|
+
# @option opts [Boolean] :completion (true)
|
383
|
+
# * When true, commands will be auto completed. Ie `foobar` will be
|
384
|
+
# executed simply when `foo` `fo` or `foob` are used
|
385
|
+
#
|
386
|
+
# @option options [Boolean] :all_accept_arguments (false)
|
387
|
+
# * When true, every option added will take an argument, this saves
|
388
|
+
# having to enable it for every option
|
389
|
+
def initialize(*opts, &block)
|
390
|
+
sloptions = opts.last.is_a?(Hash) ? opts.pop : {}
|
391
|
+
sloptions[:banner] = opts.shift if opts[0].respond_to?(:to_str)
|
392
|
+
opts.each { |o| sloptions[o] = true }
|
393
|
+
|
394
|
+
@options = Options.new
|
395
|
+
@commands = {}
|
396
|
+
@execution_block = nil
|
397
|
+
|
398
|
+
@longest_flag = 0
|
399
|
+
@invalid_options = []
|
400
|
+
|
401
|
+
@banner = sloptions[:banner]
|
402
|
+
@strict = sloptions[:strict]
|
403
|
+
@ignore_case = sloptions[:ignore_case]
|
404
|
+
@multiple_switches = sloptions.fetch(:multiple_switches, true)
|
405
|
+
@autocreate = sloptions[:autocreate]
|
406
|
+
@completion = sloptions.fetch(:completion, true)
|
407
|
+
@arguments = sloptions[:arguments]
|
408
|
+
@on_empty = sloptions[:on_empty]
|
409
|
+
@io = sloptions.fetch(:io, $stderr)
|
410
|
+
@on_noopts = sloptions[:on_noopts] || sloptions[:on_optionless]
|
411
|
+
@sloptions = sloptions
|
412
|
+
|
413
|
+
if block_given?
|
414
|
+
block.arity == 1 ? yield(self) : instance_eval(&block)
|
415
|
+
end
|
416
|
+
|
417
|
+
if sloptions[:help]
|
418
|
+
on :h, :help, 'Print this help message', :tail => true do
|
419
|
+
@io.puts help
|
420
|
+
exit unless sloptions[:exit_on_help] == false
|
421
|
+
end
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
# Set or return banner text
|
426
|
+
#
|
427
|
+
# @param [String] text Displayed banner text
|
428
|
+
# @example
|
429
|
+
# opts = Slop.parse do
|
430
|
+
# banner "Usage - ruby foo.rb [arguments]"
|
431
|
+
# end
|
432
|
+
# @return [String] The current banner
|
433
|
+
def banner(text=nil)
|
434
|
+
@banner = text if text
|
435
|
+
@banner
|
436
|
+
end
|
437
|
+
|
438
|
+
# Set or return the summary
|
439
|
+
#
|
440
|
+
# @param [String] text Displayed summary text
|
441
|
+
# @example
|
442
|
+
# opts = Slop.parse do
|
443
|
+
# summary "do stuff with more stuff"
|
444
|
+
# end
|
445
|
+
# @return [String] The current summary
|
446
|
+
def summary(text=nil)
|
447
|
+
@summary = text if text
|
448
|
+
@summary
|
449
|
+
end
|
450
|
+
|
451
|
+
# Set or return the description
|
452
|
+
#
|
453
|
+
# @param [String] text Displayed description text
|
454
|
+
# @example
|
455
|
+
# opts = Slop.parse do
|
456
|
+
# description "This command does a lot of stuff with other stuff."
|
457
|
+
# end
|
458
|
+
# @return [String] The current description
|
459
|
+
def description(text=nil)
|
460
|
+
@description = text if text
|
461
|
+
@description
|
462
|
+
end
|
463
|
+
|
464
|
+
# Parse a list of options, leaving the original Array unchanged
|
465
|
+
#
|
466
|
+
# @param [Array] items A list of items to parse
|
467
|
+
def parse(items=ARGV, &block)
|
468
|
+
parse_items items, &block
|
469
|
+
end
|
470
|
+
|
471
|
+
# Parse a list of options, removing parsed options from the original Array
|
472
|
+
#
|
473
|
+
# @param [Array] items A list of items to parse
|
474
|
+
def parse!(items=ARGV, &block)
|
475
|
+
parse_items items, true, &block
|
476
|
+
end
|
477
|
+
|
478
|
+
# Enumerable interface
|
479
|
+
def each(&block)
|
480
|
+
@options.each(&block)
|
481
|
+
end
|
482
|
+
|
483
|
+
# @param [Symbol] key Option symbol
|
484
|
+
# @example
|
485
|
+
# opts[:name] #=> "Emily"
|
486
|
+
# opts.get(:name) #=> "Emily"
|
487
|
+
# @return [Object] Returns the value associated with that option. If an
|
488
|
+
# option doesn't exist, a command will instead be searched for
|
489
|
+
def [](key)
|
490
|
+
option = @options[key]
|
491
|
+
option ? option.argument_value : @commands[key]
|
492
|
+
end
|
493
|
+
alias get []
|
494
|
+
|
495
|
+
# Specify an option with a short or long version, description and type
|
496
|
+
#
|
497
|
+
# @param [*] args Option configuration.
|
498
|
+
# @option args [Symbol, String] :short_flag Short option name.
|
499
|
+
# @option args [Symbol, String] :long_flag Full option name.
|
500
|
+
# @option args [String] :description Option description for use in Slop#help
|
501
|
+
# @option args [Boolean] :argument Specifies whether this option requires
|
502
|
+
# an argument
|
503
|
+
# @option args [Hash] :options Optional option configurations.
|
504
|
+
# @example
|
505
|
+
# opts = Slop.parse do
|
506
|
+
# on :n, :name, 'Your username', true # Required argument
|
507
|
+
# on :a, :age, 'Your age (optional)', :optional => true
|
508
|
+
# on :g, :gender, 'Your gender', :optional => false
|
509
|
+
# on :V, :verbose, 'Run in verbose mode', :default => true
|
510
|
+
# on :P, :people, 'Your friends', true, :as => Array
|
511
|
+
# on :h, :help, 'Print this help screen' do
|
512
|
+
# puts help
|
513
|
+
# end
|
514
|
+
# end
|
515
|
+
# @return [Slop::Option]
|
516
|
+
def option(*args, &block)
|
517
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
518
|
+
|
519
|
+
short, long, desc, arg, extras = clean_options(args)
|
520
|
+
|
521
|
+
options.merge!(extras)
|
522
|
+
options[:argument] = true if @sloptions[:all_accept_arguments]
|
523
|
+
|
524
|
+
option = Option.new(self, short, long, desc, arg, options, &block)
|
525
|
+
@options << option
|
526
|
+
|
527
|
+
option
|
528
|
+
end
|
529
|
+
alias opt option
|
530
|
+
alias on option
|
531
|
+
|
532
|
+
# Namespace options depending on what command is executed
|
533
|
+
#
|
534
|
+
# @param [Symbol, String] label
|
535
|
+
# @param [Hash] options
|
536
|
+
# @example
|
537
|
+
# opts = Slop.new do
|
538
|
+
# command :create do
|
539
|
+
# on :v, :verbose
|
540
|
+
# end
|
541
|
+
# end
|
542
|
+
#
|
543
|
+
# # ARGV is `create -v`
|
544
|
+
# opts.commands[:create].verbose? #=> true
|
545
|
+
# @since 1.5.0
|
546
|
+
# @raise [ArgumentError] When this command already exists
|
547
|
+
# @return [Slop] a new instance of Slop namespaced to +label+
|
548
|
+
def command(label, options={}, &block)
|
549
|
+
if @commands.key?(label)
|
550
|
+
raise ArgumentError, "command `#{label}` already exists"
|
551
|
+
end
|
552
|
+
|
553
|
+
slop = Slop.new @sloptions.merge(options)
|
554
|
+
slop.aliases = Array(options.delete(:aliases) || options.delete(:alias))
|
555
|
+
@commands[label] = slop
|
556
|
+
|
557
|
+
slop.aliases.each { |a| @commands[a] = @commands[label] }
|
558
|
+
|
559
|
+
if block_given?
|
560
|
+
block.arity == 1 ? yield(slop) : slop.instance_eval(&block)
|
561
|
+
end
|
562
|
+
|
563
|
+
slop
|
564
|
+
end
|
565
|
+
|
566
|
+
# Trigger an event when Slop has no values to parse
|
567
|
+
#
|
568
|
+
# @param [Object, #call] obj The object (which can be anything
|
569
|
+
# responding to `call`)
|
570
|
+
# @example
|
571
|
+
# Slop.parse do
|
572
|
+
# on_empty { puts 'No argument given!' }
|
573
|
+
# end
|
574
|
+
# @since 1.5.0
|
575
|
+
def on_empty(obj=nil, &block)
|
576
|
+
@on_empty ||= (obj || block)
|
577
|
+
end
|
578
|
+
alias on_empty= on_empty
|
579
|
+
|
580
|
+
# Trigger an event when the arguments contain no options
|
581
|
+
#
|
582
|
+
# @param [Object, #call] obj The object to be triggered (anything
|
583
|
+
# responding to `call`)
|
584
|
+
# @example
|
585
|
+
# Slop.parse do
|
586
|
+
# on_noopts { puts 'No options here!' }
|
587
|
+
# end
|
588
|
+
# @since 1.6.0
|
589
|
+
def on_noopts(obj=nil, &block)
|
590
|
+
@on_noopts ||= (obj || block)
|
591
|
+
end
|
592
|
+
alias on_optionless on_noopts
|
593
|
+
|
594
|
+
# Add an execution block (for commands)
|
595
|
+
#
|
596
|
+
# @example
|
597
|
+
# opts = Slop.new do
|
598
|
+
# command :foo do
|
599
|
+
# on :v, :verbose
|
600
|
+
#
|
601
|
+
# execute { |o| p o.verbose? }
|
602
|
+
# end
|
603
|
+
# end
|
604
|
+
# opts.parse %w[foo --verbose] #=> true
|
605
|
+
#
|
606
|
+
# @param [Array] args The list of arguments to send to this command
|
607
|
+
# is invoked
|
608
|
+
# @since 1.8.0
|
609
|
+
# @yield [Slop] an instance of Slop for this command
|
610
|
+
def execute(args=[], &block)
|
611
|
+
if block_given?
|
612
|
+
@execution_block = block
|
613
|
+
elsif @execution_block.respond_to?(:call)
|
614
|
+
@execution_block.call(self, args)
|
615
|
+
end
|
616
|
+
end
|
617
|
+
|
618
|
+
# Returns the parsed list into a option/value hash
|
619
|
+
#
|
620
|
+
# @example
|
621
|
+
# opts.to_hash #=> { :name => 'Emily' }
|
622
|
+
#
|
623
|
+
# # strings!
|
624
|
+
# opts.to_hash(false) #=> { 'name' => 'Emily' }
|
625
|
+
# @return [Hash]
|
626
|
+
def to_hash(symbols=true)
|
627
|
+
@options.reduce({}) do |hsh, option|
|
628
|
+
key = option.key
|
629
|
+
key = key.to_sym if symbols
|
630
|
+
hsh[key] = option.argument_value
|
631
|
+
hsh
|
632
|
+
end
|
633
|
+
end
|
634
|
+
alias to_h to_hash
|
635
|
+
|
636
|
+
# Return parsed items as a new Class
|
637
|
+
#
|
638
|
+
# @example
|
639
|
+
# opts = Slop.new do
|
640
|
+
# on :n, :name, 'Persons name', true
|
641
|
+
# on :a, :age, 'Persons age', true, :as => :int
|
642
|
+
# on :s, :sex, 'Persons sex m/f', true, :match => /^[mf]$/
|
643
|
+
# on :A, :admin, 'Enable admin mode'
|
644
|
+
# end
|
645
|
+
#
|
646
|
+
# opts.parse %w[ --name Lee --age 22 -s m --admin ]
|
647
|
+
#
|
648
|
+
# person = opts.to_struct("Person")
|
649
|
+
# person.class #=> Struct::Person
|
650
|
+
# person.name #=> 'Lee'
|
651
|
+
# person.age #=> 22
|
652
|
+
# person.sex #=> m
|
653
|
+
# person.admin #=> true
|
654
|
+
#
|
655
|
+
# @param [String] name The name of this class
|
656
|
+
# @return [Class] The new class, or nil if there are no options
|
657
|
+
# @since 2.0.0
|
658
|
+
def to_struct(name=nil)
|
659
|
+
hash = to_hash
|
660
|
+
Struct.new(name, *hash.keys).new(*hash.values) unless hash.empty?
|
661
|
+
end
|
662
|
+
|
663
|
+
# Fetch a list of options which were missing from the parsed list
|
664
|
+
#
|
665
|
+
# @example
|
666
|
+
# opts = Slop.new do
|
667
|
+
# on :n, :name, 'Your name', true
|
668
|
+
# on :p, :password, 'Your password', true
|
669
|
+
# on :A, 'Use auth?'
|
670
|
+
# end
|
671
|
+
#
|
672
|
+
# opts.parse %w[ --name Lee ]
|
673
|
+
# opts.missing #=> ['password', 'a']
|
674
|
+
#
|
675
|
+
# @return [Array] A list of options missing from the parsed string
|
676
|
+
# @since 2.1.0
|
677
|
+
def missing
|
678
|
+
@options.select { |opt| not present?(opt.key) }.map { |opt| opt.key }
|
679
|
+
end
|
680
|
+
|
681
|
+
# Allows you to check whether an option was specified in the parsed list
|
682
|
+
#
|
683
|
+
# Merely sugar for `present?`
|
684
|
+
#
|
685
|
+
# @example
|
686
|
+
# #== ruby foo.rb -v
|
687
|
+
# opts.verbose? #=> true
|
688
|
+
# opts.name? #=> false
|
689
|
+
# @see Slop#present?
|
690
|
+
# @return [Boolean] true if this option is present, false otherwise
|
691
|
+
def method_missing(meth, *args, &block)
|
692
|
+
super unless meth.to_s[-1, 1] == '?'
|
693
|
+
present = present? meth.to_s.chomp('?')
|
694
|
+
|
695
|
+
(class << self; self; end).instance_eval do
|
696
|
+
define_method(meth) { present }
|
697
|
+
end
|
698
|
+
|
699
|
+
present
|
700
|
+
end
|
701
|
+
|
702
|
+
# Check if an option is specified in the parsed list
|
703
|
+
#
|
704
|
+
# Does the same as Slop#option? but a convenience method for unacceptable
|
705
|
+
# method names
|
706
|
+
#
|
707
|
+
# @param [Object] The object name(s) to check
|
708
|
+
# @since 1.5.0
|
709
|
+
# @return [Boolean] true if these options are present, false otherwise
|
710
|
+
def present?(*option_names)
|
711
|
+
option_names.all? { |opt| @options[opt] && @options[opt].count > 0 }
|
712
|
+
end
|
713
|
+
|
714
|
+
# Returns the banner followed by available options listed on the next line
|
715
|
+
#
|
716
|
+
# @example
|
717
|
+
# opts = Slop.parse do
|
718
|
+
# banner "Usage - ruby foo.rb [arguments]"
|
719
|
+
# on :v, :verbose, "Enable verbose mode"
|
720
|
+
# end
|
721
|
+
# puts opts
|
722
|
+
# @return [String] Help text.
|
723
|
+
def to_s
|
724
|
+
parts = []
|
725
|
+
|
726
|
+
parts << banner if banner
|
727
|
+
parts << summary if summary
|
728
|
+
parts << wrap_and_indent(description, 80, 4) if description
|
729
|
+
|
730
|
+
if options.size > 0
|
731
|
+
parts << "options:"
|
732
|
+
|
733
|
+
heads = @options.reject(&:tail)
|
734
|
+
tails = @options.select(&:tail)
|
735
|
+
all = (heads + tails).select(&:help)
|
736
|
+
|
737
|
+
parts << all.map(&:to_s).join("\n")
|
738
|
+
end
|
739
|
+
|
740
|
+
parts.join("\n\n")
|
741
|
+
end
|
742
|
+
alias help to_s
|
743
|
+
|
744
|
+
# @return [String] This Slop object will options and configuration
|
745
|
+
# settings revealed
|
746
|
+
def inspect
|
747
|
+
"#<Slop config_options=#{@sloptions.inspect}\n " +
|
748
|
+
options.map(&:inspect).join("\n ") + "\n>"
|
749
|
+
end
|
750
|
+
|
751
|
+
private
|
752
|
+
|
753
|
+
class << self
|
754
|
+
private
|
755
|
+
|
756
|
+
def initialize_and_parse(items, delete, options, &block)
|
757
|
+
if items.is_a?(Hash) && options.empty?
|
758
|
+
options = items
|
759
|
+
items = ARGV
|
760
|
+
end
|
761
|
+
|
762
|
+
slop = new(options, &block)
|
763
|
+
delete ? slop.parse!(items) : slop.parse(items)
|
764
|
+
slop
|
765
|
+
end
|
766
|
+
end
|
767
|
+
|
768
|
+
# traverse through the list of items sent to parse() or parse!() and
|
769
|
+
# attempt to do the following:
|
770
|
+
#
|
771
|
+
# * Find an option object
|
772
|
+
# * Assign an argument to this option
|
773
|
+
# * Validate an option and/or argument depending on configuration options
|
774
|
+
# * Remove non-parsed items if `delete` is true
|
775
|
+
# * Yield any non-options to the block (if one is given)
|
776
|
+
def parse_items(items, delete=false, &block)
|
777
|
+
if items.empty? and @on_empty.respond_to?(:call)
|
778
|
+
@on_empty.call self
|
779
|
+
return items
|
780
|
+
elsif not items.any? {|i| i.to_s[/\A--?/] } and @on_noopts.respond_to?(:call)
|
781
|
+
@on_noopts.call self
|
782
|
+
return items
|
783
|
+
elsif execute_command(items, delete)
|
784
|
+
return items
|
785
|
+
end
|
786
|
+
|
787
|
+
trash = []
|
788
|
+
ignore_all = false
|
789
|
+
|
790
|
+
items.each_with_index do |item, index|
|
791
|
+
item = item.to_s
|
792
|
+
flag = item.sub(/\A--?/, '')
|
793
|
+
|
794
|
+
if item == '--'
|
795
|
+
trash << index
|
796
|
+
ignore_all = true
|
797
|
+
end
|
798
|
+
|
799
|
+
next if ignore_all
|
800
|
+
autocreate(flag, index, items) if @autocreate
|
801
|
+
option, argument = extract_option(item, flag)
|
802
|
+
|
803
|
+
if @multiple_switches and item[/\A-[^-]/] and not option
|
804
|
+
trash << index
|
805
|
+
next
|
806
|
+
end
|
807
|
+
|
808
|
+
if option
|
809
|
+
option.count += 1 unless item[/\A--no-/]
|
810
|
+
trash << index
|
811
|
+
next if option.forced
|
812
|
+
option.argument_value = true
|
813
|
+
|
814
|
+
if option.expects_argument? or option.accepts_optional_argument?
|
815
|
+
argument ||= items.at(index + 1)
|
816
|
+
trash << index + 1
|
817
|
+
|
818
|
+
if not option.accepts_optional_argument? and argument =~ /\A--?[a-zA-Z][a-zA-Z0-9_-]*\z/
|
819
|
+
raise MissingArgumentError, "'#{option.key}' expects an argument, none given"
|
820
|
+
end
|
821
|
+
|
822
|
+
if argument
|
823
|
+
if option.match and not argument.match(option.match)
|
824
|
+
raise InvalidArgumentError, "'#{argument}' does not match #{option.match.inspect}"
|
825
|
+
end
|
826
|
+
|
827
|
+
option.argument_value = argument
|
828
|
+
option.call option.argument_value unless option.omit_exec?(items)
|
829
|
+
else
|
830
|
+
option.argument_value = nil
|
831
|
+
check_optional_argument!(option, flag)
|
832
|
+
end
|
833
|
+
else
|
834
|
+
option.call unless option.omit_exec?(items)
|
835
|
+
end
|
836
|
+
else
|
837
|
+
@invalid_options << flag if item[/\A--?/] and @strict
|
838
|
+
block.call(item) if block_given? and not trash.include?(index)
|
839
|
+
end
|
840
|
+
end
|
841
|
+
|
842
|
+
items.reject!.with_index { |o, i| trash.include?(i) } if delete
|
843
|
+
raise_if_invalid_options!
|
844
|
+
raise_if_missing_required_options!(items)
|
845
|
+
items
|
846
|
+
end
|
847
|
+
|
848
|
+
def check_optional_argument!(option, flag)
|
849
|
+
if option.accepts_optional_argument?
|
850
|
+
option.call
|
851
|
+
else
|
852
|
+
raise MissingArgumentError, "'#{flag}' expects an argument, none given"
|
853
|
+
end
|
854
|
+
end
|
855
|
+
|
856
|
+
def raise_if_invalid_options!
|
857
|
+
return if not @strict or @invalid_options.empty?
|
858
|
+
message = "Unknown option#{'s' if @invalid_options.size > 1}"
|
859
|
+
message << ' -- ' << @invalid_options.map { |o| "'#{o}'" }.join(', ')
|
860
|
+
raise InvalidOptionError, message
|
861
|
+
end
|
862
|
+
|
863
|
+
def raise_if_missing_required_options!(items)
|
864
|
+
@options.select(&:required).each do |o|
|
865
|
+
unless items.select {|i| i[/\A--?/] }.any? {|i| i.to_s.sub(/\A--?/, '') == o.key }
|
866
|
+
raise MissingOptionError, "Expected option `#{o.key}` is required"
|
867
|
+
end
|
868
|
+
end
|
869
|
+
end
|
870
|
+
|
871
|
+
# if multiple_switches is enabled, this method filters through an items
|
872
|
+
# characters and attempts to find an Option object for each flag.
|
873
|
+
#
|
874
|
+
# Raises if a flag expects an argument or strict mode is enabled and a
|
875
|
+
# flag was not found
|
876
|
+
def enable_multiple_switches(item)
|
877
|
+
item[1..-1].each_char do |switch|
|
878
|
+
option = @options[switch]
|
879
|
+
|
880
|
+
if option
|
881
|
+
if option.expects_argument?
|
882
|
+
raise MissingArgumentError, "'-#{switch}' expects an argument, used in multiple_switch context"
|
883
|
+
end
|
884
|
+
|
885
|
+
option.argument_value = true
|
886
|
+
option.count += 1
|
887
|
+
else
|
888
|
+
raise InvalidOptionError, "Unknown option '-#{switch}'" if @strict
|
889
|
+
end
|
890
|
+
end
|
891
|
+
end
|
892
|
+
|
893
|
+
def wrap_and_indent(string, width, indentation)
|
894
|
+
string.lines.map do |paragraph|
|
895
|
+
lines = []
|
896
|
+
line = ''
|
897
|
+
|
898
|
+
paragraph.split(/\s/).each do |word|
|
899
|
+
if (line + ' ' + word).length >= width
|
900
|
+
lines << line
|
901
|
+
line = ''
|
902
|
+
end
|
903
|
+
|
904
|
+
line << (line == '' ? '' : ' ' ) + word
|
905
|
+
end
|
906
|
+
lines << line
|
907
|
+
|
908
|
+
lines.map { |l| ' ' * indentation + l }.join("\n")
|
909
|
+
end.join("\n")
|
910
|
+
end
|
911
|
+
|
912
|
+
# attempt to extract an option from an argument, this method allows us
|
913
|
+
# to parse things like 'foo=bar' and '--no-value' for negative values
|
914
|
+
# returns an array of the Option object and an argument if one was found
|
915
|
+
def extract_option(item, flag)
|
916
|
+
if item[0, 1] == '-'
|
917
|
+
option = @options[flag]
|
918
|
+
option ||= @options[flag.downcase] if @ignore_case
|
919
|
+
end
|
920
|
+
|
921
|
+
unless option
|
922
|
+
case item
|
923
|
+
when /\A-[^-]/
|
924
|
+
if @multiple_switches
|
925
|
+
enable_multiple_switches(item)
|
926
|
+
else
|
927
|
+
flag, argument = flag.split('', 2)
|
928
|
+
option = @options[flag]
|
929
|
+
end
|
930
|
+
when /\A--([^=]+)=(.+)\z/
|
931
|
+
option, argument = @options[$1], $2
|
932
|
+
when /\A--no-(.+)\z/
|
933
|
+
option = @options[$1]
|
934
|
+
option.force_argument_value(false) if option
|
935
|
+
end
|
936
|
+
end
|
937
|
+
|
938
|
+
[option, argument]
|
939
|
+
end
|
940
|
+
|
941
|
+
# attempt to execute a command if one exists, returns a positive (tru-ish)
|
942
|
+
# result if the command was found and executed. If completion is enabled
|
943
|
+
# and a flag is found to be ambiguous, this method prints an error message
|
944
|
+
# to the @io object informing the user
|
945
|
+
def execute_command(items, delete)
|
946
|
+
str = items[0]
|
947
|
+
|
948
|
+
if str
|
949
|
+
command = @commands.keys.find { |c| c.to_s == str.to_s }
|
950
|
+
|
951
|
+
if @completion and not command
|
952
|
+
cmds = @commands.keys.select { |c| c.to_s[0, str.length] == str }
|
953
|
+
|
954
|
+
if cmds.size > 1
|
955
|
+
@io.puts "Command '#{str}' is ambiguous:"
|
956
|
+
@io.puts " " + cmds.map(&:to_s).sort.join(', ')
|
957
|
+
else
|
958
|
+
command = cmds.shift
|
959
|
+
end
|
960
|
+
end
|
961
|
+
end
|
962
|
+
|
963
|
+
if command
|
964
|
+
items.shift
|
965
|
+
opts = @commands[command]
|
966
|
+
delete ? opts.parse!(items) : opts.parse(items)
|
967
|
+
opts.execute(items.reject { |i| i == '--' })
|
968
|
+
end
|
969
|
+
end
|
970
|
+
|
971
|
+
# If autocreation is enabled this method simply generates an option
|
972
|
+
# and add's it to the existing list of options
|
973
|
+
def autocreate(flag, index, items)
|
974
|
+
return if present? flag
|
975
|
+
short, long = clean_options Array(flag)
|
976
|
+
arg = (items[index + 1] && items[index + 1] !~ /\A--?/)
|
977
|
+
option = Option.new(self, short, long, nil, arg, {})
|
978
|
+
option.count = 1
|
979
|
+
@options << option
|
980
|
+
end
|
981
|
+
|
982
|
+
# Clean up arguments sent to `on` and return a list of 5 elements:
|
983
|
+
# * short flag (or nil)
|
984
|
+
# * long flag (or nil)
|
985
|
+
# * description (or nil)
|
986
|
+
# * true/false if this option takes an argument or not
|
987
|
+
# * extra options (ie: :as, :optional, and :help)
|
988
|
+
def clean_options(args)
|
989
|
+
options = []
|
990
|
+
extras = {}
|
991
|
+
extras[:as] = args.find { |c| c.is_a? Class }
|
992
|
+
args.delete(extras[:as])
|
993
|
+
extras.delete(:as) if extras[:as].nil?
|
994
|
+
|
995
|
+
short = args.first.to_s.sub(/\A--?/, '')
|
996
|
+
if short.size == 1
|
997
|
+
options.push short
|
998
|
+
args.shift
|
999
|
+
else
|
1000
|
+
options.push nil
|
1001
|
+
end
|
1002
|
+
|
1003
|
+
long = args.first
|
1004
|
+
boolean = [true, false].include? long
|
1005
|
+
|
1006
|
+
if not boolean and long.to_s =~ /\A(?:--?)?[a-z_-]+\s[A-Z\s\[\]]+\z/
|
1007
|
+
arg, help = args.shift.split(/ /, 2)
|
1008
|
+
options.push arg.sub(/\A--?/, '')
|
1009
|
+
extras[:optional] = help[0, 1] == '[' && help[-1, 1] == ']'
|
1010
|
+
extras[:help] = help
|
1011
|
+
elsif not boolean and long.to_s =~ /\A(?:--?)?[a-zA-Z][a-zA-Z0-9_-]+\=?\z/
|
1012
|
+
extras[:argument] = true if long.to_s[-1, 1] == '='
|
1013
|
+
options.push args.shift.to_s.sub(/\A--?/, '').sub(/\=\z/, '')
|
1014
|
+
else
|
1015
|
+
options.push nil
|
1016
|
+
end
|
1017
|
+
|
1018
|
+
options.push args.first.respond_to?(:to_sym) ? args.shift : nil
|
1019
|
+
options.push((@arguments || extras[:argument]) ? true : (args.shift ? true : false))
|
1020
|
+
options.push extras
|
1021
|
+
end
|
1022
|
+
end
|