ghi 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.rdoc +180 -0
- data/Manifest.txt +14 -0
- data/README.rdoc +92 -0
- data/bin/ghi +6 -0
- data/lib/ghi.rb +55 -0
- data/lib/ghi/api.rb +106 -0
- data/lib/ghi/cli.rb +608 -0
- data/lib/ghi/issue.rb +29 -0
- data/spec/ghi/api_spec.rb +194 -0
- data/spec/ghi/cli_spec.rb +258 -0
- data/spec/ghi/issue_spec.rb +26 -0
- data/spec/ghi_spec.rb +91 -0
- metadata +70 -0
data/lib/ghi/cli.rb
ADDED
@@ -0,0 +1,608 @@
|
|
1
|
+
require "optparse"
|
2
|
+
require "tempfile"
|
3
|
+
require "ghi"
|
4
|
+
require "ghi/api"
|
5
|
+
require "ghi/issue"
|
6
|
+
|
7
|
+
begin
|
8
|
+
require "launchy"
|
9
|
+
rescue LoadError
|
10
|
+
# No launchy!
|
11
|
+
end
|
12
|
+
|
13
|
+
module GHI::CLI #:nodoc:
|
14
|
+
module FileHelper
|
15
|
+
def launch_editor(file)
|
16
|
+
system "#{editor} #{file.path}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def gets_from_editor(issue)
|
20
|
+
if windows?
|
21
|
+
warn "Please supply the message with the -m option"
|
22
|
+
exit 1
|
23
|
+
end
|
24
|
+
|
25
|
+
if in_repo?
|
26
|
+
File.open message_path, "a+", &file_proc(issue)
|
27
|
+
else
|
28
|
+
Tempfile.open message_filename, &file_proc(issue)
|
29
|
+
end
|
30
|
+
|
31
|
+
return @message if comment?
|
32
|
+
return @message.shift.strip, @message.join.sub(/\b\n\b/, " ").strip
|
33
|
+
end
|
34
|
+
|
35
|
+
def delete_message
|
36
|
+
File.delete message_path
|
37
|
+
rescue Errno::ENOENT, TypeError
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def message_path
|
42
|
+
File.join gitdir, message_filename
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def editor
|
48
|
+
ENV["GHI_EDITOR"] || ENV["VISUAL"] || ENV["EDITOR"] || "vi"
|
49
|
+
end
|
50
|
+
|
51
|
+
def gitdir
|
52
|
+
@gitdir ||= `git rev-parse --git-dir 2>/dev/null`.chomp
|
53
|
+
end
|
54
|
+
|
55
|
+
def message_filename
|
56
|
+
@message_filename ||= "GHI_#{action.to_s.upcase}#{number}_MESSAGE"
|
57
|
+
end
|
58
|
+
|
59
|
+
def file_proc(issue)
|
60
|
+
lambda do |file|
|
61
|
+
file << edit_format(issue).join("\n") if File.zero? file.path
|
62
|
+
file.rewind
|
63
|
+
launch_editor file
|
64
|
+
@message = File.readlines(file.path).find_all { |l| !l.match(/^#/) }
|
65
|
+
|
66
|
+
if message.to_s =~ /\A\s*\Z/
|
67
|
+
raise GHI::API::InvalidRequest, "can't file empty message"
|
68
|
+
end
|
69
|
+
raise GHI::API::InvalidRequest, "no change" if issue == message
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def in_repo?
|
74
|
+
!gitdir.empty? && user == local_user && repo == local_repo
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
module FormattingHelper
|
79
|
+
def list_header(term = nil)
|
80
|
+
if term
|
81
|
+
"# #{state.to_s.capitalize} #{term.inspect} issues on #{user}/#{repo}"
|
82
|
+
else
|
83
|
+
"# #{state.to_s.capitalize} issues on #{user}/#{repo}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def list_format(issues, verbosity = nil)
|
88
|
+
unless issues.empty?
|
89
|
+
if verbosity
|
90
|
+
issues.map { |i| ["=" * 79] + show_format(i) }
|
91
|
+
else
|
92
|
+
issues.map { |i| " #{i.number.to_s.rjust 3}: #{truncate i.title, 72}" }
|
93
|
+
end
|
94
|
+
else
|
95
|
+
"none"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def edit_format(issue)
|
100
|
+
l = []
|
101
|
+
l << issue.title if issue.title && !comment?
|
102
|
+
l << ""
|
103
|
+
l << issue.body if issue.body && !comment?
|
104
|
+
if comment?
|
105
|
+
l << "# Please enter your comment."
|
106
|
+
else
|
107
|
+
l << "# Please explain the issue. The first line will become the title."
|
108
|
+
end
|
109
|
+
l << "# Lines beginning '#' will be ignored; ghi aborts empty messages."
|
110
|
+
l << "# All line breaks will be honored in accordance with GFM:"
|
111
|
+
l << "#"
|
112
|
+
l << "# http://github.github.com/github-flavored-markdown"
|
113
|
+
l << "#"
|
114
|
+
l << "# On #{user}/#{repo}:"
|
115
|
+
l << "#"
|
116
|
+
l += show_format(issue, false).map { |line| "# #{line}" }
|
117
|
+
end
|
118
|
+
|
119
|
+
def show_format(issue, verbose = true)
|
120
|
+
l = []
|
121
|
+
l << " number: #{issue.number}" if issue.number
|
122
|
+
l << " state: #{issue.state}" if issue.state
|
123
|
+
l << " title: #{indent(issue.title, 15, 0)}" if issue.title
|
124
|
+
l << " user: #{issue.user || GHI.login}"
|
125
|
+
l << " votes: #{issue.votes}" if issue.votes
|
126
|
+
l << " created at: #{issue.created_at}" if issue.created_at
|
127
|
+
l << " updated at: #{issue.updated_at}" if issue.updated_at
|
128
|
+
return l unless verbose
|
129
|
+
l << ""
|
130
|
+
l += indent(issue.body)[0..-2]
|
131
|
+
end
|
132
|
+
|
133
|
+
def action_format(value = nil)
|
134
|
+
key = "#{action.to_s.capitalize.sub(/e?$/, "ed")} issue #{number}"
|
135
|
+
"#{key}: #{truncate value.to_s, 78 - key.length}"
|
136
|
+
end
|
137
|
+
|
138
|
+
def truncate(string, length)
|
139
|
+
result = string.scan(/.{0,#{length - 3}}(?:\s|\Z)/).first.strip
|
140
|
+
result << "..." if result != string
|
141
|
+
result
|
142
|
+
end
|
143
|
+
|
144
|
+
def indent(string, level = 4, first = level)
|
145
|
+
lines = string.scan(/.{0,#{79 - level}}(?:\s|\Z)/).map { |line|
|
146
|
+
" " * level + line
|
147
|
+
}
|
148
|
+
lines.first.sub!(/^\s+/) {} if first != level
|
149
|
+
lines
|
150
|
+
end
|
151
|
+
|
152
|
+
private
|
153
|
+
|
154
|
+
def comment?
|
155
|
+
![:open, :edit].include?(action)
|
156
|
+
end
|
157
|
+
|
158
|
+
def puts(*args)
|
159
|
+
args = args.flatten.each { |arg|
|
160
|
+
arg.gsub!(/\B\*(.+)\*\B/) { "\e[1m#$1\e[0m" } # Bold
|
161
|
+
arg.gsub!(/\B_(.+)_\B/) { "\e[4m#$1\e[0m" } # Underline
|
162
|
+
arg.gsub!(/(state:)?(# Open.*| open)$/) { "#$1\e[32m#$2\e[0m" }
|
163
|
+
arg.gsub!(/(state:)?(# Closed.*| closed)$/) { "#$1\e[31m#$2\e[0m" }
|
164
|
+
marked = [GHI.login, search_term, tag, "(?:#|gh)-\d+"].compact * "|"
|
165
|
+
unless arg.include? "\e"
|
166
|
+
arg.gsub!(/(#{marked})/i) { "\e[1;4;33m#{$&}\e[0m" }
|
167
|
+
end
|
168
|
+
} if colorize?
|
169
|
+
rescue NoMethodError
|
170
|
+
# Do nothing.
|
171
|
+
ensure
|
172
|
+
$stdout.puts(*args)
|
173
|
+
end
|
174
|
+
|
175
|
+
def colorize?
|
176
|
+
return @colorize if defined? @colorize
|
177
|
+
@colorize = if $stdout.isatty && !windows?
|
178
|
+
!`git config --get-regexp color`.chomp.empty?
|
179
|
+
else
|
180
|
+
false
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
def prepare_stdout
|
185
|
+
return if @prepared || @no_pager || !$stdout.isatty || pager.nil?
|
186
|
+
colorize? # Check for colorization.
|
187
|
+
$stdout = pager
|
188
|
+
@prepared = true
|
189
|
+
end
|
190
|
+
|
191
|
+
def pager
|
192
|
+
return @pager if defined? @pager
|
193
|
+
pagers = [ENV["GHI_PAGER"], "less -EMRX", "pager", "more"].compact.uniq
|
194
|
+
pagers.each { |pager| return @pager = IO.popen(pager, "w") rescue nil }
|
195
|
+
end
|
196
|
+
|
197
|
+
def windows?
|
198
|
+
RUBY_PLATFORM.include? "mswin"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
class Executable
|
203
|
+
include FileHelper, FormattingHelper
|
204
|
+
|
205
|
+
attr_reader :message, :local_user, :local_repo, :user, :repo, :api,
|
206
|
+
:action, :search_term, :number, :title, :body, :tag, :args, :verbosity
|
207
|
+
|
208
|
+
def parse!(*argv)
|
209
|
+
@args, @argv = argv, argv.dup
|
210
|
+
|
211
|
+
remotes = `git config --get-regexp remote\..+\.url`.split /\n/
|
212
|
+
repo_expression = %r{([^:/]+)/([^/\s]+)(?:\.git)$}
|
213
|
+
if remote = remotes.find { |r| r.include? "github.com" }
|
214
|
+
remote.match repo_expression
|
215
|
+
@user, @repo = $1, $2
|
216
|
+
end
|
217
|
+
|
218
|
+
option_parser.parse!(*args)
|
219
|
+
|
220
|
+
if action.nil? && fallback_parsing(*args).nil?
|
221
|
+
puts option_parser
|
222
|
+
exit
|
223
|
+
end
|
224
|
+
rescue OptionParser::InvalidOption, OptionParser::InvalidArgument => e
|
225
|
+
if fallback_parsing(*e.args).nil?
|
226
|
+
warn "#{File.basename $0}: #{e.message}"
|
227
|
+
puts option_parser
|
228
|
+
exit 1
|
229
|
+
end
|
230
|
+
rescue OptionParser::MissingArgument, OptionParser::AmbiguousOption => e
|
231
|
+
warn "#{File.basename $0}: #{e.message}"
|
232
|
+
puts option_parser
|
233
|
+
exit 1
|
234
|
+
ensure
|
235
|
+
run!
|
236
|
+
$stdout.close_write
|
237
|
+
end
|
238
|
+
|
239
|
+
def run!
|
240
|
+
@api = GHI::API.new user, repo
|
241
|
+
|
242
|
+
case action
|
243
|
+
when :search then search
|
244
|
+
when :list then list
|
245
|
+
when :show then show
|
246
|
+
when :open then open
|
247
|
+
when :edit then edit
|
248
|
+
when :close then close
|
249
|
+
when :reopen then reopen
|
250
|
+
when :comment then prepare_comment && comment
|
251
|
+
when :label, :claim then prepare_label && label
|
252
|
+
when :unlabel then prepare_label && unlabel
|
253
|
+
when :url then url
|
254
|
+
end
|
255
|
+
rescue GHI::API::InvalidConnection
|
256
|
+
if action
|
257
|
+
code = 1
|
258
|
+
warn "#{File.basename $0}: not a GitHub repo"
|
259
|
+
puts option_parser if args.flatten.empty?
|
260
|
+
exit 1
|
261
|
+
end
|
262
|
+
rescue GHI::API::InvalidRequest => e
|
263
|
+
warn "#{File.basename $0}: #{e.message} (#{user}/#{repo})"
|
264
|
+
delete_message
|
265
|
+
exit 1
|
266
|
+
rescue GHI::API::ResponseError => e
|
267
|
+
warn "#{File.basename $0}: #{e.message} (#{user}/#{repo})"
|
268
|
+
exit 1
|
269
|
+
end
|
270
|
+
|
271
|
+
def commenting?
|
272
|
+
@commenting
|
273
|
+
end
|
274
|
+
|
275
|
+
def state
|
276
|
+
@state || :open
|
277
|
+
end
|
278
|
+
|
279
|
+
private
|
280
|
+
|
281
|
+
def option_parser
|
282
|
+
@option_parser ||= OptionParser.new { |opts|
|
283
|
+
opts.banner = "Usage: #{File.basename $0} [options]"
|
284
|
+
|
285
|
+
opts.on("-l", "--list", "--search", "--show [state|term|number]") do |v|
|
286
|
+
@action = :list
|
287
|
+
case v
|
288
|
+
when nil, /^o(?:pen)?$/
|
289
|
+
# Defaults.
|
290
|
+
when /^\d+$/
|
291
|
+
@action = :show
|
292
|
+
@number = v.to_i
|
293
|
+
when /^c(?:losed)?$/
|
294
|
+
@state = :closed
|
295
|
+
when /^u$/
|
296
|
+
@action = :url
|
297
|
+
when /^v$/
|
298
|
+
@verbosity = true
|
299
|
+
else
|
300
|
+
@action = :search
|
301
|
+
@search_term = v
|
302
|
+
end
|
303
|
+
end
|
304
|
+
|
305
|
+
opts.on("-v", "--verbose") do |v|
|
306
|
+
if v
|
307
|
+
@action ||= :list
|
308
|
+
@verbosity = true
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
opts.on("-o", "--open", "--reopen [title|number]") do |v|
|
313
|
+
@action = :open
|
314
|
+
case v
|
315
|
+
when /^\d+$/
|
316
|
+
@action = :reopen
|
317
|
+
@number = v.to_i
|
318
|
+
when /^l$/
|
319
|
+
@action = :list
|
320
|
+
when /^m$/
|
321
|
+
@title = args * " "
|
322
|
+
when /^u$/
|
323
|
+
@action = :url
|
324
|
+
else
|
325
|
+
@title = v
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
opts.on("-c", "--closed", "--close [number]") do |v|
|
330
|
+
case v
|
331
|
+
when /^\d+$/
|
332
|
+
@action = :close
|
333
|
+
@number = v.to_i unless v.nil?
|
334
|
+
when /^l$/
|
335
|
+
@action = :list
|
336
|
+
@state = :closed
|
337
|
+
when /^u$/
|
338
|
+
@action = :url
|
339
|
+
@state = :closed
|
340
|
+
when nil
|
341
|
+
if @action.nil? || @number
|
342
|
+
@action = :close
|
343
|
+
else
|
344
|
+
@state = :closed
|
345
|
+
end
|
346
|
+
else
|
347
|
+
raise OptionParser::InvalidArgument
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
opts.on("-e", "--edit [number]") do |v|
|
352
|
+
case v
|
353
|
+
when /^\d+$/
|
354
|
+
@action = :edit
|
355
|
+
@number = v.to_i
|
356
|
+
when nil
|
357
|
+
raise OptionParser::MissingArgument
|
358
|
+
else
|
359
|
+
raise OptionParser::InvalidArgument
|
360
|
+
end
|
361
|
+
end
|
362
|
+
|
363
|
+
opts.on("-r", "--repo", "--repository [name]") do |v|
|
364
|
+
case v
|
365
|
+
when nil
|
366
|
+
raise OptionParser::MissingArgument
|
367
|
+
else
|
368
|
+
repo = v.split "/"
|
369
|
+
repo.unshift GHI.login if repo.length == 1
|
370
|
+
@user, @repo = repo
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
opts.on("-m", "--comment [number|comment]") do |v|
|
375
|
+
case v
|
376
|
+
when /^\d+$/, nil
|
377
|
+
@action ||= :comment
|
378
|
+
@number ||= v.to_i unless v.nil?
|
379
|
+
@commenting = true
|
380
|
+
else
|
381
|
+
@body = v
|
382
|
+
end
|
383
|
+
end
|
384
|
+
|
385
|
+
opts.on("-t", "--label [number] [label]") do |v|
|
386
|
+
raise OptionParser::MissingArgument if v.nil?
|
387
|
+
@action ||= :label
|
388
|
+
@number = v.to_i
|
389
|
+
end
|
390
|
+
|
391
|
+
opts.on("--claim [number]") do |v|
|
392
|
+
raise OptionParser::MissingArgument if v.nil?
|
393
|
+
@action = :claim
|
394
|
+
@number = v.to_i
|
395
|
+
@tag = GHI.login
|
396
|
+
end
|
397
|
+
|
398
|
+
opts.on("-d", "--unlabel [number] [label]") do |v|
|
399
|
+
@action = :unlabel
|
400
|
+
case v
|
401
|
+
when /^\d+$/
|
402
|
+
@number = v.to_i
|
403
|
+
when /^\w+$/
|
404
|
+
@tag = v
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
opts.on("-u", "--url [state|number]") do |v|
|
409
|
+
@action = :url
|
410
|
+
case v
|
411
|
+
when /^\d+$/
|
412
|
+
@number = v.to_i
|
413
|
+
when /^c(?:losed)?$/
|
414
|
+
@state = :closed
|
415
|
+
when /^u(?:nread)?$/
|
416
|
+
@state = :unread
|
417
|
+
end
|
418
|
+
end
|
419
|
+
|
420
|
+
opts.on("--[no-]color") do |v|
|
421
|
+
@colorize = v
|
422
|
+
end
|
423
|
+
|
424
|
+
opts.on("--[no-]pager") do |v|
|
425
|
+
@no_pager = (v == false)
|
426
|
+
end
|
427
|
+
|
428
|
+
opts.on_tail("-V", "--version") do
|
429
|
+
puts "#{File.basename($0)}: v#{GHI::VERSION}"
|
430
|
+
exit
|
431
|
+
end
|
432
|
+
|
433
|
+
opts.on_tail("-h", "--help") do
|
434
|
+
puts opts
|
435
|
+
exit
|
436
|
+
end
|
437
|
+
}
|
438
|
+
end
|
439
|
+
|
440
|
+
def search
|
441
|
+
prepare_stdout
|
442
|
+
puts list_header(search_term)
|
443
|
+
issues = api.search search_term, state
|
444
|
+
puts list_format(issues, verbosity)
|
445
|
+
end
|
446
|
+
|
447
|
+
def list
|
448
|
+
prepare_stdout
|
449
|
+
puts list_header
|
450
|
+
issues = api.list(state)
|
451
|
+
puts list_format(issues, verbosity)
|
452
|
+
end
|
453
|
+
|
454
|
+
def show
|
455
|
+
prepare_stdout
|
456
|
+
issue = api.show number
|
457
|
+
puts show_format(issue)
|
458
|
+
end
|
459
|
+
|
460
|
+
def open
|
461
|
+
if title.nil?
|
462
|
+
new_title, new_body = gets_from_editor GHI::Issue.new("title" => body)
|
463
|
+
elsif @commenting && body.nil?
|
464
|
+
new_title, new_body = gets_from_editor GHI::Issue.new("title" => title)
|
465
|
+
end
|
466
|
+
new_title ||= title
|
467
|
+
new_body ||= body
|
468
|
+
issue = api.open new_title, new_body
|
469
|
+
delete_message
|
470
|
+
@number = issue.number
|
471
|
+
puts action_format(issue.title)
|
472
|
+
end
|
473
|
+
|
474
|
+
def edit
|
475
|
+
shown = api.show number
|
476
|
+
new_title, new_body = gets_from_editor(shown) if body.nil?
|
477
|
+
new_title ||= shown.title
|
478
|
+
new_body ||= body
|
479
|
+
issue = api.edit number, new_title, new_body
|
480
|
+
delete_message
|
481
|
+
puts action_format(issue.title)
|
482
|
+
end
|
483
|
+
|
484
|
+
def close
|
485
|
+
raise GHI::API::InvalidRequest, "need a number" if number.nil?
|
486
|
+
issue = api.close number
|
487
|
+
if @commenting || new_body = body
|
488
|
+
new_body ||= gets_from_editor issue
|
489
|
+
comment = api.comment number, new_body
|
490
|
+
end
|
491
|
+
puts action_format(issue.title)
|
492
|
+
puts "(comment #{comment["status"]})" if comment
|
493
|
+
end
|
494
|
+
|
495
|
+
def reopen
|
496
|
+
issue = api.reopen number
|
497
|
+
if @commenting || new_body = body
|
498
|
+
new_body ||= gets_from_editor issue
|
499
|
+
comment = api.comment number, new_body
|
500
|
+
end
|
501
|
+
puts action_format(issue.title)
|
502
|
+
puts "(comment #{comment["status"]})" if comment
|
503
|
+
end
|
504
|
+
|
505
|
+
def prepare_label
|
506
|
+
@tag ||= (body || args * " ")
|
507
|
+
raise GHI::API::InvalidRequest, "need a label" if @tag.empty?
|
508
|
+
true
|
509
|
+
end
|
510
|
+
|
511
|
+
def label
|
512
|
+
labels = api.add_label tag, number
|
513
|
+
puts action_format
|
514
|
+
puts indent(labels.join(", "))
|
515
|
+
end
|
516
|
+
|
517
|
+
def unlabel
|
518
|
+
labels = api.remove_label tag, number
|
519
|
+
puts action_format
|
520
|
+
puts indent(labels.empty? ? "no labels" : labels.join(", "))
|
521
|
+
end
|
522
|
+
|
523
|
+
def prepare_comment
|
524
|
+
@body = args.flatten.first
|
525
|
+
@commenting = false unless body.nil?
|
526
|
+
true
|
527
|
+
end
|
528
|
+
|
529
|
+
def comment
|
530
|
+
@body ||= gets_from_editor api.show(number)
|
531
|
+
comment = api.comment(number, body)
|
532
|
+
delete_message
|
533
|
+
puts "(comment #{comment["status"]})"
|
534
|
+
end
|
535
|
+
|
536
|
+
def url
|
537
|
+
url = "http://github.com/#{user}/#{repo}/issues"
|
538
|
+
if number.nil?
|
539
|
+
url << "/#{state}" unless state == :open
|
540
|
+
else
|
541
|
+
url << "#issue/#{number}"
|
542
|
+
end
|
543
|
+
defined?(Launchy) ? Launchy.open(url) : puts(url)
|
544
|
+
end
|
545
|
+
|
546
|
+
#-
|
547
|
+
# Because these are mere fallbacks, any options used earlier will muddle
|
548
|
+
# things: `ghi list` will work, `ghi list -c` will not.
|
549
|
+
#
|
550
|
+
# Argument parsing will have to better integrate with option parsing to
|
551
|
+
# overcome this.
|
552
|
+
#+
|
553
|
+
def fallback_parsing(*arguments)
|
554
|
+
arguments = arguments.flatten
|
555
|
+
case command = arguments.shift
|
556
|
+
when nil, "list"
|
557
|
+
@action = :list
|
558
|
+
if arg = arguments.shift
|
559
|
+
@state ||= arg.to_sym if %w(open closed).include? arg
|
560
|
+
@user, @repo = arg.split "/" if arg.count("/") == 1
|
561
|
+
end
|
562
|
+
when "search"
|
563
|
+
@action = :search
|
564
|
+
@search_term ||= arguments.shift
|
565
|
+
when "show", /^-?(\d+)$/
|
566
|
+
@action = :show
|
567
|
+
@number ||= ($1 || arguments.shift[/\d+/]).to_i
|
568
|
+
when "open"
|
569
|
+
@action = :open
|
570
|
+
when "edit"
|
571
|
+
@action = :edit
|
572
|
+
@number ||= arguments.shift[/\d+/].to_i
|
573
|
+
when "close"
|
574
|
+
@action = :close
|
575
|
+
@number ||= arguments.shift[/\d+/].to_i
|
576
|
+
when "reopen"
|
577
|
+
@action = :reopen
|
578
|
+
@number ||= arguments.shift[/\d+/].to_i
|
579
|
+
when "label"
|
580
|
+
@action = :label
|
581
|
+
@number ||= arguments.shift[/\d+/].to_i
|
582
|
+
@label ||= arguments.shift
|
583
|
+
when "unlabel"
|
584
|
+
@action = :unlabel
|
585
|
+
@number ||= arguments.shift[/\d+/].to_i
|
586
|
+
@label ||= arguments.shift
|
587
|
+
when "comment"
|
588
|
+
@action = :comment
|
589
|
+
@number ||= arguments.shift[/\d+/].to_i
|
590
|
+
when "claim"
|
591
|
+
@action = :claim
|
592
|
+
@number ||= arguments.shift[/\d+/].to_i
|
593
|
+
when %r{^([^/]+)/([^/]+)$}
|
594
|
+
@action = :list
|
595
|
+
@user, @repo = $1, $2
|
596
|
+
end
|
597
|
+
if @action
|
598
|
+
@args = @argv.dup
|
599
|
+
args.delete_if { |arg| arg == command }
|
600
|
+
option_parser.parse!(*args)
|
601
|
+
return true
|
602
|
+
end
|
603
|
+
unless command.start_with? "-"
|
604
|
+
warn "#{File.basename $0}: what do you mean, '#{command}'?"
|
605
|
+
end
|
606
|
+
end
|
607
|
+
end
|
608
|
+
end
|