xolo-server 1.0.0 → 1.0.1

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.
@@ -0,0 +1,1216 @@
1
+ # lib/optimist.rb -- optimist command-line processing library
2
+ # Copyright (c) 2008-2014 William Morgan.
3
+ # Copyright (c) 2014 Red Hat, Inc.
4
+ # optimist is licensed under the MIT license.
5
+ # See https://github.com/ManageIQ/optimist
6
+
7
+ # MIT License
8
+ #
9
+ # Permission is hereby granted, free of charge, to any person obtaining
10
+ # a copy of this software and associated documentation files (the
11
+ # "Software"), to deal in the Software without restriction, including
12
+ # without limitation the rights to use, copy, modify, merge, publish,
13
+ # distribute, sublicense, and/or sell copies of the Software, and to
14
+ # permit persons to whom the Software is furnished to do so, subject to
15
+ # the following conditions:
16
+ #
17
+ # The above copyright notice and this permission notice shall be
18
+ # included in all copies or substantial portions of the Software.
19
+ #
20
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27
+
28
+ #################################
29
+ # Added by chrisl@pixar.com:
30
+ # If you'd like to insert blank lines between sections of the
31
+ # help output, call 'insert_blanks' within the Optimist.options {} block
32
+ #
33
+ # This can improve readability with long, complex help output, preventing the
34
+ # 'wall of text', at the cost of longer output.
35
+ #
36
+ ###########
37
+ # Example: without insert_blanks
38
+ ###########
39
+ #
40
+ # Global Options:
41
+ # -v, --version Print version and exit
42
+ # -h, --help Show this help and exit
43
+ # -w, --walkthru Run xadm in interactive mode
44
+ # This causes xadm to present an interactive, menu-and-
45
+ # prompt-driven interface. All command-options given on the
46
+ # command line are ignored, and will be gathered
47
+ # interactively
48
+ #
49
+ ###########
50
+ # With insert_blanks
51
+ ###########
52
+ #
53
+ # Global Options:
54
+ # -v, --version Print version and exit
55
+ #
56
+ # -h, --help Show this help and exit
57
+ #
58
+ # -w, --walkthru Run xadm in interactive mode
59
+ # This causes xadm to present an interactive, menu-and-
60
+ # prompt-driven interface. All command-options given on the
61
+ # command line are ignored, and will be gathered
62
+ # interactively
63
+
64
+ require 'date'
65
+
66
+ module Optimist
67
+
68
+ VERSION = '3.1.0'
69
+
70
+ ## Thrown by Parser in the event of a commandline error. Not needed if
71
+ ## you're using the Optimist::options entry.
72
+ class CommandlineError < StandardError
73
+
74
+ attr_reader :error_code
75
+
76
+ def initialize(msg, error_code = nil)
77
+ super(msg)
78
+ @error_code = error_code
79
+ end
80
+
81
+ end
82
+
83
+ ## Thrown by Parser if the user passes in '-h' or '--help'. Handled
84
+ ## automatically by Optimist#options.
85
+ class HelpNeeded < StandardError
86
+ end
87
+
88
+ ## Thrown by Parser if the user passes in '-v' or '--version'. Handled
89
+ ## automatically by Optimist#options.
90
+ class VersionNeeded < StandardError
91
+ end
92
+
93
+ ## Regex for floating point numbers
94
+ FLOAT_RE = /^-?((\d+(\.\d+)?)|(\.\d+))([eE][-+]?\d+)?$/
95
+
96
+ ## Regex for parameters
97
+ PARAM_RE = /^-(-|\.$|[^\d.])/
98
+
99
+ ## The commandline parser. In typical usage, the methods in this class
100
+ ## will be handled internally by Optimist::options. In this case, only the
101
+ ## #opt, #banner and #version, #depends, and #conflicts methods will
102
+ ## typically be called.
103
+ ##
104
+ ## If you want to instantiate this class yourself (for more complicated
105
+ ## argument-parsing logic), call #parse to actually produce the output hash,
106
+ ## and consider calling it from within
107
+ ## Optimist::with_standard_exception_handling.
108
+ class Parser
109
+
110
+ ## The registry is a class-instance-variable map of option aliases to their subclassed Option class.
111
+ @registry = {}
112
+
113
+ ## The Option subclasses are responsible for registering themselves using this function.
114
+ def self.register(lookup, klass)
115
+ @registry[lookup.to_sym] = klass
116
+ end
117
+
118
+ ## Gets the class from the registry.
119
+ ## Can be given either a class-name, e.g. Integer, a string, e.g "integer", or a symbol, e.g :integer
120
+ def self.registry_getopttype(type)
121
+ return nil unless type
122
+
123
+ if type.respond_to?(:name)
124
+ type = type.name
125
+ lookup = type.downcase.to_sym
126
+ else
127
+ lookup = type.to_sym
128
+ end
129
+ unless @registry.has_key?(lookup)
130
+ raise ArgumentError,
131
+ "Unsupported argument type '#{type}', registry lookup '#{lookup}'"
132
+ end
133
+
134
+ @registry[lookup].new
135
+ end
136
+
137
+ INVALID_SHORT_ARG_REGEX = /[\d-]/ # :nodoc:
138
+
139
+ ## The values from the commandline that were not interpreted by #parse.
140
+ attr_reader :leftovers
141
+
142
+ ## The complete configuration hashes for each option. (Mainly useful
143
+ ## for testing.)
144
+ attr_reader :specs
145
+
146
+ ## A flag that determines whether or not to raise an error if the parser is passed one or more
147
+ ## options that were not registered ahead of time. If 'true', then the parser will simply
148
+ ## ignore options that it does not recognize.
149
+ attr_accessor :ignore_invalid_options
150
+
151
+ ## Initializes the parser, and instance-evaluates any block given.
152
+ def initialize(*a, &b)
153
+ @version = nil
154
+ @leftovers = []
155
+ @specs = {}
156
+ @long = {}
157
+ @short = {}
158
+ @order = []
159
+ @constraints = []
160
+ @stop_words = []
161
+ @stop_on_unknown = false
162
+ @educate_on_error = false
163
+ @synopsis = nil
164
+ @usage = nil
165
+
166
+ # instance_eval(&b) if b # can't take arguments
167
+ cloaker(&b).bind(self).call(*a) if b
168
+ end
169
+
170
+ ## Define an option. +name+ is the option name, a unique identifier
171
+ ## for the option that you will use internally, which should be a
172
+ ## symbol or a string. +desc+ is a string description which will be
173
+ ## displayed in help messages.
174
+ ##
175
+ ## Takes the following optional arguments:
176
+ ##
177
+ ## [+:long+] Specify the long form of the argument, i.e. the form with two dashes. If unspecified, will be automatically derived based on the argument name by turning the +name+ option into a string, and replacing any _'s by -'s.
178
+ ## [+:short+] Specify the short form of the argument, i.e. the form with one dash. If unspecified, will be automatically derived from +name+. Use :none: to not have a short value.
179
+ ## [+:type+] Require that the argument take a parameter or parameters of type +type+. For a single parameter, the value can be a member of +SINGLE_ARG_TYPES+, or a corresponding Ruby class (e.g. +Integer+ for +:int+). For multiple-argument parameters, the value can be any member of +MULTI_ARG_TYPES+ constant. If unset, the default argument type is +:flag+, meaning that the argument does not take a parameter. The specification of +:type+ is not necessary if a +:default+ is given.
180
+ ## [+:default+] Set the default value for an argument. Without a default value, the hash returned by #parse (and thus Optimist::options) will have a +nil+ value for this key unless the argument is given on the commandline. The argument type is derived automatically from the class of the default value given, so specifying a +:type+ is not necessary if a +:default+ is given. (But see below for an important caveat when +:multi+: is specified too.) If the argument is a flag, and the default is set to +true+, then if it is specified on the the commandline the value will be +false+.
181
+ ## [+:required+] If set to +true+, the argument must be provided on the commandline.
182
+ ## [+:multi+] If set to +true+, allows multiple occurrences of the option on the commandline. Otherwise, only a single instance of the option is allowed. (Note that this is different from taking multiple parameters. See below.)
183
+ ##
184
+ ## Note that there are two types of argument multiplicity: an argument
185
+ ## can take multiple values, e.g. "--arg 1 2 3". An argument can also
186
+ ## be allowed to occur multiple times, e.g. "--arg 1 --arg 2".
187
+ ##
188
+ ## Arguments that take multiple values should have a +:type+ parameter
189
+ ## drawn from +MULTI_ARG_TYPES+ (e.g. +:strings+), or a +:default:+
190
+ ## value of an array of the correct type (e.g. [String]). The
191
+ ## value of this argument will be an array of the parameters on the
192
+ ## commandline.
193
+ ##
194
+ ## Arguments that can occur multiple times should be marked with
195
+ ## +:multi+ => +true+. The value of this argument will also be an array.
196
+ ## In contrast with regular non-multi options, if not specified on
197
+ ## the commandline, the default value will be [], not nil.
198
+ ##
199
+ ## These two attributes can be combined (e.g. +:type+ => +:strings+,
200
+ ## +:multi+ => +true+), in which case the value of the argument will be
201
+ ## an array of arrays.
202
+ ##
203
+ ## There's one ambiguous case to be aware of: when +:multi+: is true and a
204
+ ## +:default+ is set to an array (of something), it's ambiguous whether this
205
+ ## is a multi-value argument as well as a multi-occurrence argument.
206
+ ## In thise case, Optimist assumes that it's not a multi-value argument.
207
+ ## If you want a multi-value, multi-occurrence argument with a default
208
+ ## value, you must specify +:type+ as well.
209
+
210
+ def opt(name, desc = '', opts = {}, &b)
211
+ opts[:callback] ||= b if block_given?
212
+ opts[:desc] ||= desc
213
+
214
+ o = Option.create(name, desc, opts)
215
+
216
+ raise ArgumentError, "you already have an argument named '#{name}'" if @specs.member? o.name
217
+
218
+ if @long[o.long]
219
+ raise ArgumentError,
220
+ "long option name #{o.long.inspect} is already taken; please specify a (different) :long"
221
+ end
222
+ if @short[o.short]
223
+ raise ArgumentError,
224
+ "short option name #{o.short.inspect} is already taken; please specify a (different) :short"
225
+ end
226
+
227
+ @long[o.long] = o.name
228
+ @short[o.short] = o.name if o.short?
229
+ @specs[o.name] = o
230
+ @order << [:opt, o.name]
231
+ end
232
+
233
+ ## Sets the version string. If set, the user can request the version
234
+ ## on the commandline. Should probably be of the form "<program name>
235
+ ## <version number>".
236
+ def version(s = nil)
237
+ s ? @version = s : @version
238
+ end
239
+
240
+ ## Sets the usage string. If set the message will be printed as the
241
+ ## first line in the help (educate) output and ending in two new
242
+ ## lines.
243
+ def usage(s = nil)
244
+ s ? @usage = s : @usage
245
+ end
246
+
247
+ ## Adds a synopsis (command summary description) right below the
248
+ ## usage line, or as the first line if usage isn't specified.
249
+ def synopsis(s = nil)
250
+ s ? @synopsis = s : @synopsis
251
+ end
252
+
253
+ ## Adds text to the help display. Can be interspersed with calls to
254
+ ## #opt to build a multi-section help page.
255
+ def banner(s)
256
+ @order << [:text, s]
257
+ end
258
+ alias text banner
259
+
260
+ ## Marks two (or more!) options as requiring each other. Only handles
261
+ ## undirected (i.e., mutual) dependencies. Directed dependencies are
262
+ ## better modeled with Optimist::die.
263
+ def depends(*syms)
264
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
265
+ @constraints << [:depends, syms]
266
+ end
267
+
268
+ ## Marks two (or more!) options as conflicting.
269
+ def conflicts(*syms)
270
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
271
+ @constraints << [:conflicts, syms]
272
+ end
273
+
274
+ ## Marks two (or more!) options as required but mutually exclusive.
275
+ def either(*syms)
276
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
277
+ @constraints << [:conflicts, syms]
278
+ @constraints << [:either, syms]
279
+ end
280
+
281
+ ## Defines a set of words which cause parsing to terminate when
282
+ ## encountered, such that any options to the left of the word are
283
+ ## parsed as usual, and options to the right of the word are left
284
+ ## intact.
285
+ ##
286
+ ## A typical use case would be for subcommand support, where these
287
+ ## would be set to the list of subcommands. A subsequent Optimist
288
+ ## invocation would then be used to parse subcommand options, after
289
+ ## shifting the subcommand off of ARGV.
290
+ def stop_on(*words)
291
+ @stop_words = [*words].flatten
292
+ end
293
+
294
+ ## Put a blank line between the help output for each option
295
+ ## HACK by chrisl - put a blank line between each option in the help, if desired
296
+ def insert_blanks
297
+ @insert_blanks = true
298
+ end
299
+
300
+ ## Similar to #stop_on, but stops on any unknown word when encountered
301
+ ## (unless it is a parameter for an argument). This is useful for
302
+ ## cases where you don't know the set of subcommands ahead of time,
303
+ ## i.e., without first parsing the global options.
304
+ def stop_on_unknown
305
+ @stop_on_unknown = true
306
+ end
307
+
308
+ ## Instead of displaying "Try --help for help." on an error
309
+ ## display the usage (via educate)
310
+ def educate_on_error
311
+ @educate_on_error = true
312
+ end
313
+
314
+ ## Parses the commandline. Typically called by Optimist::options,
315
+ ## but you can call it directly if you need more control.
316
+ ##
317
+ ## throws CommandlineError, HelpNeeded, and VersionNeeded exceptions.
318
+ def parse(cmdline = ARGV)
319
+ vals = {}
320
+ required = {}
321
+
322
+ opt :version, 'Print version and exit' if @version && !(@specs[:version] || @long['version'])
323
+ opt :help, 'Show this message' unless @specs[:help] || @long['help']
324
+
325
+ @specs.each do |sym, opts|
326
+ required[sym] = true if opts.required?
327
+ vals[sym] = opts.default
328
+ vals[sym] = [] if opts.multi && !opts.default # multi arguments default to [], not nil
329
+ end
330
+
331
+ resolve_default_short_options!
332
+
333
+ ## resolve symbols
334
+ given_args = {}
335
+ @leftovers = each_arg cmdline do |arg, params|
336
+ ## handle --no- forms
337
+ arg, negative_given = if arg =~ /^--no-([^-]\S*)$/
338
+ ["--#{::Regexp.last_match(1)}", true]
339
+ else
340
+ [arg, false]
341
+ end
342
+
343
+ sym = case arg
344
+ when /^-([^-])$/ then @short[::Regexp.last_match(1)]
345
+ when /^--([^-]\S*)$/ then @long[::Regexp.last_match(1)] || @long["no-#{::Regexp.last_match(1)}"]
346
+ else raise CommandlineError, "invalid argument syntax: '#{arg}'"
347
+ end
348
+
349
+ sym = nil if arg =~ /--no-/ # explicitly invalidate --no-no- arguments
350
+
351
+ next nil if ignore_invalid_options && !sym
352
+ raise CommandlineError, "unknown argument '#{arg}'" unless sym
353
+
354
+ if given_args.include?(sym) && !@specs[sym].multi?
355
+ raise CommandlineError, "option '#{arg}' specified multiple times"
356
+ end
357
+
358
+ given_args[sym] ||= {}
359
+ given_args[sym][:arg] = arg
360
+ given_args[sym][:negative_given] = negative_given
361
+ given_args[sym][:params] ||= []
362
+
363
+ # The block returns the number of parameters taken.
364
+ num_params_taken = 0
365
+
366
+ unless params.empty?
367
+ if @specs[sym].single_arg?
368
+ given_args[sym][:params] << params[0, 1] # take the first parameter
369
+ num_params_taken = 1
370
+ elsif @specs[sym].multi_arg?
371
+ given_args[sym][:params] << params # take all the parameters
372
+ num_params_taken = params.size
373
+ end
374
+ end
375
+
376
+ num_params_taken
377
+ end
378
+
379
+ ## check for version and help args
380
+ raise VersionNeeded if given_args.include? :version
381
+ raise HelpNeeded if given_args.include? :help
382
+
383
+ ## check constraint satisfaction
384
+ @constraints.each do |type, syms|
385
+ constraint_sym = syms.find { |sym| given_args[sym] }
386
+
387
+ case type
388
+ when :depends
389
+ next unless constraint_sym
390
+
391
+ syms.each do |sym|
392
+ unless given_args.include? sym
393
+ raise CommandlineError,
394
+ "--#{@specs[constraint_sym].long} requires --#{@specs[sym].long}"
395
+ end
396
+ end
397
+ when :conflicts
398
+ next unless constraint_sym
399
+
400
+ syms.each do |sym|
401
+ if given_args.include?(sym) && (sym != constraint_sym)
402
+ raise CommandlineError,
403
+ "--#{@specs[constraint_sym].long} conflicts with --#{@specs[sym].long}"
404
+ end
405
+ end
406
+ when :either
407
+ if (syms & given_args.keys).size != 1
408
+ raise CommandlineError, "one of #{syms.map do |sym|
409
+ "--#{@specs[sym].long}"
410
+ end.join(', ')} is required"
411
+ end
412
+ end
413
+ end
414
+
415
+ required.each do |sym, _val|
416
+ raise CommandlineError, "option --#{@specs[sym].long} must be specified" unless given_args.include? sym
417
+ end
418
+
419
+ ## parse parameters
420
+ given_args.each do |sym, given_data|
421
+ arg, params, negative_given = given_data.values_at :arg, :params, :negative_given
422
+
423
+ opts = @specs[sym]
424
+ if params.empty? && !opts.flag?
425
+ raise CommandlineError, "option '#{arg}' needs a parameter" unless opts.default
426
+
427
+ params << (opts.array_default? ? opts.default.clone : [opts.default])
428
+ end
429
+
430
+ vals["#{sym}_given".intern] = true # mark argument as specified on the commandline
431
+
432
+ vals[sym] = opts.parse(params, negative_given)
433
+
434
+ if opts.single_arg?
435
+ vals[sym] = if opts.multi? # multiple options, each with a single parameter
436
+ vals[sym].map { |p| p[0] }
437
+ else # single parameter
438
+ vals[sym][0][0]
439
+ end
440
+ elsif opts.multi_arg? && !opts.multi?
441
+ vals[sym] = vals[sym][0] # single option, with multiple parameters
442
+ end
443
+ # else: multiple options, with multiple parameters
444
+
445
+ opts.callback.call(vals[sym]) if opts.callback
446
+ end
447
+
448
+ ## modify input in place with only those
449
+ ## arguments we didn't process
450
+ cmdline.clear
451
+ @leftovers.each { |l| cmdline << l }
452
+
453
+ ## allow openstruct-style accessors
454
+ class << vals
455
+
456
+ def method_missing(m, *_args)
457
+ self[m] || self[m.to_s]
458
+ end
459
+
460
+ end
461
+ vals
462
+ end
463
+
464
+ ## Print the help message to +stream+.
465
+ def educate(stream = $stdout)
466
+ width # HACK: calculate it now; otherwise we have to be careful not to
467
+ # call this unless the cursor's at the beginning of a line.
468
+
469
+ left = {}
470
+ @specs.each { |name, spec| left[name] = spec.educate }
471
+
472
+ leftcol_width = left.values.map(&:length).max || 0
473
+ rightcol_start = leftcol_width + 6 # spaces
474
+
475
+ unless @order.size > 0 && @order.first.first == :text
476
+ command_name = File.basename($0).gsub(/\.[^.]+$/, '')
477
+ stream.puts "Usage: #{command_name} #{@usage}\n" if @usage
478
+ stream.puts "#{@synopsis}\n" if @synopsis
479
+ stream.puts if @usage || @synopsis
480
+ stream.puts "#{@version}\n" if @version
481
+ stream.puts 'Options:'
482
+ end
483
+
484
+ @order.each do |what, opt|
485
+ if what == :text
486
+ stream.puts wrap(opt)
487
+ next
488
+ end
489
+
490
+ spec = @specs[opt]
491
+ stream.printf " %-#{leftcol_width}s ", left[opt]
492
+ desc = spec.description_with_default
493
+
494
+ stream.puts wrap(desc, width: width - rightcol_start - 1, prefix: rightcol_start)
495
+ # HACK: by chrisl - put a blank line between each option in the help, if desired
496
+ stream.puts if @insert_blanks
497
+ end
498
+ end
499
+
500
+ def width # :nodoc:
501
+ @width ||= if $stdout.tty?
502
+ begin
503
+ require 'io/console'
504
+ w = IO.console.winsize.last
505
+ w.to_i > 0 ? w : 80
506
+ rescue LoadError, NoMethodError, Errno::ENOTTY, Errno::EBADF, Errno::EINVAL
507
+ legacy_width
508
+ end
509
+ else
510
+ 80
511
+ end
512
+ end
513
+
514
+ def legacy_width
515
+ # Support for older Rubies where io/console is not available
516
+ `tput cols`.to_i
517
+ rescue Errno::ENOENT
518
+ 80
519
+ end
520
+ private :legacy_width
521
+
522
+ def wrap(str, opts = {}) # :nodoc:
523
+ if str == ''
524
+ ['']
525
+ else
526
+ inner = false
527
+ str.split("\n").map do |s|
528
+ line = wrap_line s, opts.merge(inner: inner)
529
+ inner = true
530
+ line
531
+ end.flatten
532
+ end
533
+ end
534
+
535
+ ## The per-parser version of Optimist::die (see that for documentation).
536
+ def die(arg, msg = nil, error_code = nil)
537
+ if msg.is_a?(Integer)
538
+ error_code = msg
539
+ msg = nil
540
+ end
541
+ if msg
542
+ warn "Error: argument --#{@specs[arg].long} #{msg}."
543
+ else
544
+ warn "Error: #{arg}."
545
+ end
546
+ if @educate_on_error
547
+ $stderr.puts
548
+ educate $stderr
549
+ else
550
+ warn 'Try --help for help.'
551
+ end
552
+ exit(error_code || -1)
553
+ end
554
+
555
+ private
556
+
557
+ ## yield successive arg, parameter pairs
558
+ def each_arg(args)
559
+ remains = []
560
+ i = 0
561
+
562
+ until i >= args.length
563
+ return remains += args[i..-1] if @stop_words.member? args[i]
564
+
565
+ case args[i]
566
+ when /^--$/ # arg terminator
567
+ return remains += args[(i + 1)..-1]
568
+ when /^--(\S+?)=(.*)$/ # long argument with equals
569
+ num_params_taken = yield "--#{::Regexp.last_match(1)}", [::Regexp.last_match(2)]
570
+ if num_params_taken.nil?
571
+ remains << args[i]
572
+ return remains += args[i + 1..-1] if @stop_on_unknown
573
+ end
574
+ i += 1
575
+ when /^--(\S+)$/ # long argument
576
+ params = collect_argument_parameters(args, i + 1)
577
+ num_params_taken = yield args[i], params
578
+
579
+ if num_params_taken.nil?
580
+ remains << args[i]
581
+ return remains += args[i + 1..-1] if @stop_on_unknown
582
+ else
583
+ i += num_params_taken
584
+ end
585
+ i += 1
586
+ when /^-(\S+)$/ # one or more short arguments
587
+ short_remaining = ''
588
+ shortargs = ::Regexp.last_match(1).split(//)
589
+ shortargs.each_with_index do |a, j|
590
+ if j == (shortargs.length - 1)
591
+ params = collect_argument_parameters(args, i + 1)
592
+
593
+ num_params_taken = yield "-#{a}", params
594
+ if num_params_taken
595
+ i += num_params_taken
596
+ else
597
+ short_remaining << a
598
+ if @stop_on_unknown
599
+ remains << "-#{short_remaining}"
600
+ return remains += args[i + 1..-1]
601
+ end
602
+ end
603
+ else
604
+ unless yield "-#{a}", []
605
+ short_remaining << a
606
+ if @stop_on_unknown
607
+ short_remaining += shortargs[j + 1..-1].join
608
+ remains << "-#{short_remaining}"
609
+ return remains += args[i + 1..-1]
610
+ end
611
+ end
612
+ end
613
+ end
614
+
615
+ remains << "-#{short_remaining}" unless short_remaining.empty?
616
+ i += 1
617
+ else
618
+ return remains += args[i..-1] if @stop_on_unknown
619
+
620
+ remains << args[i]
621
+ i += 1
622
+
623
+ end
624
+ end
625
+
626
+ remains
627
+ end
628
+
629
+ def collect_argument_parameters(args, start_at)
630
+ params = []
631
+ pos = start_at
632
+ while args[pos] && args[pos] !~ PARAM_RE && !@stop_words.member?(args[pos])
633
+ params << args[pos]
634
+ pos += 1
635
+ end
636
+ params
637
+ end
638
+
639
+ def resolve_default_short_options!
640
+ @order.each do |type, name|
641
+ opts = @specs[name]
642
+ next if type != :opt || opts.short
643
+
644
+ c = opts.long.split(//).find { |d| d !~ INVALID_SHORT_ARG_REGEX && !@short.member?(d) }
645
+ if c # found a character to use
646
+ opts.short = c
647
+ @short[c] = name
648
+ end
649
+ end
650
+ end
651
+
652
+ def wrap_line(str, opts = {})
653
+ prefix = opts[:prefix] || 0
654
+ width = opts[:width] || (self.width - 1)
655
+ start = 0
656
+ ret = []
657
+ until start > str.length
658
+ nextt =
659
+ if start + width >= str.length
660
+ str.length
661
+ else
662
+ x = str.rindex(/\s/, start + width)
663
+ x = str.index(/\s/, start) if x && x < start
664
+ x || str.length
665
+ end
666
+ ret << (ret.empty? && !opts[:inner] ? '' : ' ' * prefix) + str[start...nextt]
667
+ start = nextt + 1
668
+ end
669
+ ret
670
+ end
671
+
672
+ ## instance_eval but with ability to handle block arguments
673
+ ## thanks to _why: http://redhanded.hobix.com/inspect/aBlockCostume.html
674
+ def cloaker(&b)
675
+ (class << self; self; end).class_eval do
676
+ define_method :cloaker_, &b
677
+ meth = instance_method :cloaker_
678
+ remove_method :cloaker_
679
+ meth
680
+ end
681
+ end
682
+
683
+ end
684
+
685
+ class Option
686
+
687
+ attr_accessor :name, :short, :long, :default
688
+ attr_writer :multi_given
689
+
690
+ def initialize
691
+ @long = nil
692
+ @short = nil
693
+ @name = nil
694
+ @multi_given = false
695
+ @hidden = false
696
+ @default = nil
697
+ @optshash = {}
698
+ end
699
+
700
+ def opts(key)
701
+ @optshash[key]
702
+ end
703
+
704
+ def opts=(o)
705
+ @optshash = o
706
+ end
707
+
708
+ ## Indicates a flag option, which is an option without an argument
709
+ def flag?
710
+ false
711
+ end
712
+
713
+ def single_arg?
714
+ !multi_arg? && !flag?
715
+ end
716
+
717
+ def multi
718
+ @multi_given
719
+ end
720
+ alias multi? multi
721
+
722
+ ## Indicates that this is a multivalued (Array type) argument
723
+ def multi_arg?
724
+ false
725
+ end
726
+ ## note: Option-Types with both multi_arg? and flag? false are single-parameter (normal) options.
727
+
728
+ def array_default?
729
+ default.is_a?(Array)
730
+ end
731
+
732
+ def short?
733
+ short && short != :none
734
+ end
735
+
736
+ def callback
737
+ opts(:callback)
738
+ end
739
+
740
+ def desc
741
+ opts(:desc)
742
+ end
743
+
744
+ def required?
745
+ opts(:required)
746
+ end
747
+
748
+ def parse(_paramlist, _neg_given)
749
+ raise NotImplementedError, 'parse must be overridden for newly registered type'
750
+ end
751
+
752
+ # provide type-format string. default to empty, but user should probably override it
753
+ def type_format
754
+ ''
755
+ end
756
+
757
+ def educate
758
+ (short? ? "-#{short}, " : '') + "--#{long}" + type_format + (flag? && default ? ", --no-#{long}" : '')
759
+ end
760
+
761
+ ## Format the educate-line description including the default-value(s)
762
+ def description_with_default
763
+ return desc unless default
764
+
765
+ default_s = case default
766
+ when $stdout then '<stdout>'
767
+ when $stdin then '<stdin>'
768
+ when $stderr then '<stderr>'
769
+ when Array
770
+ default.join(', ')
771
+ else
772
+ default.to_s
773
+ end
774
+ defword = desc.end_with?('.') ? 'Default' : 'default'
775
+ "#{desc} (#{defword}: #{default_s})"
776
+ end
777
+
778
+ ## Provide a way to register symbol aliases to the Parser
779
+ def self.register_alias(*alias_keys)
780
+ alias_keys.each do |alias_key|
781
+ # pass in the alias-key and the class
782
+ Parser.register(alias_key, self)
783
+ end
784
+ end
785
+
786
+ ## Factory class methods ...
787
+
788
+ # Determines which type of object to create based on arguments passed
789
+ # to +Optimist::opt+. This is trickier in Optimist, than other cmdline
790
+ # parsers (e.g. Slop) because we allow the +default:+ to be able to
791
+ # set the option's type.
792
+ def self.create(name, _desc = '', opts = {}, _settings = {})
793
+ opttype = Optimist::Parser.registry_getopttype(opts[:type])
794
+ opttype_from_default = get_klass_from_default(opts, opttype)
795
+
796
+ if opttype && opttype_from_default && (opttype.class != opttype_from_default.class)
797
+ raise ArgumentError,
798
+ ":type specification and default type don't match (default type is #{opttype_from_default.class})"
799
+ end
800
+
801
+ opt_inst = opttype || opttype_from_default || Optimist::BooleanOption.new
802
+
803
+ ## fill in :long
804
+ opt_inst.long = handle_long_opt(opts[:long], name)
805
+
806
+ ## fill in :short
807
+ opt_inst.short = handle_short_opt(opts[:short])
808
+
809
+ ## fill in :multi
810
+ multi_given = opts[:multi] || false
811
+ opt_inst.multi_given = multi_given
812
+
813
+ ## fill in :default for flags
814
+ defvalue = opts[:default] || opt_inst.default
815
+
816
+ ## autobox :default for :multi (multi-occurrence) arguments
817
+ defvalue = [defvalue] if defvalue && multi_given && !defvalue.is_a?(Array)
818
+ opt_inst.default = defvalue
819
+ opt_inst.name = name
820
+ opt_inst.opts = opts
821
+ opt_inst
822
+ end
823
+
824
+ def self.get_type_from_disdef(optdef, opttype, disambiguated_default)
825
+ if disambiguated_default.is_a? Array
826
+ return(optdef.first.class.name.downcase + 's') unless optdef.empty?
827
+
828
+ raise ArgumentError, 'multiple argument type cannot be deduced from an empty array' unless opttype
829
+ raise ArgumentError, 'multiple argument type must be plural' unless opttype.multi_arg?
830
+
831
+ return nil
832
+
833
+ end
834
+ disambiguated_default.class.name.downcase
835
+ end
836
+
837
+ def self.get_klass_from_default(opts, opttype)
838
+ ## for options with :multi => true, an array default doesn't imply
839
+ ## a multi-valued argument. for that you have to specify a :type
840
+ ## as well. (this is how we disambiguate an ambiguous situation;
841
+ ## see the docs for Parser#opt for details.)
842
+
843
+ disambiguated_default = if opts[:multi] && opts[:default].is_a?(Array) && opttype.nil?
844
+ opts[:default].first
845
+ else
846
+ opts[:default]
847
+ end
848
+
849
+ return nil if disambiguated_default.nil?
850
+
851
+ type_from_default = get_type_from_disdef(opts[:default], opttype, disambiguated_default)
852
+ Optimist::Parser.registry_getopttype(type_from_default)
853
+ end
854
+
855
+ def self.handle_long_opt(lopt, name)
856
+ lopt = lopt ? lopt.to_s : name.to_s.gsub('_', '-')
857
+ lopt = case lopt
858
+ when /^--([^-].*)$/ then ::Regexp.last_match(1)
859
+ when /^[^-]/ then lopt
860
+ else raise ArgumentError, "invalid long option name #{lopt.inspect}"
861
+ end
862
+ end
863
+
864
+ def self.handle_short_opt(sopt)
865
+ sopt = sopt.to_s if sopt && sopt != :none
866
+ sopt = case sopt
867
+ when /^-(.)$/ then ::Regexp.last_match(1)
868
+ when nil, :none, /^.$/ then sopt
869
+ else raise ArgumentError, "invalid short option name '#{sopt.inspect}'"
870
+ end
871
+
872
+ if sopt && (sopt =~ ::Optimist::Parser::INVALID_SHORT_ARG_REGEX)
873
+ raise ArgumentError,
874
+ "a short option name can't be a number or a dash"
875
+ end
876
+ sopt
877
+ end
878
+
879
+ end
880
+
881
+ # Flag option. Has no arguments. Can be negated with "no-".
882
+ class BooleanOption < Option
883
+
884
+ register_alias :flag, :bool, :boolean, :trueclass, :falseclass
885
+ def initialize
886
+ super()
887
+ @default = false
888
+ end
889
+
890
+ def flag?
891
+ true
892
+ end
893
+
894
+ def parse(_paramlist, neg_given)
895
+ (name.to_s =~ /^no_/ ? neg_given : !neg_given)
896
+ end
897
+
898
+ end
899
+
900
+ # Floating point number option class.
901
+ class FloatOption < Option
902
+
903
+ register_alias :float, :double
904
+ def type_format
905
+ '=<f>'
906
+ end
907
+
908
+ def parse(paramlist, _neg_given)
909
+ paramlist.map do |pg|
910
+ pg.map do |param|
911
+ unless param.is_a?(Numeric) || param =~ FLOAT_RE
912
+ raise CommandlineError,
913
+ "option '#{name}' needs a floating-point number"
914
+ end
915
+
916
+ param.to_f
917
+ end
918
+ end
919
+ end
920
+
921
+ end
922
+
923
+ # Integer number option class.
924
+ class IntegerOption < Option
925
+
926
+ register_alias :int, :integer, :fixnum
927
+ def type_format
928
+ '=<i>'
929
+ end
930
+
931
+ def parse(paramlist, _neg_given)
932
+ paramlist.map do |pg|
933
+ pg.map do |param|
934
+ unless param.is_a?(Numeric) || param =~ /^-?[\d_]+$/
935
+ raise CommandlineError,
936
+ "option '#{name}' needs an integer"
937
+ end
938
+
939
+ param.to_i
940
+ end
941
+ end
942
+ end
943
+
944
+ end
945
+
946
+ # Option class for handling IO objects and URLs.
947
+ # Note that this will return the file-handle, not the file-name
948
+ # in the case of file-paths given to it.
949
+ class IOOption < Option
950
+
951
+ register_alias :io
952
+ def type_format
953
+ '=<filename/uri>'
954
+ end
955
+
956
+ def parse(paramlist, _neg_given)
957
+ paramlist.map do |pg|
958
+ pg.map do |param|
959
+ if param =~ /^(stdin|-)$/i
960
+ $stdin
961
+ else
962
+ require 'open-uri'
963
+ begin
964
+ open param
965
+ rescue SystemCallError => e
966
+ raise CommandlineError, "file or url for option '#{name}' cannot be opened: #{e.message}"
967
+ end
968
+ end
969
+ end
970
+ end
971
+ end
972
+
973
+ end
974
+
975
+ # Option class for handling Strings.
976
+ class StringOption < Option
977
+
978
+ register_alias :string
979
+ def type_format
980
+ '=<s>'
981
+ end
982
+
983
+ def parse(paramlist, _neg_given)
984
+ paramlist.map { |pg| pg.map(&:to_s) }
985
+ end
986
+
987
+ end
988
+
989
+ # Option for dates. Uses Chronic if it exists.
990
+ class DateOption < Option
991
+
992
+ register_alias :date
993
+ def type_format
994
+ '=<date>'
995
+ end
996
+
997
+ def parse(paramlist, _neg_given)
998
+ paramlist.map do |pg|
999
+ pg.map do |param|
1000
+ next param if param.is_a?(Date)
1001
+
1002
+ begin
1003
+ begin
1004
+ require 'chronic'
1005
+ time = Chronic.parse(param)
1006
+ rescue LoadError
1007
+ # chronic is not available
1008
+ end
1009
+ time ? Date.new(time.year, time.month, time.day) : Date.parse(param)
1010
+ rescue ArgumentError
1011
+ raise CommandlineError, "option '#{name}' needs a date"
1012
+ end
1013
+ end
1014
+ end
1015
+ end
1016
+
1017
+ end
1018
+
1019
+ # MULTI_OPT_TYPES :
1020
+ ## The set of values that indicate a multiple-parameter option (i.e., that
1021
+ ## takes multiple space-separated values on the commandline) when passed as
1022
+ ## the +:type+ parameter of #opt.
1023
+
1024
+ # Option class for handling multiple Integers
1025
+ class IntegerArrayOption < IntegerOption
1026
+
1027
+ register_alias :fixnums, :ints, :integers
1028
+ def type_format
1029
+ '=<i+>'
1030
+ end
1031
+
1032
+ def multi_arg?
1033
+ true
1034
+ end
1035
+
1036
+ end
1037
+
1038
+ # Option class for handling multiple Floats
1039
+ class FloatArrayOption < FloatOption
1040
+
1041
+ register_alias :doubles, :floats
1042
+ def type_format
1043
+ '=<f+>'
1044
+ end
1045
+
1046
+ def multi_arg?
1047
+ true
1048
+ end
1049
+
1050
+ end
1051
+
1052
+ # Option class for handling multiple Strings
1053
+ class StringArrayOption < StringOption
1054
+
1055
+ register_alias :strings
1056
+ def type_format
1057
+ '=<s+>'
1058
+ end
1059
+
1060
+ def multi_arg?
1061
+ true
1062
+ end
1063
+
1064
+ end
1065
+
1066
+ # Option class for handling multiple dates
1067
+ class DateArrayOption < DateOption
1068
+
1069
+ register_alias :dates
1070
+ def type_format
1071
+ '=<date+>'
1072
+ end
1073
+
1074
+ def multi_arg?
1075
+ true
1076
+ end
1077
+
1078
+ end
1079
+
1080
+ # Option class for handling Files/URLs via 'open'
1081
+ class IOArrayOption < IOOption
1082
+
1083
+ register_alias :ios
1084
+ def type_format
1085
+ '=<filename/uri+>'
1086
+ end
1087
+
1088
+ def multi_arg?
1089
+ true
1090
+ end
1091
+
1092
+ end
1093
+
1094
+ ## The easy, syntactic-sugary entry method into Optimist. Creates a Parser,
1095
+ ## passes the block to it, then parses +args+ with it, handling any errors or
1096
+ ## requests for help or version information appropriately (and then exiting).
1097
+ ## Modifies +args+ in place. Returns a hash of option values.
1098
+ ##
1099
+ ## The block passed in should contain zero or more calls to +opt+
1100
+ ## (Parser#opt), zero or more calls to +text+ (Parser#text), and
1101
+ ## probably a call to +version+ (Parser#version).
1102
+ ##
1103
+ ## The returned block contains a value for every option specified with
1104
+ ## +opt+. The value will be the value given on the commandline, or the
1105
+ ## default value if the option was not specified on the commandline. For
1106
+ ## every option specified on the commandline, a key "<option
1107
+ ## name>_given" will also be set in the hash.
1108
+ ##
1109
+ ## Example:
1110
+ ##
1111
+ ## require 'optimist'
1112
+ ## opts = Optimist::options do
1113
+ ## opt :monkey, "Use monkey mode" # a flag --monkey, defaulting to false
1114
+ ## opt :name, "Monkey name", :type => :string # a string --name <s>, defaulting to nil
1115
+ ## opt :num_limbs, "Number of limbs", :default => 4 # an integer --num-limbs <i>, defaulting to 4
1116
+ ## end
1117
+ ##
1118
+ ## ## if called with no arguments
1119
+ ## p opts # => {:monkey=>false, :name=>nil, :num_limbs=>4, :help=>false}
1120
+ ##
1121
+ ## ## if called with --monkey
1122
+ ## p opts # => {:monkey=>true, :name=>nil, :num_limbs=>4, :help=>false, :monkey_given=>true}
1123
+ ##
1124
+ ## See more examples at http://optimist.rubyforge.org.
1125
+ def options(args = ARGV, *a, &b)
1126
+ @last_parser = Parser.new(*a, &b)
1127
+ with_standard_exception_handling(@last_parser) { @last_parser.parse args }
1128
+ end
1129
+
1130
+ ## If Optimist::options doesn't do quite what you want, you can create a Parser
1131
+ ## object and call Parser#parse on it. That method will throw CommandlineError,
1132
+ ## HelpNeeded and VersionNeeded exceptions when necessary; if you want to
1133
+ ## have these handled for you in the standard manner (e.g. show the help
1134
+ ## and then exit upon an HelpNeeded exception), call your code from within
1135
+ ## a block passed to this method.
1136
+ ##
1137
+ ## Note that this method will call System#exit after handling an exception!
1138
+ ##
1139
+ ## Usage example:
1140
+ ##
1141
+ ## require 'optimist'
1142
+ ## p = Optimist::Parser.new do
1143
+ ## opt :monkey, "Use monkey mode" # a flag --monkey, defaulting to false
1144
+ ## opt :goat, "Use goat mode", :default => true # a flag --goat, defaulting to true
1145
+ ## end
1146
+ ##
1147
+ ## opts = Optimist::with_standard_exception_handling p do
1148
+ ## o = p.parse ARGV
1149
+ ## raise Optimist::HelpNeeded if ARGV.empty? # show help screen
1150
+ ## o
1151
+ ## end
1152
+ ##
1153
+ ## Requires passing in the parser object.
1154
+
1155
+ def with_standard_exception_handling(parser)
1156
+ yield
1157
+ rescue CommandlineError => e
1158
+ parser.die(e.message, nil, e.error_code)
1159
+ rescue HelpNeeded
1160
+ parser.educate
1161
+ exit
1162
+ rescue VersionNeeded
1163
+ puts parser.version
1164
+ exit
1165
+ end
1166
+
1167
+ ## Informs the user that their usage of 'arg' was wrong, as detailed by
1168
+ ## 'msg', and dies. Example:
1169
+ ##
1170
+ ## options do
1171
+ ## opt :volume, :default => 0.0
1172
+ ## end
1173
+ ##
1174
+ ## die :volume, "too loud" if opts[:volume] > 10.0
1175
+ ## die :volume, "too soft" if opts[:volume] < 0.1
1176
+ ##
1177
+ ## In the one-argument case, simply print that message, a notice
1178
+ ## about -h, and die. Example:
1179
+ ##
1180
+ ## options do
1181
+ ## opt :whatever # ...
1182
+ ## end
1183
+ ##
1184
+ ## Optimist::die "need at least one filename" if ARGV.empty?
1185
+ ##
1186
+ ## An exit code can be provide if needed
1187
+ ##
1188
+ ## Optimist::die "need at least one filename", -2 if ARGV.empty?
1189
+ def die(arg, msg = nil, error_code = nil)
1190
+ raise ArgumentError, 'Optimist::die can only be called after Optimist::options' unless @last_parser
1191
+
1192
+ @last_parser.die arg, msg, error_code
1193
+ end
1194
+
1195
+ ## Displays the help message and dies. Example:
1196
+ ##
1197
+ ## options do
1198
+ ## opt :volume, :default => 0.0
1199
+ ## banner <<-EOS
1200
+ ## Usage:
1201
+ ## #$0 [options] <name>
1202
+ ## where [options] are:
1203
+ ## EOS
1204
+ ## end
1205
+ ##
1206
+ ## Optimist::educate if ARGV.empty?
1207
+ def educate
1208
+ raise ArgumentError, 'Optimist::educate can only be called after Optimist::options' unless @last_parser
1209
+
1210
+ @last_parser.educate
1211
+ exit
1212
+ end
1213
+
1214
+ module_function :options, :die, :educate, :with_standard_exception_handling
1215
+
1216
+ end # module