bugzyrb 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,7 @@
1
+ = bugzyrb 0.3.0, 2010-07-11
2
+ * added recentlogs
3
+ * added recentcomments
4
+
1
5
  = bugzyrb 0.2.1, 2010-07-09
2
6
  * added status which was missing
3
7
  * delete can take multiple issues
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.3.0
data/bugzyrb.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{bugzyrb}
8
- s.version = "0.2.1"
8
+ s.version = "0.3.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Rahul Kumar"]
12
- s.date = %q{2010-07-09}
12
+ s.date = %q{2010-07-12}
13
13
  s.default_executable = %q{bugzyrb}
14
14
  s.description = %q{basic, easy-to-use command-line issue-tracker using sqlite for ruby 1.9}
15
15
  s.email = %q{sentinel1879@gmail.com}
@@ -27,6 +27,8 @@ module Cmdapp
27
27
  # @param [Array] rest of args on command line
28
28
  # @return [Boolean] whether it is mapped or not.
29
29
  #
30
+ # NOTE: some of these are relevant only if we are not using subcommand, so we should move out
31
+ # to another file.
30
32
  def check_aliases action, args
31
33
  return false unless @aliases
32
34
  ret = @aliases[action]
@@ -96,10 +98,12 @@ module Cmdapp
96
98
  @actions[name.to_s] = desc
97
99
  end
98
100
 
101
+ # TODO: move to serial_number.rb or a file that deals with file_data as against sql data
102
+ # which should also have backup, load_array and save_array.
99
103
  ##
100
104
  # reads serial_number file, returns serialno for this app
101
105
  # and increments the serial number and writes back.
102
- def _get_serial_number
106
+ def get_serial_number
103
107
  require 'fileutils'
104
108
  appname = @appname
105
109
  filename = @app_serial_path || "serial_numbers"
@@ -125,23 +129,27 @@ module Cmdapp
125
129
  end
126
130
  ##
127
131
  # After doing a redo of the numbering, we need to reset the numbers for that app
128
- def _set_serial_number number
132
+ def set_serial_number number
129
133
  appname = @appname
130
134
  pattern = Regexp.new "^#{appname}:.*$"
131
135
  filename = @app_serial_path || "serial_numbers"
132
136
  # during testing redo this file does not exist, so i get errors
133
137
  if !File.exists? filename
134
- _get_serial_number
138
+ get_serial_number
135
139
  end
136
- _backup filename
140
+ backup filename
137
141
  # from Sed
138
142
  change_row filename, pattern, "#{appname}:#{number}"
139
143
  end
140
144
 
141
- def _backup filename=@app_file_path
145
+ #
146
+ # backup file to filename.org
147
+ # use prior to a modification operation that could be dangerous or risky
148
+ def backup filename=@app_file_path
142
149
  require 'fileutils'
143
150
  FileUtils.cp filename, "#{filename}.org"
144
151
  end
152
+ # exit after printing an error message
145
153
  def die text
146
154
  $stderr.puts text
147
155
  exit ERRCODE
@@ -194,6 +202,7 @@ module Cmdapp
194
202
  ##
195
203
  # retrieve version info updated by jeweler.
196
204
  # Typically used by --version option of any command.
205
+ # FIXME: link VERSION to version.rb in lib/buzg.. and read from same dir
197
206
  # @return [String, nil] version as string, or nil if file not found
198
207
  def version_info
199
208
  # thanks to Roger Pack on ruby-forum for how to get to the version
@@ -213,7 +222,7 @@ module Cmdapp
213
222
  # and returns as a string.
214
223
  # Add newline after each line
215
224
  # @return [String, nil] newline delimited string, or nil
216
- def get_lines
225
+ def old_get_lines
217
226
  lines = nil
218
227
  #$stdin.flush
219
228
  $stdin.each_line do |line|
@@ -231,118 +240,117 @@ module Cmdapp
231
240
  end
232
241
 
233
242
 
234
- # edits given text using EDITOR
235
- # @param [String] text to edit
236
- # @return [String, nil] edited string, or nil if no change
237
- def edit_text text
238
- # 2010-06-29 10:24
239
- require 'fileutils'
240
- require 'tempfile'
241
- ed = ENV['EDITOR'] || "vim"
242
- temp = Tempfile.new "tmp"
243
- File.open(temp,"w"){ |f| f.write text }
244
- mtime = File.mtime(temp.path)
245
- #system("#{ed} #{temp.path}")
246
- system(ed, temp.path)
243
+ # edits given text using EDITOR
244
+ # @param [String] text to edit
245
+ # @return [String, nil] edited string, or nil if no change
246
+ def edit_text text
247
+ # 2010-06-29 10:24
248
+ require 'fileutils'
249
+ require 'tempfile'
250
+ ed = ENV['EDITOR'] || "vim"
251
+ temp = Tempfile.new "tmp"
252
+ File.open(temp,"w"){ |f| f.write text }
253
+ mtime = File.mtime(temp.path)
254
+ #system("#{ed} #{temp.path}")
255
+ system(ed, temp.path)
247
256
 
248
- newmtime = File.mtime(temp.path)
249
- newstr = nil
250
- if mtime < newmtime
251
- # check timestamp, if updated ..
252
- #newstr = ""
253
- #File.open(temp,"r"){ |f| f.each {|r| newstr << r } }
254
- newstr = File.read(temp)
255
- #puts "I got: #{newstr}"
256
- else
257
- #puts "user quit without saving"
258
- end
259
- return newstr
260
- end
257
+ newmtime = File.mtime(temp.path)
258
+ newstr = nil
259
+ if mtime < newmtime
260
+ # check timestamp, if updated ..
261
+ newstr = File.read(temp)
262
+ else
263
+ #puts "user quit without saving"
264
+ return nil
265
+ end
266
+ return newstr.chomp if newstr
267
+ return nil
268
+ end
261
269
 
