[ditz-talk] [PATCH] Generalize handling of issues to support more types of issue.

Nicolas Pouillard nicolas.pouillard at gmail.com
Thu Apr 10 08:48:49 EDT 2008


---
 lib/lowline.rb       |    5 +++-
 lib/model-objects.rb |    8 ++++++-
 lib/operator.rb      |   58 +++++++++++++++++++++++++-------------------------
 lib/util.rb          |    8 +++++++
 4 files changed, 48 insertions(+), 31 deletions(-)

diff --git a/lib/lowline.rb b/lib/lowline.rb
index 74895d6..3d0a781 100644
--- a/lib/lowline.rb
+++ b/lib/lowline.rb
@@ -17,7 +17,10 @@ class String
   def blank?; self =~ /\A\s*\z/ end
   def underline; self + "\n" + ("-" * self.length) end
   def multiline prefix=""; blank? ? "" : "\n" + self.gsub(/^/, prefix) end
-  def pluralize n; n.to_pretty_s + " " + (n == 1 ? self : self + "s") end # oh yeah
+  def pluralize n, b=true
+    s = (n == 1 ? self : (self == 'bugfix' ? 'bugfixes' : self + "s")) # oh yeah
+    b ? n.to_pretty_s + " " + s : s
+  end
 end
 
 class Array
diff --git a/lib/model-objects.rb b/lib/model-objects.rb
index 5cd3156..e5bf293 100644
--- a/lib/model-objects.rb
+++ b/lib/model-objects.rb
@@ -83,6 +83,10 @@ EOS
     issues.select { |i| i.release.nil? }
   end
 
+  def group_issues these_issues=issues
+    these_issues.group_by { |i| i.type }.sort_by { |(t,g)| Issue::TYPE_ORDER[t] }
+  end
+
   def assign_issue_names!
     prefixes = components.map { |c| [c.name, c.name.gsub(/^\s+/, "-").downcase] }.to_h
     ids = components.map { |c| [c.name, 0] }.to_h
@@ -122,6 +126,8 @@ class Issue < ModelObject
   STATUS_WIDGET = { :unstarted => "_", :in_progress => ">", :paused => "=", :closed => "x" }
   DISPOSITIONS = [ :fixed, :wontfix, :reorg ]
   TYPES = [ :bugfix, :feature ]
+  TYPE_ORDER = { :bugfix => 0, :feature => 1 }
+  TYPE_LETTER = { 'b' => :bugfix, 'f' => :feature }
   STATUSES = STATUS_WIDGET.keys
 
   STATUS_STRINGS = { :in_progress => "in progress", :wontfix => "won't fix" }
@@ -213,7 +219,7 @@ class Issue < ModelObject
 
   def get_type config, project
     type = ask "Is this a (b)ugfix or a (f)eature?", :restrict => /^[bf]$/
-    type == "b" ? :bugfix : :feature
+    TYPE_LETTER[type]
   end
 
   def get_component config, project
diff --git a/lib/operator.rb b/lib/operator.rb
index 74ce0b0..355b8e2 100644
--- a/lib/operator.rb
+++ b/lib/operator.rb
@@ -35,23 +35,20 @@ class Operator
       releases.each do |r|
         next if r.released? unless force_show
 
-        bugs = project.issues.
-          select { |i| i.type == :bugfix && i.release == r.name }
-        feats = project.issues.
-          select { |i| i.type == :feature && i.release == r.name }
+        groups = project.group_issues(project.issues_for_release(r))
 
-        #next if bugs.empty? && feats.empty? unless force_show
+        #next if groups.empty? unless force_show
 
-        ret << [r, bugs, feats]
+        ret << [r, groups]
       end
 
       return ret unless show_unassigned
 
-      bugs = project.issues.select { |i| i.type == :bugfix && i.release.nil? }
-      feats = project.issues.select { |i| i.type == :feature && i.release.nil? }
+      groups = project.group_issues(project.unassigned_issues)
 
-      return ret if bugs.empty? && feats.empty? unless force_show
-      ret << [nil, bugs, feats]
+      return ret if groups.empty? unless force_show
+
+      ret << [nil, groups]
     end
     private :parse_releases_arg
 
@@ -132,7 +129,7 @@ EOS
     puts
   end
 
