kajiki 1.1.1 → 1.2.0

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