bugzyrb 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/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