ursm-ditz 0.4 → 0.5

Sign up to get free protection for your applications and to get access to all the features.
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