ursm-ditz 0.4 → 0.5

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.
Files changed (50) hide show
  1. data/Changelog +15 -0
  2. data/INSTALL +20 -0
  3. data/LICENSE +674 -0
  4. data/Manifest.txt +40 -0
  5. data/PLUGINS.txt +140 -0
  6. data/README.txt +43 -27
  7. data/Rakefile +36 -3
  8. data/ReleaseNotes +6 -0
  9. data/bin/ditz +49 -63
  10. data/contrib/completion/ditz.bash +25 -9
  11. data/lib/ditz.rb +52 -7
  12. data/lib/ditz/file-storage.rb +54 -0
  13. data/lib/{hook.rb → ditz/hook.rb} +1 -1
  14. data/lib/{html.rb → ditz/html.rb} +42 -4
  15. data/lib/{lowline.rb → ditz/lowline.rb} +31 -11
  16. data/lib/{model-objects.rb → ditz/model-objects.rb} +53 -21
  17. data/lib/ditz/model.rb +321 -0
  18. data/lib/{operator.rb → ditz/operator.rb} +122 -67
  19. data/lib/ditz/plugins/git-sync.rb +83 -0
  20. data/lib/{plugins → ditz/plugins}/git.rb +57 -18
  21. data/lib/ditz/plugins/issue-claiming.rb +174 -0
  22. data/lib/ditz/plugins/issue-labeling.rb +161 -0
  23. data/lib/{util.rb → ditz/util.rb} +4 -0
  24. data/lib/{view.rb → ditz/view.rb} +0 -0
  25. data/lib/{views.rb → ditz/views.rb} +7 -4
  26. data/man/{ditz.1 → man1/ditz.1} +1 -1
  27. data/setup.rb +1585 -0
  28. data/share/ditz/blue-check.png +0 -0
  29. data/{lib → share/ditz}/component.rhtml +7 -5
  30. data/share/ditz/green-bar.png +0 -0
  31. data/share/ditz/green-check.png +0 -0
  32. data/share/ditz/index.rhtml +130 -0
  33. data/share/ditz/issue.rhtml +119 -0
  34. data/share/ditz/issue_table.rhtml +28 -0
  35. data/share/ditz/red-check.png +0 -0
  36. data/share/ditz/release.rhtml +98 -0
  37. data/share/ditz/style.css +226 -0
  38. data/share/ditz/unassigned.rhtml +23 -0
  39. data/share/ditz/yellow-bar.png +0 -0
  40. metadata +50 -28
  41. data/lib/index.rhtml +0 -113
  42. data/lib/issue.rhtml +0 -111
  43. data/lib/issue_table.rhtml +0 -33
  44. data/lib/model.rb +0 -208
  45. data/lib/plugins/issue-claiming.rb +0 -92
  46. data/lib/release.rhtml +0 -69
  47. data/lib/style.css +0 -127
  48. data/lib/trollop.rb +0 -518
  49. data/lib/unassigned.rhtml +0 -31
  50. data/lib/vendor/yaml_waml.rb +0 -28
