### Eclipse Workspace Patch 1.0 #P ruby-metrics Index: src/main/java/hudson/plugins/rubyMetrics/railsNotes/RailsNotesBuildAction.java =================================================================== --- src/main/java/hudson/plugins/rubyMetrics/railsNotes/RailsNotesBuildAction.java (revision 0) +++ src/main/java/hudson/plugins/rubyMetrics/railsNotes/RailsNotesBuildAction.java (revision 0) @@ -0,0 +1,64 @@ +package hudson.plugins.rubyMetrics.railsNotes; + +import hudson.model.AbstractBuild; +import hudson.model.HealthReport; +import hudson.plugins.rubyMetrics.AbstractRubyMetricsBuildAction; +import hudson.plugins.rubyMetrics.railsNotes.model.RailsNotesMetrics; +import hudson.plugins.rubyMetrics.railsNotes.model.RailsNotesResults; +import hudson.util.ChartUtil; +import hudson.util.DataSetBuilder; +import hudson.util.ChartUtil.NumberOnlyBuildLabel; + +import java.util.Map; + +public class RailsNotesBuildAction extends AbstractRubyMetricsBuildAction { + private final RailsNotesResults results; + + public RailsNotesBuildAction(AbstractBuild owner, RailsNotesResults results) { + super(owner); + this.results = results; + } + + public HealthReport getBuildHealth() { + // TODO Auto-generated method stub + return null; + } + + public RailsNotesResults getResults() { + return results; + } + + public String getDisplayName() { + return "Annotations (Rails notes)"; + } + + public String getIconFileName() { + return "graph.gif"; + } + + public String getUrlName() { + return "railsNotes"; + } + + @Override + protected DataSetBuilder getDataSetBuilder() { + DataSetBuilder dsb = new DataSetBuilder(); + + Map total = results.getTotal(); + + for (RailsNotesBuildAction a = this; a != null; a = a.getPreviousResult()) { + ChartUtil.NumberOnlyBuildLabel label = new ChartUtil.NumberOnlyBuildLabel(a.owner); + + for (Map.Entry entry : total.entrySet()) { + dsb.add(entry.getValue(), entry.getKey().toString(), label); + } + } + + return dsb; + } + + @Override + protected String getRangeAxisLabel() { + return "Annotations"; + } +} Property changes on: src\main\java\hudson\plugins\rubyMetrics\railsNotes\RailsNotesBuildAction.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision Added: svn:eol-style + native Index: src/main/resources/hudson/plugins/rubyMetrics/railsNotes/RailsNotesProjectAction/floatingBox.jelly =================================================================== --- src/main/resources/hudson/plugins/rubyMetrics/railsNotes/RailsNotesProjectAction/floatingBox.jelly (revision 0) +++ src/main/resources/hudson/plugins/rubyMetrics/railsNotes/RailsNotesProjectAction/floatingBox.jelly (revision 0) @@ -0,0 +1,5 @@ + + + + \ No newline at end of file Index: src/main/java/hudson/plugins/rubyMetrics/railsNotes/RailsNotesParser.java =================================================================== --- src/main/java/hudson/plugins/rubyMetrics/railsNotes/RailsNotesParser.java (revision 0) +++ src/main/java/hudson/plugins/rubyMetrics/railsNotes/RailsNotesParser.java (revision 0) @@ -0,0 +1,61 @@ +package hudson.plugins.rubyMetrics.railsNotes; + +import hudson.plugins.rubyMetrics.railsNotes.model.RailsNotesMetrics; +import hudson.plugins.rubyMetrics.railsNotes.model.RailsNotesResults; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.regex.Pattern; + +import org.apache.commons.lang.StringUtils; +import org.codehaus.plexus.util.StringOutputStream; + +public class RailsNotesParser { + public RailsNotesResults parse(StringOutputStream output) { + return parse(output.toString()); + } + + public RailsNotesResults parse(String output) { + RailsNotesResults response = new RailsNotesResults(); + + String[] aux = output.split("[\n\r]"); + Collection lines = new LinkedHashSet(Arrays.asList(aux)); + lines = removeSeparators(lines); + + // Fortunately a LinkedHashSet has a predictable order, so this can use the filenames + Iterator linesIterator = lines.iterator(); + String lastFile = ""; + while (linesIterator.hasNext()) { + String line = linesIterator.next(); + if (StringUtils.isEmpty(line.trim())) continue; + // Filename lines don't start with a space. Annotation lines do. + if (line.charAt(0) != ' ') { + lastFile = line.substring(0, line.length() - 1); // remove colon from end of line + } else { + for (RailsNotesMetrics metric : RailsNotesMetrics.values()) { + // Match " * [line#] [ANNOTATION]" + Pattern metricPattern = Pattern.compile("^ \\* \\[[\\s\\d]+\\] \\[" + metric.toString() + "\\]"); + if (metricPattern.matcher(line).find()) { + response.addAnnotationFor(lastFile, metric); + break; // next line + } + } + } + } + + response.setOutput(output); + return response; + } + + private Collection removeSeparators(Collection lines) { + Collection response = new LinkedHashSet(); + for (String line : lines) { + response.add(line.replaceAll("[\\r\\n+-]+", "")); + } + + response.remove(""); + return response; + } +} \ No newline at end of file Property changes on: src\main\java\hudson\plugins\rubyMetrics\railsNotes\RailsNotesParser.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision Added: svn:eol-style + native Index: src/main/java/hudson/plugins/rubyMetrics/railsNotes/RailsNotesProjectAction.java =================================================================== --- src/main/java/hudson/plugins/rubyMetrics/railsNotes/RailsNotesProjectAction.java (revision 0) +++ src/main/java/hudson/plugins/rubyMetrics/railsNotes/RailsNotesProjectAction.java (revision 0) @@ -0,0 +1,44 @@ +package hudson.plugins.rubyMetrics.railsNotes; + +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import hudson.model.Result; +import hudson.plugins.rubyMetrics.AbstractRubyMetricsBuildAction; +import hudson.plugins.rubyMetrics.AbstractRubyMetricsProjectAction; +import hudson.plugins.rubyMetrics.railsStats.RailsStatsBuildAction; + +public class RailsNotesProjectAction extends AbstractRubyMetricsProjectAction { + public RailsNotesProjectAction(AbstractProject project) { + super(project); + } + + public String getDisplayName() { + return "Annotations report"; + } + + public String getUrlName() { + return "railsNotes"; + } + + public RailsNotesBuildAction getLastResult() { + for (AbstractBuild b = project.getLastStableBuild(); b != null; b = b.getPreviousNotFailedBuild()) { + if (b.getResult() == Result.FAILURE) + continue; + RailsNotesBuildAction r = b.getAction(RailsNotesBuildAction.class); + if (r != null) + return r; + } + return null; + } + + public Integer getLastResultBuild() { + for (AbstractBuild b = project.getLastStableBuild(); b != null; b = b.getPreviousNotFailedBuild()) { + if (b.getResult() == Result.FAILURE) + continue; + RailsNotesBuildAction r = b.getAction(RailsNotesBuildAction.class); + if (r != null) + return b.getNumber(); + } + return null; + } +} Property changes on: src\main\java\hudson\plugins\rubyMetrics\railsNotes\RailsNotesProjectAction.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision Added: svn:eol-style + native Index: src/main/java/hudson/plugins/rubyMetrics/railsNotes/RailsNotesPublisher.java =================================================================== --- src/main/java/hudson/plugins/rubyMetrics/railsNotes/RailsNotesPublisher.java (revision 0) +++ src/main/java/hudson/plugins/rubyMetrics/railsNotes/RailsNotesPublisher.java (revision 0) @@ -0,0 +1,123 @@ +package hudson.plugins.rubyMetrics.railsNotes; + +import hudson.Extension; +import hudson.FilePath; +import hudson.Launcher; +import hudson.model.AbstractBuild; +import hudson.model.AbstractProject; +import hudson.model.Action; +import hudson.model.BuildListener; +import hudson.model.StreamBuildListener; +import hudson.plugins.rake.Rake; +import hudson.plugins.rake.RubyInstallation; +import hudson.plugins.rubyMetrics.AbstractRubyMetricsPublisher; +import hudson.plugins.rubyMetrics.railsNotes.model.RailsNotesResults; +import hudson.tasks.BuildStepDescriptor; +import hudson.tasks.Publisher; + +import java.io.IOException; + +import org.codehaus.plexus.util.StringOutputStream; +import org.kohsuke.stapler.DataBoundConstructor; + +/** + * Rails notes {@link Publisher} + * + * @author Adam Stegman + */ +public class RailsNotesPublisher extends AbstractRubyMetricsPublisher { + private final Rake rake; + private final String rakeInstallation; + private final String rakeWorkingDir; + + @DataBoundConstructor + public RailsNotesPublisher(String rakeInstallation, String rakeWorkingDir) { + this.rakeInstallation = rakeInstallation; + this.rakeWorkingDir = rakeWorkingDir; + this.rake = new Rake(this.rakeInstallation, null, "notes", null, this.rakeWorkingDir, true); + } + + @Override + public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { + + FilePath workspace = build.getModuleRoot(); + + if (!isRailsProject(workspace)) { + return fail(build, listener, "This is not a rails app directory: " + workspace.getName()); + } + + listener.getLogger().println("Publishing rails notes report..."); + + StringOutputStream out = new StringOutputStream(); + BuildListener stringListener = new StreamBuildListener(out); + + if (rake.perform(build, launcher, stringListener)) { + final RailsNotesParser parser = new RailsNotesParser(); + RailsNotesResults results = parser.parse(out); + + RailsNotesBuildAction action = new RailsNotesBuildAction(build, results); + build.getActions().add(action); + } + + return true; + } + + public String getRakeInstallation() { + return rakeInstallation; + } + + public String getRakeWorkingDir() { + return rakeWorkingDir; + } + + private boolean isRailsProject(FilePath workspace) { + try { //relaxed rails app schema + return workspace.isDirectory() + && workspace.list("app") != null && workspace.list("config") != null + && workspace.list("db") != null && workspace.list("test") != null; + } catch (Exception e) { + return false; + } + } + + + @Override + public Action getProjectAction(AbstractProject project) { + return new RailsNotesProjectAction(project); + } + + @Extension + public static final DescriptorImpl DESCRIPTOR = new DescriptorImpl(); + + public static final class DescriptorImpl extends BuildStepDescriptor { + + protected DescriptorImpl() { + super(RailsNotesPublisher.class); + } + + @Override + public String getHelpFile() { + return "/plugin/rubyMetrics/railsNotesHelp.html"; + } + + @Override + public String getDisplayName() { + return "Publish Rails Notes report"; + } + + public RubyInstallation[] getRakeInstallations() { + return Rake.DESCRIPTOR.getInstallations(); + } + + @Override + public boolean isApplicable(Class jobType) { + return true; + } + + } + + @Override + public BuildStepDescriptor getDescriptor() { + return DESCRIPTOR; + } +} \ No newline at end of file Property changes on: src\main\java\hudson\plugins\rubyMetrics\railsNotes\RailsNotesPublisher.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision Added: svn:eol-style + native Index: src/main/java/hudson/plugins/rubyMetrics/railsNotes/model/RailsNotesResults.java =================================================================== --- src/main/java/hudson/plugins/rubyMetrics/railsNotes/model/RailsNotesResults.java (revision 0) +++ src/main/java/hudson/plugins/rubyMetrics/railsNotes/model/RailsNotesResults.java (revision 0) @@ -0,0 +1,120 @@ +package hudson.plugins.rubyMetrics.railsNotes.model; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.TreeMap; + +public class RailsNotesResults { + private Map> metrics = new HashMap>(); + private List sortedLabels = new ArrayList(); + + private class SortLabelsComparator implements Comparator { + + private final List labels; + public SortLabelsComparator(List coll) { + labels = coll; + } + + public int compare(String o1, String o2) { + return new Integer(labels.indexOf(o1)).compareTo(labels.indexOf(o2)); + } + + } + + private String output; + + public Collection getHeaders() { + Collection headers = new LinkedHashSet(); + headers.add("Filename"); + + for (RailsNotesMetrics metric : RailsNotesMetrics.values()) { + headers.add(metric.toString()); + } + + return headers; + } + + /** + * Add an instance of annotation in the file fileName. This will + * create an entry in metrics for fileName if one does not already + * exist. + * + * @param fileName the filename from the output of Rails notes. + * @param annotation the RailsNotesMetrics value associated with the annotation + * in the report. + */ + public void addAnnotationFor(String fileName, RailsNotesMetrics annotation) { + if (!sortedLabels.contains(fileName)) { + sortedLabels.add(fileName); + } + + // Create the map if it doesn't already exist, seed each annotation with a count of 0. + if (!metrics.containsKey(fileName)) { + Map metric = new TreeMap(); + for (RailsNotesMetrics value : RailsNotesMetrics.values()) { + metric.put(value, new Integer(0)); + } + metrics.put(fileName, metric); + } + // Get this file's map and add 1 to the count of that metric. + Map metric = metrics.get(fileName); + metric.put(annotation, new Integer(metric.get(annotation).intValue() + 1)); + } + + /** + * Generate the total count of each annotation by adding together all the annotations in + * metrics. + * + * @return A Map to insert into metrics with each annotation and a + * total count. + */ + public Map getTotal() { + Map total = new TreeMap(); + // Initialize all the values + for (RailsNotesMetrics metric : RailsNotesMetrics.values()) total.put(metric, new Integer(0)); + + // For each class entered, add its counts to the total + for (Map fileEntry : metrics.values()) { + for (RailsNotesMetrics metric : fileEntry.keySet()) { + total.put(metric, new Integer(total.get(metric).intValue() + fileEntry.get(metric).intValue())); + } + } + + return total; + } + + public Map> getMetrics() { + Comparator comparator = new SortLabelsComparator(sortedLabels); + + Map> response = + new TreeMap>(comparator); + + for (Map.Entry> entry : metrics.entrySet()) { + response.put(entry.getKey(), entry.getValue()); + } + + // Include the total + response.put("Total", getTotal()); + + return response; + } + + /** + * @return the output of the "rake notes" command. + */ + public String getOutput() { + return output; + } + + /** + * @param output the output of the "rake notes" command. + */ + public void setOutput(String output) { + this.output = output; + } +} \ No newline at end of file Property changes on: src\main\java\hudson\plugins\rubyMetrics\railsNotes\model\RailsNotesResults.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision Added: svn:eol-style + native Index: src/test/java/hudson/plugins/rubyMetrics/railsNotes/RailsNotesParserTest.java =================================================================== --- src/test/java/hudson/plugins/rubyMetrics/railsNotes/RailsNotesParserTest.java (revision 0) +++ src/test/java/hudson/plugins/rubyMetrics/railsNotes/RailsNotesParserTest.java (revision 0) @@ -0,0 +1,34 @@ +package hudson.plugins.rubyMetrics.railsNotes; + +import hudson.plugins.rubyMetrics.railsNotes.model.RailsNotesResults; +import junit.framework.TestCase; + +public class RailsNotesParserTest extends TestCase { + public void testParse() throws Exception { + RailsNotesParser parser = new RailsNotesParser(); + + String out = "app/controllers/a_controller.rb:\n" + + " * [ 53] [TODO] do this\n" + + "\n" + + "app/models/b model.rb:\n" + + " * [ 1] [FIXME] [TODO]\n" + // should end up as a FIXME + "\n" + + "app/models/c_model.rb:\n" + + " * [111] [OPTIMIZE]\n" + + " * [222] [TODO]\n" + + "\n" + + "test/unit/b test.rb:\n" + + " * [ 2] [TODO]\n" + + "\n" + + "test/unit/c_test.rb:\n" + + " * [ 5] [FIXME]\n" + + "\n" + + "\n"; + + RailsNotesResults metrics = parser.parse(out); + + assertFalse(metrics.getMetrics().isEmpty()); + assertNotNull(metrics.getOutput()); + assertFalse(metrics.getOutput() == ""); + } +} \ No newline at end of file Property changes on: src\test\java\hudson\plugins\rubyMetrics\railsNotes\RailsNotesParserTest.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision Added: svn:eol-style + native Index: src/main/webapp/railsNotesHelp.html =================================================================== --- src/main/webapp/railsNotesHelp.html (revision 0) +++ src/main/webapp/railsNotesHelp.html (revision 0) @@ -0,0 +1 @@ +Run Rake notes task and build a nice report with a trend graph. You have to select the rake version you want to use. \ No newline at end of file Property changes on: src\main\webapp\railsNotesHelp.html ___________________________________________________________________ Added: svn:mime-type + text/html Added: svn:keywords + Author Date Id Revision Added: svn:eol-style + native Index: src/main/resources/hudson/plugins/rubyMetrics/railsNotes/RailsNotesPublisher/config.jelly =================================================================== --- src/main/resources/hudson/plugins/rubyMetrics/railsNotes/RailsNotesPublisher/config.jelly (revision 0) +++ src/main/resources/hudson/plugins/rubyMetrics/railsNotes/RailsNotesPublisher/config.jelly (revision 0) @@ -0,0 +1,19 @@ + + + + + + + + + + + + + \ No newline at end of file Index: src/main/java/hudson/plugins/rubyMetrics/railsNotes/model/RailsNotesMetrics.java =================================================================== --- src/main/java/hudson/plugins/rubyMetrics/railsNotes/model/RailsNotesMetrics.java (revision 0) +++ src/main/java/hudson/plugins/rubyMetrics/railsNotes/model/RailsNotesMetrics.java (revision 0) @@ -0,0 +1,27 @@ +package hudson.plugins.rubyMetrics.railsNotes.model; + +import java.util.Arrays; +import java.util.Comparator; + +public enum RailsNotesMetrics { + TODO, FIXME, OPTIMIZE; + + public static RailsNotesMetrics toRailsNotesMetrics(String name) { + try { + return RailsNotesMetrics.valueOf(name.toUpperCase()); + } catch (Exception e) { + return null; + } + } + + public int getOrder() { + return Arrays.asList(RailsNotesMetrics.values()).indexOf(this); + } + + public static class COMPARATOR implements Comparator { + public int compare(RailsNotesMetrics o1, RailsNotesMetrics o2) { + return new Integer(o1.getOrder()).compareTo(new Integer(o2.getOrder())); + } + + } +} \ No newline at end of file Property changes on: src\main\java\hudson\plugins\rubyMetrics\railsNotes\model\RailsNotesMetrics.java ___________________________________________________________________ Added: svn:keywords + Author Date Id Revision Added: svn:eol-style + native Index: src/main/resources/hudson/plugins/rubyMetrics/railsNotes/RailsNotesProjectAction/nodata.jelly =================================================================== --- src/main/resources/hudson/plugins/rubyMetrics/railsNotes/RailsNotesProjectAction/nodata.jelly (revision 0) +++ src/main/resources/hudson/plugins/rubyMetrics/railsNotes/RailsNotesProjectAction/nodata.jelly (revision 0) @@ -0,0 +1,9 @@ + + + + +

No annotation data available

+
+
+
Property changes on: . ___________________________________________________________________ Modified: svn:ignore - .classpath .project .settings target rubyMetrics.iml + *.patch .classpath .project .settings bin rubyMetrics.iml target work Index: src/main/resources/hudson/plugins/rubyMetrics/railsNotes/RailsNotesBuildAction/index.jelly =================================================================== --- src/main/resources/hudson/plugins/rubyMetrics/railsNotes/RailsNotesBuildAction/index.jelly (revision 0) +++ src/main/resources/hudson/plugins/rubyMetrics/railsNotes/RailsNotesBuildAction/index.jelly (revision 0) @@ -0,0 +1,40 @@ + + + + + + +

Annotations (Rails notes) report

+ + + + + + + + + + + + + + + + + + + + + + + +
${header}
${metric.key}${values.value}
+ +

Output

+
+
${it.results.output}
+
+
+
+
\ No newline at end of file