ditz-str 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (65) hide show
  1. data/.document +5 -0
  2. data/Gemfile +14 -0
  3. data/Gemfile.lock +24 -0
  4. data/LICENSE +674 -0
  5. data/LICENSE.txt +20 -0
  6. data/README.rdoc +19 -0
  7. data/README.txt +143 -0
  8. data/Rakefile +55 -0
  9. data/VERSION +1 -0
  10. data/bin/ditz-str +189 -0
  11. data/bugs/issue-02615b8c3dd0382c92f350ce2158ecfe94d11ef8.yaml +22 -0
  12. data/bugs/issue-06a3bbf35a60c4da2d8ea0fdc86164263126d6b2.yaml +22 -0
  13. data/bugs/issue-0c00c1d7fdffaad304e62d79d9b3d5e92547055b.yaml +38 -0
  14. data/bugs/issue-20dad4b4533d6d76d496fe5970098f1eb8efd561.yaml +26 -0
  15. data/bugs/issue-360ae6529dbc66358fde6b532cbea79ece37a670.yaml +22 -0
  16. data/bugs/issue-5177d61bf3c2783f71ef63e6e2c5e720247ef699.yaml +18 -0
  17. data/bugs/issue-695b564c210da1965a2bb38eef782178aead6952.yaml +26 -0
  18. data/bugs/issue-7d0ce6429a9fb5fa09ce3376a8921a5ecb7ecfe5.yaml +34 -0
  19. data/bugs/issue-a04462fa22ab6e1b02cfdd052d1f6c6f491f08f5.yaml +22 -0
  20. data/bugs/issue-bca54ca5107eabc3b281701041cc36ea0641cbdd.yaml +26 -0
  21. data/bugs/issue-d0c7d04b014d705c5fd865e4d487b5e5b6983c33.yaml +26 -0
  22. data/bugs/issue-f94b879842aa0274aa74fc2833252d4a06ec65cc.yaml +22 -0
  23. data/bugs/project.yaml +18 -0
  24. data/data/ditz-str/blue-check.png +0 -0
  25. data/data/ditz-str/close.rhtml +39 -0
  26. data/data/ditz-str/component.rhtml +38 -0
  27. data/data/ditz-str/dropdown.css +11 -0
  28. data/data/ditz-str/dropdown.js +58 -0
  29. data/data/ditz-str/edit_issue.rhtml +53 -0
  30. data/data/ditz-str/green-bar.png +0 -0
  31. data/data/ditz-str/green-check.png +0 -0
  32. data/data/ditz-str/header.gif +0 -0
  33. data/data/ditz-str/header_over.gif +0 -0
  34. data/data/ditz-str/index.rhtml +148 -0
  35. data/data/ditz-str/issue.rhtml +152 -0
  36. data/data/ditz-str/issue_table.rhtml +28 -0
  37. data/data/ditz-str/new_component.rhtml +28 -0
  38. data/data/ditz-str/new_issue.rhtml +57 -0
  39. data/data/ditz-str/new_release.rhtml +29 -0
  40. data/data/ditz-str/plugins/git-sync.rb +83 -0
  41. data/data/ditz-str/plugins/git.rb +153 -0
  42. data/data/ditz-str/plugins/issue-claiming.rb +174 -0
  43. data/data/ditz-str/red-check.png +0 -0
  44. data/data/ditz-str/release.rhtml +111 -0
  45. data/data/ditz-str/style.css +236 -0
  46. data/data/ditz-str/unassigned.rhtml +37 -0
  47. data/data/ditz-str/yellow-bar.png +0 -0
  48. data/ditz-str.gemspec +121 -0
  49. data/lib/ditzstr/brick.rb +251 -0
  50. data/lib/ditzstr/file-storage.rb +54 -0
  51. data/lib/ditzstr/hook.rb +67 -0
  52. data/lib/ditzstr/html.rb +104 -0
  53. data/lib/ditzstr/lowline.rb +201 -0
  54. data/lib/ditzstr/model-objects.rb +346 -0
  55. data/lib/ditzstr/model.rb +265 -0
  56. data/lib/ditzstr/operator.rb +593 -0
  57. data/lib/ditzstr/trollop.rb +614 -0
  58. data/lib/ditzstr/util.rb +61 -0
  59. data/lib/ditzstr/view.rb +16 -0
  60. data/lib/ditzstr/views.rb +157 -0
  61. data/lib/ditzstr.rb +69 -0
  62. data/man/ditz.1 +38 -0
  63. data/test/helper.rb +18 -0
  64. data/test/test_ditz-str.rb +7 -0
  65. metadata +219 -0
@@ -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
@@ -0,0 +1,61 @@
1
+ class Object
2
+ def returning o; yield o; o end # k-combinator
3
+ end
4
+
5
+ module Enumerable
6
+ def count_of(&b); select(&b).size end
7
+ def max_of(&b); map(&b).max end
8
+ def min_of(&b); map(&b).min end
9
+ end
10
+
11
+ class Array
12
+ def uniq_by; inject({}) { |h, o| h[yield(o)] = o; h }.values end
13
+ end
14
+
15
+ class NilClass
16
+ def blank?; true end
17
+ end
18
+
19
+ module Enumerable
20
+ def map_with_index # sigh...
21
+ ret = []
22
+ each_with_index { |e, i| ret << yield(e, i) }
23
+ ret
24
+ end
25
+
26
+ def argfind
27
+ each { |e| x = yield(e); return x if x }
28
+ nil
29
+ end
30
+
31
+ def group_by
32
+ inject({}) do |groups, element|
33
+ (groups[yield(element)] ||= []) << element
34
+ groups
35
+ end
36
+ end if RUBY_VERSION < '1.9'
37
+
38
+ end
39
+
40
+ class Array
41
+ def first_duplicate
42
+ sa = sort
43
+ (1 .. sa.length).argfind { |i| (sa[i] == sa[i - 1]) && sa[i] }
44
+ end
45
+
46
+ def to_h
47
+ Hash[*flatten]
48
+ end
49
+
50
+ def flatten_one_level
51
+ inject([]) do |ret, e|
52
+ case e
53
+ when Array
54
+ ret + e
55
+ else
56
+ ret << e
57
+ end
58
+ end
59
+ end
60
+ end
61
+
@@ -0,0 +1,16 @@
1
+ module DitzStr
2
+
3
+ class View
4
+ def self.add_to_view type, &block
5
+ @views ||= {}
6
+ @views[type] ||= []
7
+ @views[type] << block
8
+ end
9
+
10
+ def self.view_additions_for type
11
+ @views ||= {}
12
+ @views[type] || []
13
+ end
14
+ end
15
+
16
+ end