@@ -1,92 +0,0 @@
1
- module Ditz
2
- class Issue
3
- field :claimer, :ask => false
4
-
5
- def claim who, comment, force=false
6
- raise Error, "already claimed by #{claimer}" if claimer && !force
7
- log "issue claimed", who, comment
8
- self.claimer = who
9
- end
10
-
11
- def unclaim who, comment, force=false
12
- raise Error, "not claimed" unless claimer
13
- raise Error, "can only be unclaimed by #{claimer}" unless claimer == who || force
14
- if claimer == who
15
- log "issue unclaimed", who, comment
16
- else
17
- log "unassigned from #{claimer}", who, comment
18
- end
19
- self.claimer = nil
20
- end
21
-
22
- def claimed?; claimer end
23
- def unclaimed?; !claimed? end
24
- end
25
-
26
- class ScreenView
27
- add_to_view :issue_summary do |issue, config|
28
- " Claimed by: #{issue.claimer || 'none'}\n"
29
- end
30
- end
31
-
32
- class HtmlView
33
- add_to_view :issue_summary do |issue, config|
34
- next unless issue.git_branch
35
- [<<EOS, { :issue => issue, :url_prefix => config.git_branch_url_prefix }]
36
- <tr>
37
- <td class='attrname'>Claimed by:</td>
38
- <td class='attrval'>h(issue.claimer) %></td>
39
- </tr>
40
- EOS
41
- end
42
- end
43
-
44
- class Operator
45
- operation :claim, "Claim an issue for yourself", :issue do
46
- opt :force, "Claim this issue even if someone else has claimed it", :default => false
47
- end
48
- def claim project, config, opts, issue
49
- puts "Claiming issue #{issue.name}: #{issue.title}."
50
- comment = ask_multiline "Comments" unless $opts[:no_comment]
51
- issue.claim config.user, comment, opts[:force]
52
- puts "Issue #{issue.name} marked as claimed by #{config.user}"
53
- end
54
-
55
- operation :unclaim, "Unclaim a claimed issue", :issue do
56
- opt :force, "Unclaim this issue even if it's claimed by someone else", :default => false
57
- end
58
- def unclaim project, config, opts, issue
59
- puts "Unclaiming issue #{issue.name}: #{issue.title}."
60
- comment = ask_multiline "Comments" unless $opts[:no_comment]
61
- issue.unclaim config.user, comment, opts[:force]
62
- puts "Issue #{issue.name} marked as unclaimed."
63
- end
64
-
65
- operation :claimed, "Show all issues claimed by you" do
66
- opt :all, "Show all issues, not just open ones"
67
- end
68
- def claimed project, config, opts
69
- puts "#{opts[:all] ? "All" : "Open"} issues claimed by you:"
70
- issues = project.issues.select { |i| i.claimer == config.user && (opts[:all] || i.open?) }
71
- if issues.empty?
72
- puts "No issues."
73
- else
74
- print_todo_list_by_release_for project, issues
75
- end
76
- end
77
-
78
- operation :unclaimed, "Show all unclaimed issues" do
79
- opt :all, "Show all issues, not just open ones"
80
- end
81
- def unclaimed project, config, opts
82
- puts "Unclaimed#{opts[:all] ? "" : ", open"} issues:"
83
- issues = project.issues.select { |i| i.claimer.nil? && (opts[:all] || i.open?) }
84
- if issues.empty?
85
- puts "No issues."
86
- else
87
- print_todo_list_by_release_for project, issues
88
- end
89
- end
90
- end
91
-
92
- end
@@ -1,69 +0,0 @@
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><%= project.name %> release <%= release.name %></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
- <div><%= link_to "index", "&laquo; #{project.name} project page" %></div>
13
-
14
- <h1><%= project.name %> release <%= release.name %></h1>
15
-
16
- <table>
17
- <tr>
18
- <td class="attrname">Status:</td>
19
- <td class="attrval"><%= release.status %></td>
20
- </tr>
21
- <% if release.released? %>
22
- <tr>
23
- <td class="attrname">Release time:</td>
24
- <td class="attrval"><%= release.release_time %></td>
25
- </tr>
26
- <% end %>
27
- <tr>
28
- <%
29
- num_done = issues.count_of { |i| i.closed? }
30
- pct_done = issues.size == 0 ? 100 : (100.0 * num_done / issues.size)
31
- %>
32
- <td class="attrname">Completion:</td>
33
- <td class="attrval"><%= sprintf "%.0f%%", pct_done %></td>
34
- </tr>
35
- </table>
36
-
37
- <h2>Issues</h2>
38
- <% if issues.empty? %>
39
- <p>No issues assigned to this release.</p>
40
- <% else %>
41
- <%= render "issue_table", :show_component => false, :show_release => false %>
42
- <% end %>
43
-
44
- <h2>Release log</h2>
45
- <table>
46
- <% release.log_events.each_with_index do |(time, who, what, comment), i| %>
47
- <% if i % 2 == 0 %>
48
- <tr class="logentryeven">
49
- <% else %>
50
- <tr class="logentryodd">
51
- <% end %>
52
- <td class="logtime"><%=h time %></td>
53
- <td class="logwho"><%=obscured_email who %></td>
54
- <td class="logwhat"><%=h what %></td>
55
- </tr>
56
- <tr><td colspan="3" class="logcomment">
57
- <% if comment.empty? %>
58
- <% else %>
59
- <%= link_issue_names project, p(comment) %>
60
- <% end %>
61
- </td></tr>
62
- <% end %>
63
- </table>
64
-
65
- <p class="footer">Generated by <a
66
- href="http://ditz.rubyforge.org/">ditz</a>.</p>
67
-
68
- </body>
69
- </html>
@@ -1,127 +0,0 @@
1
- body {
2
- background: white;
3
- color: #202020;
4
- margin-bottom: 2em;
5
- margin-left: auto;
6
- margin-right: auto;
7
- width: 90%;
8
- }
9
-
10
- div.wrapper {
11
- }
12
-
13
- div.footer {
14
- }
15
-
16
- a, a:visited {
17
- border-bottom: 1px dotted #03c;
18
- background: inherit;
19
- text-decoration: none;
20
- color: #03c;
21
- }
22
-
23
- ul {
24
- list-style-type: none;
25
- padding: 0;
26
- }
27
-
28
- p {
29
- width: 40em;
30
- }
31
-
32
- .dimmed {
33
- color: #bbb;
34
- }
35
-
36
- div.on-the-left {
37
- float: left;
38
- width: 39%;
39
- }
40
-
41
- div.on-the-right {
42
- float: right;
43
- width: 59%;
44
- }
45
-
46
- table {
47
- border-style: none;
48
- border-spacing: 0;
49
- }
50
-
51
- td {
52
- border-width: 0;
53
- border-style: none;
54
- padding-right: 0.5em;
55
- padding-left: 0.5em;
56
- }
57
-
58
- tr {
59
- padding-bottom: 5em;
60
- }
61
-
62
- h1 {
63
- padding: 0.2em;
64
- margin-left: -1em;
65
- margin-right: 1em;
66
- background: #abc;
67
- }
68
-
69
- .attrname {
70
- text-align: right;
71
- }
72
-
73
- .issuestatus_closed {
74
- background-color: #afa;
75
- text-align: center;
76
- }
77
-
78
- .issuestatus_in_progress {
79
- background-color: #ffa;
80
- text-align: center;
81
- }
82
-
83
- .issuestatus_paused {
84
- background-color: #ffa;
85
- text-align: center;
86
- }
87
-
88
- .issuestatus_unstarted {
89
- background-color: #faa;
90
- text-align: center;
91
- }
92
-
93
- .issuestatus_closed a {
94
- color: #202020;
95
- }
96
-
97
- .issuestatus_in_progress a {
98
- color: #202020;
99
- }
100
-
101
- .issuestatus_paused a {
102
- color: #202020;
103
- }
104
-
105
- .issuestatus_unstarted a {
106
- color: #202020;
107
- }
108
-
109
- .logwhat {
110
- }
111
-
112
- .logentryeven {
113
- background: #eee;
114
- }
115
-
116
- .logentryodd {
117
- background: #eee;
118
- }
119
-
120
- .logcomment {
121
- padding-left: 3em;
122
- }
123
-
124
- p.footer {
125
- font-size: small;
126
- }
127
-
@@ -1,518 +0,0 @@
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