ursm-ditz 0.4

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.
data/lib/trollop.rb ADDED
@@ -0,0 +1,518 @@
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
+ ## The set of values specifiable as the :type parameter to #opt.
34
+ TYPES = [:flag, :boolean, :bool, :int, :integer, :string, :double, :float]
35
+
36
+ ## :nodoc:
37
+ INVALID_SHORT_ARG_REGEX = /[\d-]/
38
+
39
+ ## The values from the commandline that were not interpreted by #parse.
40
+ attr_reader :leftovers
41
+
42
+ ## The complete configuration hashes for each option. (Mainly useful
43
+ ## for testing.)
44
+ attr_reader :specs
45
+
46
+ ## Initializes the parser, and instance-evaluates any block given.
47
+ def initialize *a, &b
48
+ @version = nil
49
+ @leftovers = []
50
+ @specs = {}
51
+ @long = {}
52
+ @short = {}
53
+ @order = []
54
+ @constraints = []
55
+ @stop_words = []
56
+ @stop_on_unknown = false
57
+
58
+ #instance_eval(&b) if b # can't take arguments
59
+ cloaker(&b).bind(self).call(*a) if b
60
+ end
61
+
62
+ ## Add an option. 'name' is the argument name, a unique identifier
63
+ ## for the option that you will use internally. 'desc' a string
64
+ ## description which will be displayed in help messages. Takes the
65
+ ## following optional arguments:
66
+ ##
67
+ ## * :long: Specify the long form of the argument, i.e. the form
68
+ ## with two dashes. If unspecified, will be automatically derived
69
+ ## based on the argument name.
70
+ ## * :short: Specify the short form of the argument, i.e. the form
71
+ ## with one dash. If unspecified, will be automatically derived
72
+ ## based on the argument name.
73
+ ## * :type: Require that the argument take a parameter of type
74
+ ## 'type'. Can by any member of the TYPES constant or a
75
+ ## corresponding class (e.g. Integer for :int). If unset, the
76
+ ## default argument type is :flag, meaning the argument does not
77
+ ## take a parameter. Not necessary if :default: is specified.
78
+ ## * :default: Set the default value for an argument. Without a
79
+ ## default value, the hash returned by #parse (and thus
80
+ ## Trollop#options) will not contain the argument unless it is
81
+ ## given on the commandline. The argument type is derived
82
+ ## automatically from the class of the default value given, if
83
+ ## any. Specifying a :flag argument on the commandline whose
84
+ ## default value is true will change its value to false.
85
+ ## * :required: if set to true, the argument must be provided on the
86
+ ## commandline.
87
+ def opt name, desc="", opts={}
88
+ raise ArgumentError, "you already have an argument named '#{name}'" if @specs.member? name
89
+
90
+ ## fill in :type
91
+ opts[:type] =
92
+ case opts[:type]
93
+ when :flag, :boolean, :bool; :flag
94
+ when :int, :integer; :int
95
+ when :string; :string
96
+ when :double, :float; :float
97
+ when Class
98
+ case opts[:type].to_s # sigh... there must be a better way to do this
99
+ when 'TrueClass', 'FalseClass'; :flag
100
+ when 'String'; :string
101
+ when 'Integer'; :int
102
+ when 'Float'; :float
103
+ else
104
+ raise ArgumentError, "unsupported argument type '#{opts[:type].class.name}'"
105
+ end
106
+ when nil; nil
107
+ else
108
+ raise ArgumentError, "unsupported argument type '#{opts[:type]}'" unless TYPES.include?(opts[:type])
109
+ end
110
+
111
+ type_from_default =
112
+ case opts[:default]
113
+ when Integer; :int
114
+ when Numeric; :float
115
+ when TrueClass, FalseClass; :flag
116
+ when String; :string
117
+ when nil; nil
118
+ else
119
+ raise ArgumentError, "unsupported argument type '#{opts[:default].class.name}'"
120
+ end
121
+
122
+ raise ArgumentError, ":type specification and default type don't match" if opts[:type] && type_from_default && opts[:type] != type_from_default
123
+
124
+ opts[:type] = (opts[:type] || type_from_default || :flag)
125
+
126
+ ## fill in :long
127
+ opts[:long] = opts[:long] ? opts[:long].to_s : name.to_s.gsub("_", "-")
128
+ opts[:long] =
129
+ case opts[:long]
130
+ when /^--([^-].*)$/
131
+ $1
132
+ when /^[^-]/
133
+ opts[:long]
134
+ else
135
+ raise ArgumentError, "invalid long option name #{opts[:long].inspect}"
136
+ end
137
+ raise ArgumentError, "long option name #{opts[:long].inspect} is already taken; please specify a (different) :long" if @long[opts[:long]]
138
+
139
+ ## fill in :short
140
+ opts[:short] = opts[:short].to_s if opts[:short] unless opts[:short] == :none
141
+ opts[:short] =
142
+ case opts[:short]
143
+ when nil
144
+ c = opts[:long].split(//).find { |c| c !~ INVALID_SHORT_ARG_REGEX && !@short.member?(c) }
145
+ raise ArgumentError, "can't generate a short option name for #{opts[:long].inspect}: out of unique characters" unless c
146
+ c
147
+ when /^-(.)$/
148
+ $1
149
+ when /^.$/
150
+ opts[:short]
151
+ when :none
152
+ nil
153
+ else
154
+ raise ArgumentError, "invalid short option name '#{opts[:short].inspect}'"
155
+ end
156
+ if opts[:short]
157
+ raise ArgumentError, "short option name #{opts[:short].inspect} is already taken; please specify a (different) :short" if @short[opts[:short]]
158
+ raise ArgumentError, "a short option name can't be a number or a dash" if opts[:short] =~ INVALID_SHORT_ARG_REGEX
159
+ end
160
+
161
+ ## fill in :default for flags
162
+ opts[:default] = false if opts[:type] == :flag && opts[:default].nil?
163
+
164
+ opts[:desc] ||= desc
165
+ @long[opts[:long]] = name
166
+ @short[opts[:short]] = name if opts[:short]
167
+ @specs[name] = opts
168
+ @order << [:opt, name]
169
+ end
170
+
171
+ ## Sets the version string. If set, the user can request the version
172
+ ## on the commandline. Should be of the form "<program name>
173
+ ## <version number>".
174
+ def version s=nil; @version = s if s; @version end
175
+
176
+ ## Adds text to the help display.
177
+ def banner s; @order << [:text, s] end
178
+ alias :text :banner
179
+
180
+ ## Marks two (or more!) options as requiring each other. Only handles
181
+ ## undirected (i.e., mutual) dependencies. Directed dependencies are
182
+ ## better modeled with Trollop::die.
183
+ def depends *syms
184
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
185
+ @constraints << [:depends, syms]
186
+ end
187
+
188
+ ## Marks two (or more!) options as conflicting.
189
+ def conflicts *syms
190
+ syms.each { |sym| raise ArgumentError, "unknown option '#{sym}'" unless @specs[sym] }
191
+ @constraints << [:conflicts, syms]
192
+ end
193
+
194
+ ## Defines a set of words which cause parsing to terminate when encountered,
195
+ ## such that any options to the left of the word are parsed as usual, and
196
+ ## options to the right of the word are left intact.
197
+ ##
198
+ ## A typical use case would be for subcommand support, where these would be
199
+ ## set to the list of subcommands. A subsequent Trollop invocation would
200
+ ## then be used to parse subcommand options.
201
+ def stop_on *words
202
+ @stop_words = [*words].flatten
203
+ end
204
+
205
+ ## Similar to stop_on, but stops on any unknown word when encountered (unless
206
+ ## it is a parameter for an argument).
207
+ def stop_on_unknown
208
+ @stop_on_unknown = true
209
+ end
210
+
211
+ ## yield successive arg, parameter pairs
212
+ def each_arg args # :nodoc:
213
+ remains = []
214
+ i = 0
215
+
216
+ until i >= args.length
217
+ if @stop_words.member? args[i]
218
+ remains += args[i .. -1]
219
+ return remains
220
+ end
221
+ case args[i]
222
+ when /^--$/ # arg terminator
223
+ remains += args[(i + 1) .. -1]
224
+ return remains
225
+ when /^--(\S+?)=(\S+)$/ # long argument with equals
226
+ yield "--#{$1}", $2
227
+ i += 1
228
+ when /^--(\S+)$/ # long argument
229
+ if args[i + 1] && args[i + 1] !~ PARAM_RE && !@stop_words.member?(args[i + 1])
230
+ take_an_argument = yield args[i], args[i + 1]
231
+ unless take_an_argument
232
+ if @stop_on_unknown
233
+ remains += args[i + 1 .. -1]
234
+ return remains
235
+ else
236
+ remains << args[i + 1]
237
+ end
238
+ end
239
+ i += 2
240
+ else # long argument no parameter
241
+ yield args[i], nil
242
+ i += 1
243
+ end
244
+ when /^-(\S+)$/ # one or more short arguments
245
+ shortargs = $1.split(//)
246
+ shortargs.each_with_index do |a, j|
247
+ if j == (shortargs.length - 1) && args[i + 1] && args[i + 1] !~ PARAM_RE && !@stop_words.member?(args[i + 1])
248
+ take_an_argument = yield "-#{a}", args[i + 1]
249
+ unless take_an_argument
250
+ if @stop_on_unknown
251
+ remains += args[i + 1 .. -1]
252
+ return remains
253
+ else
254
+ remains << args[i + 1]
255
+ end
256
+ end
257
+ i += 1 # once more below
258
+ else
259
+ yield "-#{a}", nil
260
+ end
261
+ end
262
+ i += 1
263
+ else
264
+ if @stop_on_unknown
265
+ remains += args[i .. -1]
266
+ return remains
267
+ else
268
+ remains << args[i]
269
+ i += 1
270
+ end
271
+ end
272
+ end
273
+ remains
274
+ end
275
+
276
+ def parse cmdline #:nodoc:
277
+ vals = {}
278
+ required = {}
279
+ found = {}
280
+
281
+ opt :version, "Print version and exit" if @version unless @specs[:version] || @long["version"]
282
+ opt :help, "Show this message" unless @specs[:help] || @long["help"]
283
+
284
+ @specs.each do |sym, opts|
285
+ required[sym] = true if opts[:required]
286
+ vals[sym] = opts[:default]
287
+ end
288
+
289
+ ## resolve symbols
290
+ args = []
291
+ @leftovers = each_arg cmdline do |arg, param|
292
+ sym =
293
+ case arg
294
+ when /^-([^-])$/
295
+ @short[$1]
296
+ when /^--([^-]\S*)$/
297
+ @long[$1]
298
+ else
299
+ raise CommandlineError, "invalid argument syntax: '#{arg}'"
300
+ end
301
+ raise CommandlineError, "unknown argument '#{arg}'" unless sym
302
+ raise CommandlineError, "option '#{arg}' specified multiple times" if found[sym]
303
+ args << [sym, arg, param]
304
+ found[sym] = true
305
+
306
+ @specs[sym][:type] != :flag # take params on all except flags
307
+ end
308
+
309
+ ## check for version and help args
310
+ raise VersionNeeded if args.any? { |sym, *a| sym == :version }
311
+ raise HelpNeeded if args.any? { |sym, *a| sym == :help }
312
+
313
+ ## check constraint satisfaction
314
+ @constraints.each do |type, syms|
315
+ constraint_sym = syms.find { |sym| found[sym] }
316
+ next unless constraint_sym
317
+
318
+ case type
319
+ when :depends
320
+ syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym][:long]} requires --#{@specs[sym][:long]}" unless found[sym] }
321
+ when :conflicts
322
+ syms.each { |sym| raise CommandlineError, "--#{@specs[constraint_sym][:long]} conflicts with --#{@specs[sym][:long]}" if found[sym] && sym != constraint_sym }
323
+ end
324
+ end
325
+
326
+ required.each do |sym, val|
327
+ raise CommandlineError, "option '#{sym}' must be specified" unless found[sym]
328
+ end
329
+
330
+ ## parse parameters
331
+ args.each do |sym, arg, param|
332
+ opts = @specs[sym]
333
+
334
+ raise CommandlineError, "option '#{arg}' needs a parameter" unless param || opts[:type] == :flag
335
+
336
+ case opts[:type]
337
+ when :flag
338
+ vals[sym] = !opts[:default]
339
+ when :int
340
+ raise CommandlineError, "option '#{arg}' needs an integer" unless param =~ /^\d+$/
341
+ vals[sym] = param.to_i
342
+ when :float
343
+ raise CommandlineError, "option '#{arg}' needs a floating-point number" unless param =~ FLOAT_RE
344
+ vals[sym] = param.to_f
345
+ when :string
346
+ vals[sym] = param.to_s
347
+ end
348
+ end
349
+
350
+ vals
351
+ end
352
+
353
+ def width #:nodoc:
354
+ @width ||=
355
+ if $stdout.tty?
356
+ begin
357
+ require 'curses'
358
+ Curses::init_screen
359
+ x = Curses::cols
360
+ Curses::close_screen
361
+ x
362
+ rescue Exception
363
+ 80
364
+ end
365
+ else
366
+ 80
367
+ end
368
+ end
369
+
370
+ ## Print the help message to 'stream'.
371
+ def educate stream=$stdout
372
+ width # just calculate it now; otherwise we have to be careful not to
373
+ # call this unless the cursor's at the beginning of a line.
374
+
375
+ left = {}
376
+ @specs.each do |name, spec|
377
+ left[name] = "--#{spec[:long]}" +
378
+ (spec[:short] ? ", -#{spec[:short]}" : "") +
379
+ case spec[:type]
380
+ when :flag; ""
381
+ when :int; " <i>"
382
+ when :string; " <s>"
383
+ when :float; " <f>"
384
+ end
385
+ end
386
+
387
+ leftcol_width = left.values.map { |s| s.length }.max || 0
388
+ rightcol_start = leftcol_width + 6 # spaces
389
+
390
+ unless @order.size > 0 && @order.first.first == :text
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] +
404
+ if spec[:default]
405
+ if spec[:desc] =~ /\.$/
406
+ " (Default: #{spec[:default]})"
407
+ else
408
+ " (default: #{spec[:default]})"
409
+ end
410
+ else
411
+ ""
412
+ end
413
+ stream.puts wrap(desc, :width => width - rightcol_start - 1, :prefix => rightcol_start)
414
+ end
415
+ end
416
+
417
+ def wrap_line str, opts={} # :nodoc:
418
+ prefix = opts[:prefix] || 0
419
+ width = opts[:width] || (self.width - 1)
420
+ start = 0
421
+ ret = []
422
+ until start > str.length
423
+ nextt =
424
+ if start + width >= str.length
425
+ str.length
426
+ else
427
+ x = str.rindex(/\s/, start + width)
428
+ x = str.index(/\s/, start) if x && x < start
429
+ x || str.length
430
+ end
431
+ ret << (ret.empty? ? "" : " " * prefix) + str[start ... nextt]
432
+ start = nextt + 1
433
+ end
434
+ ret
435
+ end
436
+
437
+ def wrap str, opts={} # :nodoc:
438
+ if str == ""
439
+ [""]
440
+ else
441
+ str.split("\n").map { |s| wrap_line s, opts }.flatten
442
+ end
443
+ end
444
+
445
+ ## instance_eval but with ability to handle block arguments
446
+ ## thanks to why: http://redhanded.hobix.com/inspect/aBlockCostume.html
447
+ def cloaker &b #:nodoc:
448
+ (class << self; self; end).class_eval do
449
+ define_method :cloaker_, &b
450
+ meth = instance_method :cloaker_
451
+ remove_method :cloaker_
452
+ meth
453
+ end
454
+ end
455
+ end
456
+
457
+ ## The top-level entry method into Trollop. Creates a Parser object,
458
+ ## passes the block to it, then parses +args+ with it, handling any
459
+ ## errors or requests for help or version information appropriately
460
+ ## (and then exiting). Modifies +args+ in place. Returns a hash of
461
+ ## option values.
462
+ ##
463
+ ## The block passed in should contain one or more calls to #opt
464
+ ## (Parser#opt), one or more calls to text (Parser#text), and
465
+ ## probably a call to version (Parser#version).
466
+ ##
467
+ ## See the synopsis in README.txt for examples.
468
+ def options args = ARGV, *a, &b
469
+ @p = Parser.new(*a, &b)
470
+ begin
471
+ vals = @p.parse args
472
+ args.clear
473
+ @p.leftovers.each { |l| args << l }
474
+ vals
475
+ rescue CommandlineError => e
476
+ $stderr.puts "Error: #{e.message}."
477
+ $stderr.puts "Try --help for help."
478
+ exit(-1)
479
+ rescue HelpNeeded
480
+ @p.educate
481
+ exit
482
+ rescue VersionNeeded
483
+ puts @p.version
484
+ exit
485
+ end
486
+ end
487
+
488
+ ## Informs the user that their usage of 'arg' was wrong, as detailed by
489
+ ## 'msg', and dies. Example:
490
+ ##
491
+ ## options do
492
+ ## opt :volume, :default => 0.0
493
+ ## end
494
+ ##
495
+ ## die :volume, "too loud" if opts[:volume] > 10.0
496
+ ## die :volume, "too soft" if opts[:volume] < 0.1
497
+ ##
498
+ ## In the one-argument case, simply print that message, a notice
499
+ ## about -h, and die. Example:
500
+ ##
501
+ ## options do
502
+ ## opt :whatever # ...
503
+ ## end
504
+ ##
505
+ ## Trollop::die "need at least one filename" if ARGV.empty?
506
+ def die arg, msg=nil
507
+ if msg
508
+ $stderr.puts "Error: argument --#{@p.specs[arg][:long]} #{msg}."
509
+ else
510
+ $stderr.puts "Error: #{arg}."
511
+ end
512
+ $stderr.puts "Try --help for help."
513
+ exit(-1)
514
+ end
515
+
516
+ module_function :options, :die
517
+
518
+ end # module
@@ -0,0 +1,31 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
2
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3
+
4
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
5
+ <head>
6
+ <title>Issues not assigned to any release</title>
7
+ <meta http-equiv="Content-Type" content="text/html; charset=utf8" />
8
+ <link rel="stylesheet" href="style.css" type="text/css" />
9
+ </head>
10
+ <body>
11
+
12
+ <h1>Issues not assigned to any release</h1>
13
+
14
+ <table>
15
+ <% issues.sort_by { |i| i.sort_order }.each do |i| %>
16
+ <tr>
17
+ <td class="issuestatus_<%= i.status %>">
18
+ <%= i.status_string %><% if i.closed? %>: <%= i.disposition_string %><% end %>
19
+ </td>
20
+ <td class="issuename">
21
+ <%= fancy_issue_link_for i %>
22
+ <%= i.bug? ? '(bug)' : '' %>
23
+ </td>
24
+ </tr>
25
+ <% end %>
26
+ </table>
27
+
28
+ <p class="footer">Generated by <a
29
+ href="http://ditz.rubyforge.org/">ditz</a>.</p>
30
+ </body>
31
+ </html>
data/lib/util.rb ADDED
@@ -0,0 +1,57 @@
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
+ module Enumerable
16
+ def map_with_index # sigh...
17
+ ret = []
18
+ each_with_index { |e, i| ret << yield(e, i) }
19
+ ret
20
+ end
21
+
22
+ def argfind
23
+ each { |e| x = yield(e); return x if x }
24
+ nil
25
+ end
26
+
27
+ def group_by
28
+ inject({}) do |groups, element|
29
+ (groups[yield(element)] ||= []) << element
30
+ groups
31
+ end
32
+ end if RUBY_VERSION < '1.9'
33
+
34
+ end
35
+
36
+ class Array
37
+ def first_duplicate
38
+ sa = sort
39
+ (1 .. sa.length).argfind { |i| (sa[i] == sa[i - 1]) && sa[i] }
40
+ end
41
+
42
+ def to_h
43
+ Hash[*flatten]
44
+ end
45
+
46
+ def flatten_one_level
47
+ inject([]) do |ret, e|
48
+ case e
49
+ when Array
50
+ ret + e
51
+ else
52
+ ret << e
53
+ end
54
+ end
55
+ end
56
+ end
57
+
@@ -0,0 +1,28 @@
1
+ require "yaml"
2
+
3
+ class String
4
+ def is_binary_data?
5
+ false
6
+ end
7
+
8
+ def decode
9
+ gsub(/\\x(\w{2})/){[Regexp.last_match.captures.first.to_i(16)].pack("C")}
10
+ end
11
+ end
12
+
13
+ ObjectSpace.each_object(Class){|klass|
14
+ klass.class_eval{
15
+ if method_defined?(:to_yaml) && !method_defined?(:to_yaml_with_decode)
16
+ def to_yaml_with_decode(*args)
17
+ result = to_yaml_without_decode(*args)
18
+ if result.kind_of? String
19
+ result.decode
20
+ else
21
+ result
22
+ end
23
+ end
24
+ alias_method :to_yaml_without_decode, :to_yaml
25
+ alias_method :to_yaml, :to_yaml_with_decode
26
+ end
27
+ }
28
+ }
data/lib/view.rb ADDED
@@ -0,0 +1,16 @@
1
+ module Ditz
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