rbcli 0.3.2 → 0.3.3

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