pangrid 0.2.1

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