firebase-ruby 0.0.2

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