-  operation :add, "Add a bug/feature request"
+  operation :add, "Add an issue"
   def add project, config
     issue = Issue.create_interactively(:args => [config, project]) or return
     comment = ask_multiline "Comments"
@@ -142,7 +139,7 @@ EOS
     puts "Added issue #{issue.name}."
   end
 
-  operation :drop, "Drop a bug/feature request", :issue
+  operation :drop, "Drop an issue", :issue
   def drop project, config, issue
     project.drop_issue issue
     puts "Dropped #{issue.name}. Note that other issue names may have changed."
@@ -176,26 +173,30 @@ EOS
 
   operation :status, "Show project status", :maybe_release
   def status project, config, releases
-    releases.each do |r, bugs, feats|
-      title, bar = [r ? r.name : "unassigned", status_bar_for(bugs + feats)]
-
-      ncbugs = bugs.count_of { |b| b.closed? }
-      ncfeats = feats.count_of { |f| f.closed? }
-      pcbugs = 100.0 * (bugs.empty? ? 1.0 : ncbugs.to_f / bugs.size)
-      pcfeats = 100.0 * (feats.empty? ? 1.0 : ncfeats.to_f / feats.size)
+    releases.each do |r, groups|
+      issues = groups.map { |_,g| g }.flatten
+      title = r ? r.name : "unassigned"
+
+      groups = groups.map do |t,g|
+        nc = g.count_of { |i| i.closed? }
+        pc = 100.0 * (g.empty? ? 1.0 : nc.to_f / g.size)
+        [t, g, nc, pc]
+      end
 
       special = if r && r.released?
         "(released)"
-      elsif bugs.empty? && feats.empty?
+      elsif groups.empty?
         "(no issues)"
-      elsif ncbugs == bugs.size && ncfeats == feats.size
+      elsif issues.all? { |i| i.closed? }
         "(ready for release)"
       else
-        bar
+        status_bar_for(issues)
       end
 
-      printf "%-10s %2d/%2d (%3.0f%%) bugs, %2d/%2d (%3.0f%%) features %s\n",
-        title, ncbugs, bugs.size, pcbugs, ncfeats, feats.size, pcfeats, special
+      middle = groups.map do |(t,g,nc,pc)|
+        "%2d/%2d (%3.0f%%) %s" % [nc, g.size, pc, t.to_s.pluralize(g.size, false)]
+      end.join(', ')
+      printf "%-10s %s %s\n", title, middle, special
     end
 
     if project.releases.empty?
@@ -230,13 +231,13 @@ EOS
   end
 
   def actually_do_todo project, config, releases, full
-    releases.each do |r, bugs, feats|
+    releases.each do |r, groups|
       if r
         puts "Version #{r.name} (#{r.status}):"
       else
         puts "Unassigned:"
       end
-      issues = bugs + feats
+      issues = groups.map { |_,g| g }.flatten
       issues = issues.select { |i| i.open? } unless full
       puts(todo_list_for(issues.sort_by { |i| i.sort_order }) || "No open issues.")
       puts
@@ -347,10 +348,9 @@ EOS
 
   operation :changelog, "Generate a changelog for a release", :release
   def changelog project, config, r
-    feats, bugs = project.issues_for_release(r).partition { |i| i.feature? }
     puts "== #{r.name} / #{r.released? ? r.release_time.pretty_date : 'unreleased'}"
-    feats.select { |f| f.closed? }.each { |i| puts "* #{i.title}" }
-    bugs.select { |f| f.closed? }.each { |i| puts "* bugfix: #{i.title}" }
+    project.group_issues(project.issues_for_release(r)).
+      each { |t,g| g.select { |i| i.closed? }.each { |i| puts "* #{t}: #{i.title}" } }
   end
 
   operation :html, "Generate html status pages", :dir
diff --git a/lib/util.rb b/lib/util.rb
index 7812a1c..03dba7e 100644
--- a/lib/util.rb
+++ b/lib/util.rb
@@ -19,6 +19,14 @@ module Enumerable
     each { |e| x = yield(e); return x if x }
     nil
   end
+
+  def group_by
+    inject({}) do |groups, element|
+      (groups[yield(element)] ||= []) << element
+      groups
+    end
+  end if RUBY_VERSION < '1.9'
+
 end
 
 class Array
-- 
1.5.5.rc3



More information about the ditz-talk mailing list