262
- # pipes given string to command
263
- # @param [String] command to pipe data to
264
- # @param [String] data to pipe to command
265
- # @example
266
- # cmd = %{mail -s "my title" rahul}
267
- # pipe_output(cmd, "some long text")
268
- # FIXME: not clear how to return error.
269
- # NOTE: this is obviously more portable than using system echo or system cat.
270
- def pipe_output (pipeto, str)
271
- #pipeto = '/usr/sbin/sendmail -t'
272
- #pipeto = %q{mail -s "my title" rahul}
273
- if pipeto != nil # i was taking pipeto from a hash, so checking
274
- proc = IO.popen(pipeto, "w+")
275
- proc.puts str
276
- proc.close_write
277
- #puts proc.gets
270
+ # pipes given string to command
271
+ # @param [String] command to pipe data to
272
+ # @param [String] data to pipe to command
273
+ # @example
274
+ # cmd = %{mail -s "my title" rahul}
275
+ # pipe_output(cmd, "some long text")
276
+ # FIXME: not clear how to return error.
277
+ # NOTE: this is obviously more portable than using system echo or system cat.
278
+ def pipe_output (pipeto, str)
279
+ #pipeto = '/usr/sbin/sendmail -t'
280
+ #pipeto = %q{mail -s "my title" rahul}
281
+ if pipeto != nil # i was taking pipeto from a hash, so checking
282
+ proc = IO.popen(pipeto, "w+")
283
+ proc.puts str
284
+ proc.close_write
285
+ #puts proc.gets
286
+ end
278
287
  end
279
- end
280
- ##
281
- # reads up template, and substirutes values from myhash
282
- # @param [String] template text
283
- # @param [Hash] values to replace in template
284
- # @return [String] template output
285
- # NOTE: probably better to use rdoc/template which can handle arrays as well.
286
- def template_replace template, myhash
287
- #tmpltext=File::read(template);
288
+ ##
289
+ # reads up template, and substirutes values from myhash
290
+ # @param [String] template text
291
+ # @param [Hash] values to replace in template
292
+ # @return [String] template output
293
+ # NOTE: probably better to use rdoc/template which can handle arrays as well.
294
+ def template_replace template, myhash
295
+ #tmpltext=File::read(template);
288
296
 
289
- t = template.dup
290
- t.gsub!( /##(.*?)##/ ) {
291
- #raise "Key '#{$1}' found in template but the value has not been set" unless ( myhash.has_key?( $1 ) )
292
- myhash[ $1 ].to_s
293
- }
294
- t
295
- end
296
- #------------------------------------------------------------
297
- # these 2 methods deal with with maintaining readline history
298
- # for various columns. _read reads up any earlier values
299
- # so user can select from them.
300
- # _save saves the values for future use.
301
- #------------------------------------------------------------
302
- # for a given column, check if there's any previous data
303
- # in our cache, and put in readlines history so user can
304
- # use or edit. Also put default value in history.
305
- # @param [String] name of column for maintaining cache
306
- # @param [String] default data for user to recall, or edit
307
- def history_read column, default=nil
308
- values = []
309
- oldstr = ""
310
- if !defined? $history_hash
311
- require 'readline'
312
- require 'yaml'
313
- filename = File.expand_path "~/.bugzy_history.yml"
314
- $history_filename = filename
315
- # if file exists with values push them into history
316
- if File.exists? filename
317
- $history_hash = YAML::load( File.open( filename ) )
318
- else
319
- $history_hash = Hash.new
297
+ t = template.dup
298
+ t.gsub!( /##(.*?)##/ ) {
299
+ #raise "Key '#{$1}' found in template but the value has not been set" unless ( myhash.has_key?( $1 ) )
300
+ myhash[ $1 ].to_s
301
+ }
302
+ t
303
+ end
304
+ #------------------------------------------------------------
305
+ # these 2 methods deal with with maintaining readline history
306
+ # for various columns. _read reads up any earlier values
307
+ # so user can select from them.
308
+ # _save saves the values for future use.
309
+ #------------------------------------------------------------
310
+ # for a given column, check if there's any previous data
311
+ # in our cache, and put in readlines history so user can
312
+ # use or edit. Also put default value in history.
313
+ # @param [String] name of column for maintaining cache
314
+ # @param [String] default data for user to recall, or edit
315
+ def history_read column, default=nil
316
+ values = []
317
+ oldstr = ""
318
+ if !defined? $history_hash
319
+ require 'readline'
320
+ require 'yaml'
321
+ filename = File.expand_path "~/.bugzy_history.yml"
322
+ $history_filename = filename
323
+ # if file exists with values push them into history
324
+ if File.exists? filename
325
+ $history_hash = YAML::load( File.open( filename ) )
326
+ else
327
+ $history_hash = Hash.new
328
+ end
320
329
  end
330
+ values.push(*$history_hash[column]) if $history_hash.has_key? column
331
+ # push existing value into history also, so it can be edited
332
+ values.push(default) if default
333
+ values.uniq!
334
+ Readline::HISTORY.clear # else previous values of other fields also come in
335
+ Readline::HISTORY.push(*values) unless values.empty?
336
+ #puts Readline::HISTORY.to_a
321
337
  end
