cft_smartcloud 0.2.2 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|