ditz 0.4 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,23 +1,33 @@
1
1
  body {
2
- background: white;
3
- color: #202020;
4
- margin-bottom: 2em;
5
- margin-left: auto;
6
- margin-right: auto;
7
- width: 90%;
2
+ font-family: "lucida grande", "sans serif";
3
+ color: #333;
4
+ width: 60em;
5
+ margin: auto;
8
6
  }
9
7
 
10
- div.wrapper {
8
+ div.main {
9
+ padding: 20px;
10
+ margin: auto;
11
+ padding-top: 0;
12
+ margin-top: 1em;
13
+ background-color: #fcfcfc;
11
14
  }
12
15
 
13
- div.footer {
16
+ .person {
17
+ font-family: courier;
14
18
  }
15
19
 
16
20
  a, a:visited {
17
- border-bottom: 1px dotted #03c;
18
- background: inherit;
19
- text-decoration: none;
20
- color: #03c;
21
+ background: inherit;
22
+ text-decoration: none;
23
+ }
24
+
25
+ a {
26
+ color: #003d41;
27
+ }
28
+
29
+ a:visited {
30
+ color: #553d41;
21
31
  }
22
32
 
23
33
  ul {
@@ -29,18 +39,13 @@ p {
29
39
  width: 40em;
30
40
  }
31
41
 
32
- .dimmed {
33
- color: #bbb;
34
- }
35
-
36
- div.on-the-left {
37
- float: left;
38
- width: 39%;
42
+ .inline-status-image {
43
+ position: relative;
44
+ top: 0.2em;
39
45
  }
40
46
 
41
- div.on-the-right {
42
- float: right;
43
- width: 59%;
47
+ .dimmed {
48
+ color: #bbb;
44
49
  }
45
50
 
46
51
  table {
@@ -48,6 +53,10 @@ table {
48
53
  border-spacing: 0;
49
54
  }
50
55
 
56
+ table.log {
57
+ }
58
+
59
+
51
60
  td {
52
61
  border-width: 0;
53
62
  border-style: none;
@@ -56,37 +65,78 @@ td {
56
65
  }
57
66
 
58
67
  tr {
59
- padding-bottom: 5em;
68
+ vertical-align: top;
60
69
  }
61
70
 
62
71
  h1 {
63
- padding: 0.2em;
64
- margin-left: -1em;
65
- margin-right: 1em;
66
- background: #abc;
72
+ padding: 0.5em;
73
+ margin-left: -20px;
74
+ margin-right: -20px;
75
+ background-color: #305275;
76
+ margin-top: 0;
77
+ margin-bottom: 0;
78
+ color: #fff;
79
+ }
80
+
81
+ h2 {
82
+ text-transform: uppercase;
83
+ font-size: smaller;
84
+ margin-top: 1em;
85
+ margin-left: -0.5em;
86
+ width: 100%;
87
+ /*background: #fffbce;*/
88
+ /*background: #628a0d;*/
89
+ padding: 5px;
90
+ color: #305275;
67
91
  }
68
92
 
69
93
  .attrname {
70
94
  text-align: right;
95
+ font-size: smaller;
96
+ }
97
+
98
+ .attrval {
99
+ color: #222;
100
+ }
101
+
102
+ .issue-closed-fixed {
103
+ background-image: "green-check.png";
104
+ }
105
+
106
+ .issue-closed-wontfix {
107
+ background-image: "red-check.png";
108
+ }
109
+
110
+ .issue-closed-reorg {
111
+ background-image: "blue-check.png";
71
112
  }
72
113
 
114
+ .inline-issue-link {
115
+ text-decoration: underline;
116
+ }
117
+
118
+ img {
119
+ border: 0;
120
+ }
121
+
122
+ /*
73
123
  .issuestatus_closed {
74
- background-color: #afa;
124
+ background-color: #f2fff2;
75
125
  text-align: center;
76
126
  }
77
127
 
78
128
  .issuestatus_in_progress {
79
- background-color: #ffa;
129
+ background-color: #fffff2;
80
130
  text-align: center;
81
131
  }
82
132
 
83
133
  .issuestatus_paused {
84
- background-color: #ffa;
134
+ background-color: #fffff2;
85
135
  text-align: center;
86
136
  }
87
137
 
88
138
  .issuestatus_unstarted {
89
- background-color: #faa;
139
+ background-color: #fff2f2;
90
140
  text-align: center;
91
141
  }
92
142
 
@@ -105,23 +155,72 @@ h1 {
105
155
  .issuestatus_unstarted a {
106
156
  color: #202020;
107
157
  }
158
+ */
108
159
 
109
- .logwhat {
160
+ div.footer {
161
+ font-size: small;
162
+ padding-left: 20px;
163
+ padding-right: 20px;
164
+ padding-top: 5px;
165
+ padding-bottom: 5px;
166
+ margin: auto;
167
+ background: #305275;
168
+ color: #fffee7;
110
169
  }
111
170
 
112
- .logentryeven {
113
- background: #eee;
171
+ .footer a {
172
+ color: #508d91;
114
173
  }
115
174
 
116
- .logentryodd {
117
- background: #eee;
175
+ .even-row {
176
+ font-size: smaller;
177
+ background-color: #f2f2f2;
178
+ }
179
+
180
+ .odd-row {
181
+ font-size: smaller;
182
+ background-color: #f2f2f2;
183
+ }
184
+
185
+ .backptr {
186
+ font-size: smaller;
187
+ width: 100%;
188
+ text-align: left;
189
+ padding-bottom: 1em;
190
+ margin-top: 0;
118
191
  }
119
192
 
120
193
  .logcomment {
121
- padding-left: 3em;
194
+ padding-left: 4em;
195
+ font-size: smaller;
122
196
  }
123
197
 
124
- p.footer {
125
- font-size: small;
198
+ .id {
199
+ font-family: courier;
126
200
  }
127
201
 
202
+ .description {
203
+ background: #f2f2f2;
204
+ padding-left: 1em;
205
+ padding-right: 1em;
206
+ padding-top: 0.5em;
207
+ padding-bottom: 0.5em;
208
+ }
209
+
210
+ .message {
211
+ }
212
+
213
+ .littledate {
214
+ font-size: smaller;
215
+ }
216
+
217
+ .progress-meter-done {
218
+ background-color: #03af00;
219
+ }
220
+
221
+ .progress-meter-undone {
222
+ background-color: #ddd;
223
+ }
224
+
225
+ .progress-meter {
226
+ }
@@ -0,0 +1,614 @@
1
+ ## lib/trollop.rb -- trollop command-line processing library
2
+ ## Author:: William Morgan (mailto: wmorgan-trollop@masanjin.net)
3
+ ## Copyright:: Copyright 2007 William Morgan
4
+ ## License:: GNU GPL version 2
5
+
6
+ module Trollop
7
+
8
+ VERSION = "1.8.2"
9
+
10
+ ## Thrown by Parser in the event of a commandline error. Not needed if
11
+ ## you're using the Trollop::options entry.
12
+ class CommandlineError < StandardError; end
13
+
14
+ ## Thrown by Parser if the user passes in '-h' or '--help'. Handled
15
+ ## automatically by Trollop#options.
16
+ class HelpNeeded < StandardError; end
17
+
18
+ ## Thrown by Parser if the user passes in '-h' or '--version'. Handled
19
+ ## automatically by Trollop#options.
20
+ class VersionNeeded < StandardError; end
21
+
22
+ ## Regex for floating point numbers
23
+ FLOAT_RE = /^-?((\d+(\.\d+)?)|(\.\d+))$/
24
+
25
+ ## Regex for parameters
26
+ PARAM_RE = /^-(-|\.$|[^\d\.])/
27
+
28
+ ## The commandline parser. In typical usage, the methods in this class
29
+ ## will be handled internally by Trollop#options, in which case only the
30
+ ## methods #opt, #banner and #version, #depends, and #conflicts will
31
+ ## typically be called.
32
+ class Parser
33
+
34
+ ## The set of values that indicate a flag type of option when one of
35
+ ## the values is given to the :type parameter to #opt.
36
+ FLAG_TYPES = [:flag, :bool, :boolean]
37
+
38
+ ## The set of values that indicate an option that takes a single
39
+ ## parameter when one of the values is given to the :type parameter to
40
+ ## #opt.
41
+ SINGLE_ARG_TYPES = [:int, :integer, :string, :double, :float]
42
+
43
+ ## The set of values that indicate an option that takes multiple
44
+ ## parameters when one of the values is given to the :type parameter to
45
+ ## #opt.
46
+ MULTI_ARG_TYPES = [:ints, :integers, :strings, :doubles, :floats]
47
+
48
+ ## The set of values specifiable as the :type parameter to #opt.
49
+ TYPES = FLAG_TYPES + SINGLE_ARG_TYPES + MULTI_ARG_TYPES
50
+
51
+ INVALID_SHORT_ARG_REGEX = /[\d-]/ #:nodoc:
52
+
53
+ ## The values from the commandline that were not interpreted by #parse.
54
+ attr_reader :leftovers
55
+
56
+ ## The complete configuration hashes for each option. (Mainly useful
57
+ ## for testing.)
58
+ attr_reader :specs
59
+
60
+ ## Initializes the parser, and instance-evaluates any block given.
61
+ def initialize *a, &b
62
+ @version = nil
63
+ @leftovers = []
64
+ @specs = {}
65
+ @long = {}
66
+ @short = {}
67
+ @order = []
68
+ @constraints = []
69
+ @stop_words = []
70
+ @stop_on_unknown = false
71
+
72
+ #instance_eval(&b) if b # can't take arguments
73
+ cloaker(&b).bind(self).call(*a) if b
74
+ end
75
+
76
+ ## Add an option. 'name' is the argument name, a unique identifier
77
+ ## for the option that you will use internally. 'desc' a string
78
+ ## description which will be displayed in help messages. Takes the
79
+ ## following optional arguments:
80
+ ##
81
+ ## * :long: Specify the long form of the argument, i.e. the form
82
+ ## with two dashes. If unspecified, will be automatically derived
83
+ ## based on the argument name.
84
+ ## * :short: Specify the short form of the argument, i.e. the form
85
+ ## with one dash. If unspecified, will be automatically derived
86
+ ## based on the argument name.
87
+ ## * :type: Require that the argument take a parameter or parameters
88
+ ## of type 'type'. For a single parameter, the value can be a
89
+ ## member of the SINGLE_ARG_TYPES constant or a corresponding class
90
+ ## (e.g. Integer for :int). For multiple parameters, the value can
91
+ ## be a member of the MULTI_ARG_TYPES constant. If unset, the
92
+ ## default argument type is :flag, meaning that the argument does
93
+ ## not take a parameter. The specification of :type is not
94
+ ## necessary if :default is given.
95
+ ## * :default: Set the default value for an argument. Without a
96
+ ## default value, the hash returned by #parse (and thus
97
+ ## Trollop#options) will not contain the argument unless it is
98
+ ## given on the commandline. The argument type is derived
99
+ ## automatically from the class of the default value given, if
100
+ ## any. Specifying a :flag argument on the commandline whose
101
+ ## default value is true will change its value to false.
102
+ ## * :required: If set to true, the argument must be provided on the
103
+ ## commandline.
104
+ ## * :multi: If set to true, allows multiple instances of the
105
+ ## option. Otherwise, only a single instance of the option is
106
+ ## allowed.
107
+ def opt name, desc="", opts={}
108
+ raise ArgumentError, "you already have an argument named '#{name}'" if @specs.member? name
109
+
110
+ ## fill in :type
111
+ opts[:type] =
112
+ case opts[:type]
113
+ when :flag, :boolean, :bool; :flag
114
+ when :int, :integer; :int
115
+ when :ints, :integers; :ints
116
+ when :string; :string
117
+ when :strings; :strings
118
+ when :double, :float; :float
119
+ when :doubles, :floats; :floats
120
+ when Class
121
+ case opts[:type].to_s # sigh... there must be a better way to do this
122
+ when 'TrueClass', 'FalseClass'; :flag
123
+ when 'String'; :string
124
+ when 'Integer'; :int
125
+ when 'Float'; :float
126
+ else
127
+ raise ArgumentError, "unsupported argument type '#{opts[:type].class.name}'"
128
+ end
129
+ when nil; nil
130
+ else
131
+ raise ArgumentError, "unsupported argument type '#{opts[:type]}'" unless TYPES.include?(opts[:type])
132
+ end
133
+
134
+ type_from_default =
135
+ case opts[:default]
136
+ when Integer; :int
137
+ when Numeric; :float
138
+ when TrueClass, FalseClass; :flag
139
+ when String; :string
140
+ when Array
141
+ if opts[:default].empty?
142
+ raise ArgumentError, "multiple argument type cannot be deduced from an empty array for '#{opts[:default][0].class.name}'"
143
+ end
144
+ case opts[:default][0] # the first element determines the types
145
+ when Integer; :ints
146
+ when Numeric; :floats
147
+ when String; :strings
148
+ else
149
+ raise ArgumentError, "unsupported multiple argument type '#{opts[:default][0].class.name}'"
150
+ end
151
+ when nil; nil
152
+ else
153
+ raise ArgumentError, "unsupported argument type '#{opts[:default].class.name}'"
154
+ end
155
+
156
+ raise ArgumentError, ":type specification and default type don't match" if opts[:type] && type_from_default && opts[:type] != type_from_default
157
+
158
+ opts[:type] = (opts[:type] || type_from_default || :flag)
159
+
160
+ ## fill in :long
161
+ opts[:long] = opts[:long] ? opts[:long].to_s : name.to_s.gsub("_", "-")
162
+ opts[:long] =
163
+ case opts[:long]
164
+ when /^--([^-].*)$/
165
+ $1
166
+ when /^[^-]/
167
+ opts[:long]
168
+ else
169
+ raise ArgumentError, "invalid long option name #{opts[:long].inspect}"
170
+ end
171
+ raise ArgumentError, "long option name #{opts[:long].inspect} is already taken; please specify a (different) :long" if @long[opts[:long]]
172
+
173
+ ## fill in :short
174
+ opts[:short] = opts[:short].to_s if opts[:short] unless opts[:short] == :none
175
+ opts[:short] =
176
+ case opts[:short]
177
+ when nil
178
+ c = opts[:long].split(//).find { |c| c !~ INVALID_SHORT_ARG_REGEX && !@short.member?(c) }
179
+ raise ArgumentError, "can't generate a short option name for #{opts[:long].inspect}: out of unique characters" unless c
180
+ c
181
+ when /^-(.)$/
182
+ $1
183
+ when /^.$/
184
+ opts[:short]
185
+ when :none
186
+ nil
187
+ else
188
+ raise ArgumentError, "invalid short option name '#{opts[:short].inspect}'"
189
+ end
190
+ if opts[:short]
191
+ raise ArgumentError, "short option name #{opts[:short].inspect} is already taken; please specify a (different) :short" if @short[opts[:short]]
192
+ raise ArgumentError, "a short option name can't be a number or a dash" if opts[:short] =~ INVALID_SHORT_ARG_REGEX
193
+ end
194
+
195
+ ## fill in :default for flags
196
+ opts[:default] = false if opts[:type] == :flag && opts[:default].nil?
197
+
198
+ ## fill in :multi
199
+ opts[:multi] ||= false
200
+
201
+ opts[:desc] ||= desc
202
+ @long[opts[:long]] = name
203
+ @short[opts[:short]] = name if opts[:short]
204
+ @specs[name] = opts
205
+ @order << [:opt, name]
206
+ end
207
+
208
+ ## Sets the version string. If set, the user can request the version
209
+ ## on the commandline. Should be of the form "<program name>
210
+ ## <version number>".
211
+ def version s=nil; @version = s if s; @version end
212
+
213
+ ## Adds text to the help display.
214
+ def banner s; @order << [:text, s] end
215
+ alias :text :banner
216
+
217
+ ## Marks two (or more!) options as requiring each other. Only handles
218
+ ## undirected (i.e., mutual) dependencies. Directed dependencies are
219
+ ## better modeled with Trollop::die.
220
+ def depends *syms
221
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
222
+ @constraints << [:depends, syms]
223
+ end
224
+
225
+ ## Marks two (or more!) options as conflicting.
226
+ def conflicts *syms
227
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
228
+ @constraints << [:conflicts, syms]
229
+ end
230
+
231
+ ## Defines a set of words which cause parsing to terminate when encountered,
232
+ ## such that any options to the left of the word are parsed as usual, and
233
+ ## options to the right of the word are left intact.
234
+ ##
235
+ ## A typical use case would be for subcommand support, where these would be
236
+ ## set to the list of subcommands. A subsequent Trollop invocation would
237
+ ## then be used to parse subcommand options.
238
+ def stop_on *words
239
+ @stop_words = [*words].flatten
240
+ end
241
+
242
+ ## Similar to stop_on, but stops on any unknown word when encountered (unless
243
+ ## it is a parameter for an argument).
244
+ def stop_on_unknown
245
+ @stop_on_unknown = true
246
+ end
247
+
248
+ ## yield successive arg, parameter pairs
249
+ def each_arg args # :nodoc:
250
+ remains = []
251
+ i = 0
252
+
253
+ until i >= args.length
254
+ if @stop_words.member? args[i]
255
+ remains += args[i .. -1]
256
+ return remains
257
+ end
258
+ case args[i]
259
+ when /^--$/ # arg terminator
260
+ remains += args[(i + 1) .. -1]
261
+ return remains
262
+ when /^--(\S+?)=(\S+)$/ # long argument with equals
263
+ yield "--#{$1}", [$2]
264
+ i += 1
265
+ when /^--(\S+)$/ # long argument
266
+ params = collect_argument_parameters(args, i + 1)
267
+ unless params.empty?
268
+ num_params_taken = yield args[i], params
269
+ unless num_params_taken
270
+ if @stop_on_unknown
271
+ remains += args[i + 1 .. -1]
272
+ return remains
273
+ else
274
+ remains += params
275
+ end
276
+ end
277
+ i += 1 + num_params_taken
278
+ else # long argument no parameter
279
+ yield args[i], nil
280
+ i += 1
281
+ end
282
+ when /^-(\S+)$/ # one or more short arguments
283
+ shortargs = $1.split(//)
284
+ shortargs.each_with_index do |a, j|
285
+ if j == (shortargs.length - 1)
286
+ params = collect_argument_parameters(args, i + 1)
287
+ unless params.empty?
288
+ num_params_taken = yield "-#{a}", params
289
+ unless num_params_taken
290
+ if @stop_on_unknown
291
+ remains += args[i + 1 .. -1]
292
+ return remains
293
+ else
294
+ remains += params
295
+ end
296
+ end
297
+ i += 1 + num_params_taken
298
+ else # argument no parameter
299
+ yield "-#{a}", nil
300
+ i += 1
301
+ end
302
+ else
303
+ yield "-#{a}", nil
304
+ end
305
+ end
306
+ else
307
+ if @stop_on_unknown
308
+ remains += args[i .. -1]
309
+ return remains
310
+ else
311
+ remains << args[i]
312
+ i += 1
313
+ end
314
+ end
315
+ end
316
+
317
+ remains
318
+ end
319
+
320
+ def parse cmdline #:nodoc:
321
+ vals = {}
322
+ required = {}
323
+
324
+ opt :version, "Print version and exit" if @version unless @specs[:version] || @long["version"]
325
+ opt :help, "Show this message" unless @specs[:help] || @long["help"]
326
+
327
+ @specs.each do |sym, opts|
328
+ required[sym] = true if opts[:required]
329
+ vals[sym] = opts[:default]
330
+ end
331
+
332
+ ## resolve symbols
333
+ given_args = {}
334
+ @leftovers = each_arg cmdline do |arg, params|
335
+ sym =
336
+ case arg
337
+ when /^-([^-])$/
338
+ @short[$1]
339
+ when /^--([^-]\S*)$/
340
+ @long[$1]
341
+ else
342
+ raise CommandlineError, "invalid argument syntax: '#{arg}'"
343
+ end
344
+ raise CommandlineError, "unknown argument '#{arg}'" unless sym
345
+
346
+ if given_args.include?(sym) && !@specs[sym][:multi]
347
+ raise CommandlineError, "option '#{arg}' specified multiple times"
348
+ end
349
+
350
+ given_args[sym] ||= {}
351
+
352
+ given_args[sym][:arg] = arg
353
+ given_args[sym][:params] ||= []
354
+
355
+ # The block returns the number of parameters taken.
356
+ num_params_taken = 0
357
+
358
+ unless params.nil?
359
+ if SINGLE_ARG_TYPES.include?(@specs[sym][:type])
360
+ given_args[sym][:params] << params[0, 1] # take the first parameter
361
+ num_params_taken = 1
362
+ elsif MULTI_ARG_TYPES.include?(@specs[sym][:type])
363
+ given_args[sym][:params] << params # take all the parameters
364
+ num_params_taken = params.size
365
+ end
366
+ end
367
+
368
+ num_params_taken
369
+ end
370
+
371
+ ## check for version and help args
372
+ raise VersionNeeded if given_args.include? :version
373
+ raise HelpNeeded if given_args.include? :help
374
+
375
+ ## check constraint satisfaction
376
+ @constraints.each do |type, syms|
377
+ constraint_sym = syms.find { |sym| given_args[sym] }
378
+ next unless constraint_sym
379
+
380
+ case type
381
+ when :depends
382
+ syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym][:long]} requires --#{@specs[sym][:long]}" unless given_args.include? sym }
383
+ when :conflicts
384
+ syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym][:long]} conflicts with --#{@specs[sym][:long]}" if given_args.include?(sym) && (sym != constraint_sym) }
385
+ end
386
+ end
387
+
388
+ required.each do |sym, val|
389
+ raise CommandlineError, "option '#{sym}' must be specified" unless given_args.include? sym
390
+ end
391
+
392
+ ## parse parameters
393
+ given_args.each do |sym, given_data|
394
+ arg = given_data[:arg]
395
+ params = given_data[:params]
396
+
397
+ opts = @specs[sym]
398
+ raise CommandlineError, "option '#{arg}' needs a parameter" if params.empty? && opts[:type] != :flag
399
+
400
+ case opts[:type]
401
+ when :flag
402
+ vals[sym] = !opts[:default]
403
+ when :int, :ints
404
+ vals[sym] = params.map { |pg| pg.map { |p| parse_integer_parameter p, arg } }
405
+ when :float, :floats
406
+ vals[sym] = params.map { |pg| pg.map { |p| parse_float_parameter p, arg } }
407
+ when :string, :strings
408
+ vals[sym] = params.map { |pg| pg.map { |p| p.to_s } }
409
+ end
410
+
411
+ if SINGLE_ARG_TYPES.include?(opts[:type])
412
+ unless opts[:multi] # single parameter
413
+ vals[sym] = vals[sym][0][0]
414
+ else # multiple options, each with a single parameter
415
+ vals[sym] = vals[sym].map { |p| p[0] }
416
+ end
417
+ elsif MULTI_ARG_TYPES.include?(opts[:type]) && !opts[:multi]
418
+ vals[sym] = vals[sym][0] # single option, with multiple parameters
419
+ end
420
+ # else: multiple options, with multiple parameters
421
+ end
422
+
423
+ vals
424
+ end
425
+
426
+ def parse_integer_parameter param, arg #:nodoc:
427
+ raise CommandlineError, "option '#{arg}' needs an integer" unless param =~ /^\d+$/
428
+ param.to_i
429
+ end
430
+
431
+ def parse_float_parameter param, arg #:nodoc:
432
+ raise CommandlineError, "option '#{arg}' needs a floating-point number" unless param =~ FLOAT_RE
433
+ param.to_f
434
+ end
435
+
436
+ def collect_argument_parameters args, start_at #:nodoc:
437
+ params = []
438
+ pos = start_at
439
+ while args[pos] && args[pos] !~ PARAM_RE && !@stop_words.member?(args[pos]) do
440
+ params << args[pos]
441
+ pos += 1
442
+ end
443
+ params
444
+ end
445
+
446
+ def width #:nodoc:
447
+ @width ||=
448
+ if $stdout.tty?
449
+ begin
450
+ require 'curses'
451
+ Curses::init_screen
452
+ x = Curses::cols
453
+ Curses::close_screen
454
+ x
455
+ rescue Exception
456
+ 80
457
+ end
458
+ else
459
+ 80
460
+ end
461
+ end
462
+
463
+ ## Print the help message to 'stream'.
464
+ def educate stream=$stdout
465
+ width # just calculate it now; otherwise we have to be careful not to
466
+ # call this unless the cursor's at the beginning of a line.
467
+
468
+ left = {}
469
+ @specs.each do |name, spec|
470
+ left[name] = "--#{spec[:long]}" +
471
+ (spec[:short] ? ", -#{spec[:short]}" : "") +
472
+ case spec[:type]
473
+ when :flag; ""
474
+ when :int; " <i>"
475
+ when :ints; " <i+>"
476
+ when :string; " <s>"
477
+ when :strings; " <s+>"
478
+ when :float; " <f>"
479
+ when :floats; " <f+>"
480
+ end
481
+ end
482
+
483
+ leftcol_width = left.values.map { |s| s.length }.max || 0
484
+ rightcol_start = leftcol_width + 6 # spaces
485
+
486
+ unless @order.size > 0 && @order.first.first == :text
487
+ stream.puts "#@version\n" if @version
488
+ stream.puts "Options:"
489
+ end
490
+
491
+ @order.each do |what, opt|
492
+ if what == :text
493
+ stream.puts wrap(opt)
494
+ next
495
+ end
496
+
497
+ spec = @specs[opt]
498
+ stream.printf " %#{leftcol_width}s: ", left[opt]
499
+ desc = spec[:desc] +
500
+ if spec[:default]
501
+ if spec[:desc] =~ /\.$/
502
+ " (Default: #{spec[:default]})"
503
+ else
504
+ " (default: #{spec[:default]})"
505
+ end
506
+ else
507
+ ""
508
+ end
509
+ stream.puts wrap(desc, :width => width - rightcol_start - 1, :prefix => rightcol_start)
510
+ end
511
+ end
512
+
513
+ def wrap_line str, opts={} # :nodoc:
514
+ prefix = opts[:prefix] || 0
515
+ width = opts[:width] || (self.width - 1)
516
+ start = 0
517
+ ret = []
518
+ until start > str.length
519
+ nextt =
520
+ if start + width >= str.length
521
+ str.length
522
+ else
523
+ x = str.rindex(/\s/, start + width)
524
+ x = str.index(/\s/, start) if x && x < start
525
+ x || str.length
526
+ end
527
+ ret << (ret.empty? ? "" : " " * prefix) + str[start ... nextt]
528
+ start = nextt + 1
529
+ end
530
+ ret
531
+ end
532
+
533
+ def wrap str, opts={} # :nodoc:
534
+ if str == ""
535
+ [""]
536
+ else
537
+ str.split("\n").map { |s| wrap_line s, opts }.flatten
538
+ end
539
+ end
540
+
541
+ ## instance_eval but with ability to handle block arguments
542
+ ## thanks to why: http://redhanded.hobix.com/inspect/aBlockCostume.html
543
+ def cloaker &b #:nodoc:
544
+ (class << self; self; end).class_eval do
545
+ define_method :cloaker_, &b
546
+ meth = instance_method :cloaker_
547
+ remove_method :cloaker_
548
+ meth
549
+ end
550
+ end
551
+ end
552
+
553
+ ## The top-level entry method into Trollop. Creates a Parser object,
554
+ ## passes the block to it, then parses +args+ with it, handling any
555
+ ## errors or requests for help or version information appropriately
556
+ ## (and then exiting). Modifies +args+ in place. Returns a hash of
557
+ ## option values.
558
+ ##
559
+ ## The block passed in should contain one or more calls to #opt
560
+ ## (Parser#opt), one or more calls to text (Parser#text), and
561
+ ## probably a call to version (Parser#version).
562
+ ##
563
+ ## See the synopsis in README.txt for examples.
564
+ def options args = ARGV, *a, &b
565
+ @p = Parser.new(*a, &b)
566
+ begin
567
+ vals = @p.parse args
568
+ args.clear
569
+ @p.leftovers.each { |l| args << l }
570
+ vals
571
+ rescue CommandlineError => e
572
+ $stderr.puts "Error: #{e.message}."
573
+ $stderr.puts "Try --help for help."
574
+ exit(-1)
575
+ rescue HelpNeeded
576
+ @p.educate
577
+ exit
578
+ rescue VersionNeeded
579
+ puts @p.version
580
+ exit
581
+ end
582
+ end
583
+
584
+ ## Informs the user that their usage of 'arg' was wrong, as detailed by
585
+ ## 'msg', and dies. Example:
586
+ ##
587
+ ## options do
588
+ ## opt :volume, :default => 0.0
589
+ ## end
590
+ ##
591
+ ## die :volume, "too loud" if opts[:volume] > 10.0
592
+ ## die :volume, "too soft" if opts[:volume] < 0.1
593
+ ##
594
+ ## In the one-argument case, simply print that message, a notice
595
+ ## about -h, and die. Example:
596
+ ##
597
+ ## options do
598
+ ## opt :whatever # ...
599
+ ## end
600
+ ##
601
+ ## Trollop::die "need at least one filename" if ARGV.empty?
602
+ def die arg, msg=nil
603
+ if msg
604
+ $stderr.puts "Error: argument --#{@p.specs[arg][:long]} #{msg}."
605
+ else
606
+ $stderr.puts "Error: #{arg}."
607
+ end
608
+ $stderr.puts "Try --help for help."
609
+ exit(-1)
610
+ end
611
+
612
+ module_function :options, :die
613
+
614
+ end # module