322
- values.push(*$history_hash[column]) if $history_hash.has_key? column
323
- # push existing value into history also, so it can be edited
324
- values.push(default) if default
325
- values.uniq!
326
- Readline::HISTORY.clear # else previous values of other fields also come in
327
- Readline::HISTORY.push(*values) unless values.empty?
328
- #puts Readline::HISTORY.to_a
329
- end
330
- ##
331
- # update our cache with str if not present in cache already
332
- # @param [String] name of column for maintaining cache
333
- # @param [String] str : data just entered by user
334
- #
335
- def history_save column, str
336
- return if str.nil? or str == ""
337
- if $history_hash.has_key? column
338
- return if $history_hash[column].include? str
339
- end
340
- ($history_hash[column] ||= []) << str
341
- filename = $history_filename
342
- File.open( filename, 'w' ) do |f|
343
- f << $history_hash.to_yaml
338
+ ##
339
+ # update our cache with str if not present in cache already
340
+ # @param [String] name of column for maintaining cache
341
+ # @param [String] str : data just entered by user
342
+ #
343
+ def history_save column, str
344
+ return if str.nil? or str == ""
345
+ if $history_hash.has_key? column
346
+ return if $history_hash[column].include? str
347
+ end
348
+ ($history_hash[column] ||= []) << str
349
+ filename = $history_filename
350
+ File.open( filename, 'w' ) do |f|
351
+ f << $history_hash.to_yaml
352
+ end
344
353
  end
345
- end
346
354
  # separates args to list-like operations
347
355
  # +xxx means xxx should match in output
348
356
  # -xxx means xxx should not exist in output
@@ -377,7 +385,159 @@ end
377
385
  end
378
386
  rows
379
387
  end
388
+ # ----------- file operations -------------
389
+ #
390
+ def file_read filename
391
+ filename = File.expand_path filename
392
+ return File.read(filename)
393
+ #if File.exists? filename
394
+ end
395
+ def file_write filename, text
396
+ filename = File.expand_path filename
397
+ File.open(filename,"w"){ |f| f.write text }
398
+ #if File.exists? filename
399
+ end
400
+ def file_append filename, text
401
+ filename = File.expand_path filename
402
+ File.open(temp,"a"){ |f| f.write text }
403
+ end
404
+ def check_file filename=@app_file_path
405
+ File.exists?(filename) or die "#{filename} does not exist in this dir. "
406
+ end
380
407
 
408
+ ##
409
+ # separates args into tag or subcommand and items
410
+ # This allows user to pass e.g. a priority first and then item list
411
+ # or item list first and then priority.
412
+ # This can only be used if the tag or pri or status is non-numeric and the item is numeric.
413
+ def _separate args, pattern=nil #/^[a-zA-Z]/
414
+ tag = nil
415
+ items = []
416
+ args.each do |arg|
417
+ if arg =~ /^[0-9\.]+$/
418
+ items << arg
419
+ else
420
+ tag = arg
421
+ if pattern
422
+ die "#{@action}: #{arg} appears invalid." if arg !~ pattern
423
+ end
424
+ end
425
+ end
426
+ items = nil if items.empty?
427
+ return tag, items
428
+ end
381
429
 
430
+ ## prompts user for multiline input
431
+ # NOTE: we do not take Ctrl-d as EOF then causes an error in next input in 1.9 (not 1.8)
432
+ # @param [String] text to use as prompt
433
+ # @return [String, nil] string with newlines or nil (if nothing entered).
434
+ # FIXME: move to Cmdapp
435
+ def get_lines prompt=nil
436
+ #prompt ||= "Enter multiple lines, to quit enter . on empty line"
437
+ #message prompt
438
+ str = ""
439
+ while $stdin.gets # reads from STDIN
440
+ case $_.chomp
441
+ when "."
442
+ break
443
+ when ".vim"
444
+ return edit_text str
445
+ end
446
+ str << $_
447
+ #puts "Read: #{$_}" # writes to STDOUT
448
+ end
449
+ return nil if str == ""
450
+ return str.chomp
451
+ end
452
+ # get a string from user, using readline or gets
453
+ # if readline, then manage column specific history
454
+ # FIXME: move to Cmdapp.
455
+ def _gets column, prompt, default=nil
456
+ text = "#{prompt}? "
457
+ text << "|#{default}|" if default
458
+ puts text
459
+ if $use_readline
460
+ Cmdapp::history_read column, default
461
+ str = Readline::readline('>', false)
462
+ Cmdapp::history_save column, str
463
+ str = default if str.nil? or str == ""
464
+ return str
465
+ else
466
+ str = $stdin.gets.chomp
467
+ str = default if str.nil? or str == ""
468
+ return str
469
+ end
470
+ end
471
+ # get choice from user from a list of options
472
+ # @param [String] prompt text
473
+ # @param [Array] values to chose from
474
+ def _choice prompt, choices
475
+ choose do |menu|
476
+ menu.prompt = prompt
477
+ menu.choices(*choices) do |n| return n; end
478
+ end
479
+ end
480
+ #
481
+ # take user input based on value of flag
482
+ # @param [String] column name
483
+ # @param [Boolean, Symbol] true, false, :freeform, :choice
484
+ # @param [String, nil] text to prompt
485
+ # @param [Array, nil] choices array or nil
486
+ # @param [Object] default value
487
+ # @return [String, nil] users choice
488
+ #
489
+ # TODO: should we not check for the ask_x methods and call them if present.
490
+ # FIXME: move to Cmdapp
491
+ def user_input column, prompt_flag, prompt_text=nil, choices=nil, default=nil
492
+ if prompt_flag == true
493
+ prompt_flag = :freeform
494
+ prompt_flag = :choice if choices
495
+ end
496
+ case prompt_flag
497
+ when :freeform
498
+ prompt_text ||= "#{column.capitalize}"
499
+ #str = ask(prompt_text){ |q| q.default = default if default }
500
+ str = _gets(column, prompt_text, default)
501
+ return str
502
+ when :choice
503
+ prompt_text ||= "Select #{column}:"
504
+ str = _choice(prompt_text, choices)
505
+ return str
506
+ when :multiline, :ml
507
+ return Cmdapp::edit_text default
508
+ when false
509
+ return default
510
+ end
511
+ end
512
+ # indents given string, n spaces
513
+ # http://redmine.ruby-lang.org/issues/show/749 Thomas Sawyer
514
+ def indent(str,n)
515
+ if n >= 0
516
+ str.gsub(/^/, ' ' * n)
517
+ else
518
+ str.gsub(/^ {0,#{-n}}/, "")
519
+ end
520
+ end
521
+ # indents a string, indenting all but first line.
522
+ # This allows us to print a string as follows:
523
+ # Description : The description starts here.
524
+ # And continues here. The first line was not indented.
525
+ # http://www.ralfebert.de/blog/ruby/string_helpers/ Ralf Ebert
526
+ def indent2(str, count)
527
+ if str
528
+ char = ' '
529
+ #(char * count) + gsub(/(\n+)/) { $1 + (char * count) }
530
+ str.gsub(/(\n+)/) { $1 + (char * count) }
531
+ end
532
+ end
533
+ # for updating in log file
534
+ # truncates long strings comments, descriptions, fix etc and removed newlines
535
+ def truncate string, count
536
+ string = string.to_s
537
+ string.tr!("\n",' ')
538
+ return string if string.length <= count
539
+ (string[0..(count-4)] + " ...")
540
+ end
382
541
 
542
+ # ADD
383
543
  end
data/lib/bugzyrb.rb CHANGED
@@ -5,7 +5,6 @@
5
5
  * Author: rkumar
6
6
  * Date: 2010-06-24
7
7
  * License: Ruby License
8
- * Now requires subcommand gem
9
8
 
10
9
  =end
11
10
  require 'rubygems'
@@ -220,7 +219,7 @@ SQL
220
219
  title = body['title']
221
220
  logid = db.sql_logs_insert rowid, "create", "#{rowid} #{type}: #{title}"
222
221
  body["id"] = rowid
223
- mail_issue body
222
+ mail_issue nil, body
224
223
  0
225
224
  end
226
225
 
@@ -248,8 +247,10 @@ SQL
248
247
  title = text
249
248
  desc = nil
250
249
  if $prompt_desc
250
+ # choice of vim or this XXX also how to store in case of error or abandon
251
+ # and allow user to edit, so no retyping. This could be for mult fields
251
252
  message "Enter a detailed description (. to exit): "
252
- desc = get_lines
253
+ desc = Cmdapp.get_lines
253
254
  #message "You entered #{desc}"
254
255
  end
255
256
  type = $default_type || "bug"
@@ -257,34 +258,34 @@ SQL
257
258
  status = $default_status || "open"
258
259
  priority = $default_priority || "P3"
259
260
  if $prompt_type
260
- type = _choice("Select type:", %w[bug enhancement feature task] )
261
+ type = Cmdapp._choice("Select type:", %w[bug enhancement feature task] )
261
262
  #message "You selected #{type}"
262
263
  end
263
264
  if $prompt_severity
264
- severity = _choice("Select severity:", %w[normal critical moderate] )
265
+ severity = Cmdapp._choice("Select severity:", %w[normal critical moderate] )
265
266
  #message "You selected #{severity}"
266
267
  end
267
268
  if $prompt_status
268
- status = _choice("Select status:", %w[open started closed stopped canceled] )
269
+ status = Cmdapp._choice("Select status:", %w[open started closed stopped canceled] )
269
270
  #message "You selected #{status}"
270
271
  end
271
272
  assigned_to = $default_assigned_to
272
273
  if $prompt_assigned_to
273
274
  message "Assign to:"
274
275
  #assigned_to = $stdin.gets.chomp
275
- assigned_to = _gets "assigned_to", "assigned_to", $default_assigned_to
276
+ assigned_to = Cmdapp._gets "assigned_to", "assigned_to", $default_assigned_to
276
277
  #message "You selected #{assigned_to}"
277
278
  end
278
279
  project = component = version = nil
279
280
  # project
280
281
  if $use_project
281
- project = user_input('project', $prompt_project, nil, $valid_project, $default_project)
282
+ project = Cmdapp.user_input('project', $prompt_project, nil, $valid_project, $default_project)
282
283
  end
283
284
  if $use_component
284
- component = user_input('component', $prompt_component, nil, $valid_component, $default_component)
285
+ component = Cmdapp.user_input('component', $prompt_component, nil, $valid_component, $default_component)
285
286
  end
286
287
  if $use_version
287
- version = user_input('version', $prompt_version, nil, $valid_version, $default_version)
288
+ version = Cmdapp.user_input('version', $prompt_version, nil, $valid_version, $default_version)
288
289
  end
289
290
 
290
291
  start_date = @now
@@ -315,17 +316,18 @@ SQL
315
316
  puts "Issue #{rowid} created"
316
317
  logid = db.sql_logs_insert rowid, "create", "#{rowid} #{type}: #{title}"
317
318
  body["id"] = rowid
318
- mail_issue body
319
+ mail_issue nil, body
319
320
 
320
321
  0
321
322
  end
322
- def mail_issue row, emailid=nil
323
+ def mail_issue subject, row, emailid=nil
323
324
  emailid ||= $default_user
324
325
  body = <<TEXT
325
326
  Id : #{row['id']}
326
327
  Title : #{row['title']}
327
328
  Description : #{row['description']}
328
329
  Type : #{row['type']}
330
+ Status : #{row['status']}
329
331
  Start Date : #{row['start_date']}
330
332
  Due Date : #{row['due_date']}
331
333
  Priority : #{row['priority']}
@@ -335,9 +337,9 @@ TEXT
335
337
  body << " Project : #{row['project']}\n" if $use_project
336
338
  body << " Component : #{row['component']}\n" if $use_component
337
339
  body << " Version : #{row['version']}\n" if $use_version
338
- title = "#{row['id']}: #{row['title']} "
340
+ subject ||= "#{row['id']}: #{row['title']} "
339
341
 
340
- cmd = %{ mail -s "#{title}" "#{emailid}" }
342
+ cmd = %{ mail -s "#{subject}" "#{emailid}" }
341
343
  #puts cmd
342
344
  Cmdapp::pipe_output(cmd, body)
343
345
  end
@@ -352,32 +354,40 @@ TEXT
352
354
  db, row = validate_id id
353
355
  die "No data found for #{id}" unless row
354
356
  puts "[#{row['type']} \##{row['id']}] #{row['title']}"
355
- puts row['description']
356
- puts
357
+ puts "Description:"
358
+ puts Cmdapp.indent(row['description'],3)
359
+ puts "\nAdded by #{row['created_by']} on #{row['date_created']}. Updated #{row['date_modified']}."
357
360
  comment_count = 0
358
361
  #puts row
359
362
  row.each_pair { |name, val|
360
363
  next if name == "project" && !$use_project
361
364
  next if name == "version" && !$use_version
362
365
  next if name == "component" && !$use_component
366
+ next if %w{ id title description created_by date_created date_modified }.include? name
363
367
  comment_count = val.to_i if name == "comment_count"
368
+ val = Cmdapp.indent2(val, 18) if name == "fix"
364
369
  n = sprintf("%-15s", name);
365
370
  puts "#{n} : #{val}"
366
371
  }
367
372
  puts
368
373
  if comment_count > 0
369
374
  puts "Comments :"
375
+ ctr = 0
370
376
  db.select_where "comments", "id", id do |r|
371
- #puts r.join(" | ")
372
- puts "(#{r['date_created']}) [ #{r['created_by']} ] #{r['comment']}"
373
- #pp r
377
+ #puts "(#{r['date_created']}) [ #{r['created_by']} ] #{r['comment']}"
378
+ ctr += 1
379
+ puts "------- (#{r['date_created']}) #{r['created_by']} (#{ctr})------"
380
+ puts r['comment']
374
381
  end
382
+ puts
375
383
  end
376
384
  puts "Log:"
385
+ ctr = 0
377
386
  db.select_where "log", "id", id do |r|
378
- #puts r.join(" | ")
379
- puts "------- (#{r['date_created']}) ------"
380
- puts "#{r['field']} [ #{r['created_by']} ] #{r['log']} "
387
+ ctr += 1
388
+ #puts "------- (#{r['date_created']}) #{r['created_by']} ------"
389
+ puts "------- #{r['date_created']} - #{r['created_by']} (#{ctr})------"
390
+ puts " * [#{r['field']}]: #{r['log']} "
381
391
  #pp r
382
392
  end
383
393
  end
@@ -412,7 +422,7 @@ TEXT
412
422
  editable << "project" if $use_project
413
423
  editable << "component" if $use_component
414
424
  editable << "version" if $use_version
415
- sel = _choice "Select field to edit", editable
425
+ sel = Cmdapp._choice "Select field to edit", editable
416
426
  print "You chose: #{sel}"
417
427
  old = row[sel]
418
428
  puts " Current value is: #{old}"
@@ -434,8 +444,8 @@ TEXT
434
444
  message str
435
445
  db.sql_update "bugs", id, sel, str
436
446
  puts "Updated #{id}"
437
- str = str.to_s
438
- rowid = db.sql_logs_insert id, sel, "[#{id}] updated [#{sel}] with #{str[0..50]}"
447
+ sstr = Cmdapp.truncate(str.to_s,50)
448
+ rowid = db.sql_logs_insert id, sel, "[#{id}] updated [#{sel}] with #{sstr}"
439
449
  0
440
450
  end
441
451
  # deletes given issue
@@ -482,9 +492,11 @@ TEXT
482
492
 
483
493
  logid = db.sql_logs_insert rowid, "create", "#{rowid} #{type}: #{title}"
484
494
  newrow["id"] = rowid
485
- mail_issue newrow
495
+ mail_issue nil, newrow
486
496
  end
487
- def viewlogs args
497
+ # view logs for a given id, or highest issue
498
+ # @param [Array] issue id
499
+ def viewlogs args=nil
488
500
  db = get_db
489
501
  id = args[0].nil? ? db.max_bug_id : args[0]
490
502
  row = db.sql_select_rowid "bugs", id
@@ -591,16 +603,16 @@ TEXT
591
603
  puts "GOT:: #{args}"
592
604
  end
593
605
  def ask_type old=nil
594
- type = _choice("Select type:", %w[bug enhancement feature task] )
606
+ type = Cmdapp._choice("Select type:", %w[bug enhancement feature task] )
595
607
  end
596
608
  def ask_severity old=nil
597
- severity = _choice("Select severity:", %w[normal critical moderate] )
609
+ severity = Cmdapp._choice("Select severity:", %w[normal critical moderate] )
598
610
  end
599
611
  def ask_status old=nil
600
- status = _choice("Select status:", %w[open started closed stopped canceled] )
612
+ status = Cmdapp._choice("Select status:", %w[open started closed stopped canceled] )
601
613
  end
602
614
  def ask_priority old=nil
603
- priority = _choice("Select priority:", %w[P1 P2 P3 P4 P5] )
615
+ priority = Cmdapp._choice("Select priority:", %w[P1 P2 P3 P4 P5] )
604
616
  end
605
617
  def ask_fix old=nil
606
618
  Cmdapp::edit_text old
@@ -620,22 +632,23 @@ TEXT
620
632
  unless id
621
633
  id = ask("Issue Id? ", Integer)
622
634
  end
635
+ db, row = validate_id id, true
636
+ die "No issue found for #{id}" unless row
623
637
  if !args.empty?
624
638
  comment = args.join(" ")
625
639
  else
626
640
  message "Enter a comment (. to exit): "
627
- comment = get_lines
641
+ comment = Cmdapp.get_lines
628
642
  end
629
643
  die "Operation cancelled" if comment.nil? or comment.empty?
630
644
  message "Comment is: #{comment}."
631
- db, row = validate_id id
632
- die "No issue found for #{id}" unless row
633
645
  message "Adding comment to #{id}: #{row['title']}"
634
646
  _comment db, id, comment
635
647
  0
636
648
  end
637
649
  # insert comment into database
638
650
  # called from interactive, as well as "close" or others
651
+ # Should we send a mail here ? XXX
639
652
  def _comment db, id, text
640
653
  rowid = db.sql_comments_insert id, text
641
654
  puts "Comment #{rowid} created"
@@ -644,55 +657,43 @@ TEXT
644
657
  commcount = handle.get_first_value( "select count(id) from comments where id = #{id};" )
645
658
  commcount = commcount.to_i
646
659
  db.sql_update "bugs", id, "comment_count", commcount
647
- rowid = db.sql_logs_insert id, "comment",text[0..50]
660
+ rowid = db.sql_logs_insert id, "comment", Cmdapp.truncate(text, 50)
648
661
  end
649
662
  # prompts user for a fix related to an issue
663
+ # # XXX what if fix already exists, will we overwrite.
650
664
  def fix args #id, fix
651
665
  id = args.shift
652
666
  unless id
653
667
  id = ask("Issue Id? ", Integer)
654
668
  end
669
+ db, row = validate_id id
655
670
  if !args.empty?
656
671
  text = args.join(" ")
672
+ if row['fix']
673
+ die "Sorry. I already have a fix, pls edit ... #{row['fix']}"
674
+ end
657
675
  else
658
- message "Enter a fix (. to exit): "
659
- text = get_lines
676
+ # XXX give the choice of using vim
677
+ if row['fix']
678
+ text = Cmdapp.edit_text row['fix']
679
+ else
680
+ message "Enter a fix (. to exit): "
681
+ text = Cmdapp.get_lines
682
+ end
660
683
  end
684
+ # FIXME: what if user accidentally enters a fix, and wants to nullify ?
661
685
  die "Operation cancelled" if text.nil? or text.empty?
662
686
  message "fix is: #{text}."
663
- db, row = validate_id id
664
687
  message "Adding fix to #{id}: #{row['title']}"
665
688
  _fix db, id, text
666
689
  0
667
690
  end
691
+ # internal method that actually updates the fix. can be called
692
+ # from fix or from close using --fix
693
+ # Should we send a mail here ? XXX
668
694
  def _fix db, id, text
669
695
  db.sql_update "bugs", id, "fix", text
670
- rowid = db.sql_logs_insert id, "fix", text[0..50]
671
- end
672
- ## internal method to log an action
673
- # @param [Fixnum] id
674
- # @param [String] column or create/delete for row
675
- # @param [String] details such as content added, or content changed
676
- def log id, field, text
677
- id = args.shift
678
- unless id
679
- id = ask("Issue Id? ", Integer)
680
- end
681
- if !args.empty?
682
- comment = args.join(" ")
683
- else
684
- message "Enter a comment (. to exit): "
685
- comment = get_lines
686
- end
687
- die "Operation cancelled" if comment.nil? or comment.empty?
688
- message "Comment is: #{comment}."
689
- db = get_db
690
- row = db.sql_select_rowid "bugs", id
691
- die "No issue found for #{id}" unless row
692
- message "Adding comment to #{id}: #{row['title']}"
693
- rowid = db.sql_logs_insert id, field, log
694
- puts "Comment #{rowid} created"
695
- 0
696
+ rowid = db.sql_logs_insert id, "fix", Cmdapp.truncate(text, 50)
696
697
  end
697
698
 
698
699
  ##
@@ -724,6 +725,8 @@ TEXT
724
725
  db.sql_update "bugs", id, field, value
725
726
  puts "Updated #{id}"
726
727
  rowid = db.sql_logs_insert id, field, "[#{id}] updated [#{field}] with #{value}"
728
+ row[field] = value
729
+ mail_issue "[#{id}] updated [#{field}] with #{value}", row
727
730
  else
728
731
  message "#{id} already #{value}"
729
732
  end
@@ -788,173 +791,62 @@ TEXT
788
791
  }
789
792
  end
790
793
 
791
- def check_file filename=@app_file_path
792
- File.exists?(filename) or die "#{filename} does not exist in this dir. Use 'add' to create an item first."
793
- end
794
- ##
795
- # colorize each line, if required.
796
- # However, we should put the colors in some Map, so it can be changed at configuration level.
797
- #
798
- def colorize # TODO:
799
- colorme = @options[:colorize]
800
- @data.each do |r|
801
- if @options[:hide_numbering]
802
- string = "#{r[1]} "
803
- else
804
- string = " #{r[0]} #{r[1]} "
805
- end
806
- if colorme
807
- m=string.match(/\(([A-Z])\)/)
808
- if m
809
- case m[1]
810
- when "A", "B", "C", "D"
811
- pri = self.class.const_get("PRI_#{m[1]}")
812
- #string = "#{YELLOW}#{BOLD}#{string}#{CLEAR}"
813
- string = "#{pri}#{string}#{CLEAR}"
814
- else
815
- string = "#{NORMAL}#{GREEN}#{string}#{CLEAR}"
816
- #string = "#{BLUE}\e[6m#{string}#{CLEAR}"
817
- #string = "#{BLUE}#{string}#{CLEAR}"
818
- end
819
- else
820
- #string = "#{NORMAL}#{string}#{CLEAR}"
821
- # no need to put clear, let it be au natural
822
- end
823
- end # colorme
824
- ## since we've added notes, we convert C-a to newline with spaces
825
- # so it prints in next line with some neat indentation.
826
- string.gsub!('', "\n ")
827
- #string.tr! '', "\n"
828
- puts string
829
- end
830
- end
831
- # internal method for sorting on reverse of line (status, priority)
832
- def sort # TODO:
833
- fold_subtasks
834
- if @options[:reverse]
835
- @data.sort! { |a,b| a[1] <=> b[1] }
836
- else
837
- @data.sort! { |a,b| b[1] <=> a[1] }
838
- end
839
- unfold_subtasks
840
- end
841
- def grep # TODO:
842
- r = Regexp.new @options[:grep]
843
- #@data = @data.grep r
844
- @data = @data.find_all {|i| i[1] =~ r }
845
- end
846
-
847
- ##
848
- # separates args into tag or subcommand and items
849
- # This allows user to pass e.g. a priority first and then item list
850
- # or item list first and then priority.
851
- # This can only be used if the tag or pri or status is non-numeric and the item is numeric.
852
- def _separate args, pattern=nil #/^[a-zA-Z]/
853
- tag = nil
854
- items = []
855
- args.each do |arg|
856
- if arg =~ /^[0-9\.]+$/
857
- items << arg
858
- else
859
- tag = arg
860
- if pattern
861
- die "#{@action}: #{arg} appears invalid." if arg !~ pattern
862
- end
863
- end
864
- end
865
- items = nil if items.empty?
866
- return tag, items
867
- end
868
794
 
869
- # get choice from user from a list of options
870
- # @param [String] prompt text
871
- # @param [Array] values to chose from
872
- # FIXME: move to Cmdapp
873
- def _choice prompt, choices
874
- choose do |menu|
875
- menu.prompt = prompt
876
- menu.choices(*choices) do |n| return n; end
877
- end
878
- end
879
- #
880
- # take user input based on value of flag
881
- # @param [String] column name
882
- # @param [Boolean, Symbol] true, false, :freeform, :choice
883
- # @param [String, nil] text to prompt
884
- # @param [Array, nil] choices array or nil
885
- # @param [Object] default value
886
- # @return [String, nil] users choice
887
- #
888
- # TODO: should we not check for the ask_x methods and call them if present.
889
- # FIXME: move to Cmdapp
890
- def user_input column, prompt_flag, prompt_text=nil, choices=nil, default=nil
891
- if prompt_flag == true
892
- prompt_flag = :freeform
893
- prompt_flag = :choice if choices
894
- end
895
- case prompt_flag
896
- when :freeform
897
- prompt_text ||= "#{column.capitalize}"
898
- #str = ask(prompt_text){ |q| q.default = default if default }
899
- str = _gets(column, prompt_text, default)
900
- return str
901
- when :choice
902
- prompt_text ||= "Select #{column}:"
903
- str = _choice(prompt_text, choices)
904
- return str
905
- when :multiline, :ml
906
- return Cmdapp::edit_text default
907
- when false
908
- return default
909
- end
910
- end
911
795
  def test args=nil
912
796
  puts "This is only for testing things out"
913
797
  if $use_project
914
- project = user_input('project', $prompt_project, nil, $valid_project, $default_project)
798
+ project = Cmdapp.user_input('project', $prompt_project, nil, $valid_project, $default_project)
915
799
  puts project
916
800
  end
917
801
  if $use_component
918
- component = user_input('component', $prompt_component, nil, $valid_component, $default_component)
802
+ component = Cmdapp.user_input('component', $prompt_component, nil, $valid_component, $default_component)
919
803
  puts component
920
804
  end
921
805
  end
922
- ## prompts user for multiline input
923
- # NOTE: we do not take Ctrl-d as EOF then causes an error in next input in 1.9 (not 1.8)
924
- # @param [String] text to use as prompt
925
- # @return [String, nil] string with newlines or nil (if nothing entered).
926
- # FIXME: move to Cmdapp
927
- def get_lines prompt=nil
928
- #prompt ||= "Enter multiple lines, to quit enter . on empty line"
929
- #message prompt
930
- str = ""
931
- while $stdin.gets # reads from STDIN
932
- if $_.chomp == "."
933
- break
934
- end
935
- str << $_
936
- #puts "Read: #{$_}" # writes to STDOUT
806
+ #
807
+ # prints recent log/activity
808
+ # @param [Array] first index is limit (how many rows to show), default 10
809
+ def recentlogs args=nil
810
+ limit = args[0] || 10
811
+ sql = "select log.id, title, log.created_by, log.date_created, log from log,bugs where bugs.id = log.id order by log.date_created desc limit #{limit}"
812
+
813
+ db = get_db
814
+ db.run sql do |row|
815
+ log = Cmdapp.indent2( row['log'],20)
816
+ text = <<-TEXT
817
+
818
+ id : [#{row['id']}] #{row['title']}
819
+ action_by : #{row['created_by']}
820
+ date : #{row['date_created']}
821
+ activity : #{log}
822
+
823
+ TEXT
824
+ #puts row.keys
825
+ puts text
826
+
937
827
  end
938
- return nil if str == ""
939
- return str.chomp
940
828
  end
941
- # get a string from user, using readline or gets
942
- # if readline, then manage column specific history
943
- # FIXME: move to Cmdapp.
944
- def _gets column, prompt, default=nil
945
- text = "#{prompt}? "
946
- text << "|#{default}|" if default
947
- puts text
948
- if $use_readline
949
- Cmdapp::history_read column, default
950
- str = Readline::readline('>', false)
951
- Cmdapp::history_save column, str
952
- str = default if str.nil? or str == ""
953
- return str
954
- else
955
- str = $stdin.gets.chomp
956
- str = default if str.nil? or str == ""
957
- return str
829
+ #
830
+ # prints recent comments
831
+ # @param [Array] first index is limit (how many rows to show), default 10
832
+ def recentcomments args=nil
833
+ limit = args[0] || 10
834
+ sql = "select comments.id, title, comments.created_by, comments.date_created, comment from comments,bugs where bugs.id = comments.id order by comments.date_created desc limit #{limit}"
835
+
836
+ db = get_db
837
+ db.run sql do |row|
838
+ comment = Cmdapp.indent2( row['comment'],20)
839
+ text = <<-TEXT
840
+
841
+ id : [#{row['id']}] #{row['title']}
842
+ author : #{row['created_by']}
843
+ date : #{row['date_created']}
844
+ comment : #{comment}
845
+
846
+ TEXT
847
+ #puts row.keys
848
+ puts text
849
+
958
850
  end
959
851
  end
960
852
  # ADD here
@@ -1111,7 +1003,11 @@ TEXT
1111
1003
  end
1112
1004
  Subcommands::command :comment do |opts|
1113
1005
  opts.banner = "Usage: comment [options] ISSUE_NO TEXT"
1114
- opts.description = "Add comment a given issue"
1006
+ opts.description = "Add a comment to a given issue"
1007
+ end
1008
+ Subcommands::command :fix do |opts|
1009
+ opts.banner = "Usage: fix [options] ISSUE_NO TEXT"
1010
+ opts.description = "Add a fix for a given issue"
1115
1011
  end
1116
1012
  Subcommands::command :test do |opts|
1117
1013
  opts.banner = "Usage: test [options] ISSUE_NO TEXT"
@@ -1144,6 +1040,14 @@ TEXT
1144
1040
  opts.banner = "Usage: viewlogs [options] ISSUE_NO"
1145
1041
  opts.description = "view logs for an issue"
1146
1042
  end
1043
+ Subcommands::command :recentlogs do |opts|
1044
+ opts.banner = "Usage: recentlogs [options] <HOWMANY>"
1045
+ opts.description = "view recent logs/activity, default last 10 logs "
1046
+ end
1047
+ Subcommands::command :recentcomments do |opts|
1048
+ opts.banner = "Usage: recentcomments [options] <HOWMANY>"
1049
+ opts.description = "view recent comments, default last 10 logs "
1050
+ end
1147
1051
  # XXX order of these 2 matters !! reverse and see what happens
1148
1052
  Subcommands::command :close, :clo do |opts|
1149
1053
  opts.banner = "Usage: clo [options] <ISSUENO>"
@@ -1178,16 +1082,10 @@ TEXT
1178
1082
  opts.on("-f", "--[no-]force", "force - don't prompt") do |v|
1179
1083
  options[:force] = v
1180
1084
  end
1181
- #opts.on("--recursive", "operate on subtasks also") { |v|
1182
- #options[:recursive] = v
1183
- #}
1184
1085
  end
1185
1086
  Subcommands::command :status do |opts|
1186
1087
  opts.banner = "Usage: status [options] <STATUS> <ISSUE>"
1187
1088
  opts.description = "Change the status of an issue. \t<STATUS> are open closed started canceled stopped "
1188
- #opts.on("--recursive", "operate on subtasks also") { |v|
1189
- #options[:recursive] = v
1190
- #}
1191
1089
  end
1192
1090
  # TODO:
1193
1091
  #Subcommands::command :tag do |opts|
metadata CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
4
4
  prerelease: false
5
5
  segments:
6
6
  - 0
7
- - 2
8
- - 1
9
- version: 0.2.1
7
+ - 3
8
+ - 0
9
+ version: 0.3.0
10
10
  platform: ruby
11
11
  authors:
12
12
  - Rahul Kumar
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-07-09 00:00:00 +05:30
17
+ date: 2010-07-12 00:00:00 +05:30
18
18
  default_executable: bugzyrb
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency