Filter and FileProject support for extended properties.

* Added support for extended properties to `Filter`. Unlike the basic
  properties, which allow the caller to specify a list of acceptable values, the
  extended properties only supports a map of properties, all of which must be
  available on the issue and match the given value.
* Made `Issue` a concrete class. There was no reason it could not be a concrete
  object. The main difference is that the extending classes all add some sort of
  persistence to the issue, but having an in-memory `Issue` in its most basic
  form can be useful and should be allowed.
* Changed the default priority for new issues from 9 to 5.
* Bug fix on `FileIssue.formatIssue()`. If was not properly returning its
  result leading to failure if no extended properties were given.
* Reworked `FileIssue.formatIssue()` to only print the horizontal rule
  seperating the issue text from the extended properties if there are extended
  properties to print as well.
* Changed the `FileProject.createNewIssue()` to handle extended properties and
  handle the basic properties in a manner more consistent with `Issue`. It is
  now creating an `Issue` object and using the `FileIssue.formatIssue()`
  function to write that to a new issue file, then loading that file back in as
  a `FileIssue`. This cuts down on code duplication and makes it so that the
  options map that `FileProject.createNewIssue()` expects is the same as the
  options to the `Issue` constructor.
This commit is contained in:
Jonathan Bernard 2011-12-08 14:38:24 -06:00
parent fd94f0e41a
commit 846d1edc74
5 changed files with 62 additions and 45 deletions

View File

@ -1,11 +1,11 @@
#Wed, 07 Dec 2011 17:53:14 -0600
#Thu, 08 Dec 2011 14:35:45 -0600
#Sat Apr 24 17:08:00 CDT 2010
build.dir=build
src.dir=src
lib.shared.dir=../shared-libs
test.dir=test
build.number=8
version=3.1.0
build.number=10
version=3.2.0
name=libpit
lib.dir=lib
lib.local=true

View File

@ -6,6 +6,7 @@ class Filter {
List<Status> status = null
List<String> projects = null
List<String> ids = null
Map<String, Object> extendedProperties = null
int priority = 9
boolean acceptProjects = true
def issueSorter = defaultIssueSorter
@ -15,10 +16,18 @@ class Filter {
public static Closure defaultProjectSorter = { it.name }
public boolean accept(Issue i) {
return (i.priority <= priority &&
(!categories || categories.contains(i.category)) &&
(!status || status.contains(i.status)) &&
(!ids || ids.contains(i.id)))
return (
// Needs to meet the priority threshold.
i.priority <= priority &&
// Needs to be in one of the filtered categories (if given)
(!categories || categories.contains(i.category)) &&
// Needs to have one of the filtered statuses (if given)
(!status || status.contains(i.status)) &&
// Needs to be one of the filtered ids (if given)
(!ids || ids.contains(i.id)) &&
// Needs to have all of the extended properties (if given)
(!extendedProperties ||
extendedProperties.every { name, value -> i[name] == value }))
}
public boolean accept(Project p) {

View File

@ -2,7 +2,7 @@ package com.jdbernard.pit
import java.lang.IllegalArgumentException as IAE
public abstract class Issue {
public class Issue {
protected String id
protected Category category
@ -17,16 +17,15 @@ public abstract class Issue {
this.id = props.id
this.category = props.category ?: Category.TASK
this.status = props.status ?: Status.NEW
this.priority = props.priority ?: 9
this.priority = props.priority ?: 5
this.title = props.title ?: ''
this.text = props.text ?: ''
// Put all the non-native properties into our extendedProperties map.
def nativeProps =
["id", "category", "status", "priority", "title", "text"]
props.each { key, val ->
if (nativeProps.contains(key)) { return }
this.extendedProperties[key] = val }}
extendedProperties.putAll(props.findAll {
!nativeProps.contains(it.key) })}
public String getId() { return id; }

View File

@ -138,10 +138,10 @@ public class FileIssue extends Issue {
result.append("=".multiply(issue.title.length()))
result.append("\n\n")
result.append(issue.text)
result.append("\n----\n\n")
// If there are any extended properties, let's write those.
if (issue.extendedProperties.size() > 0) {
result.append("\n----\n\n")
def extOutput = [:]
def maxKeyLen = 0
def maxValLen = 0
@ -169,7 +169,9 @@ public class FileIssue extends Issue {
result.append("=".multiply(maxKeyLen + 1))
result.append(" ")
result.append("=".multiply(maxValLen))
result.append("\n") }}
result.append("\n") }
return result.toString()}
protected void writeFile() {
try { source.write(formatIssue(this)) }

View File

@ -23,56 +23,65 @@ class FileProject extends Project {
child.isHidden()) return // just an issue folder
// otherwise build and add to list
projects[(child.name)] = new FileProject(child)
} else if (child.isFile() &&
projects[(child.name)] = new FileProject(child) }
else if (child.isFile() &&
FileIssue.isValidFilename(child.name)) {
def issue
// if exception, then not an issue
try { issue = new FileIssue(child) } catch (all) { return }
issues[(issue.id)] = issue
}
}
}
issues[(issue.id)] = issue } }}
public void setName(String name) {
super.setName(name)
source.renameTo(new File(source.canonicalFile.parentFile, name))
}
source.renameTo(new File(source.canonicalFile.parentFile, name)) }
public FileIssue createNewIssue(Map options) {
Issue issue
File issueFile
if (!options) options = [:]
if (!options.category) options.category = Category.TASK
if (!options.status) options.status = Status.NEW
if (!options.priority) options.priority = 5
if (!options.text) options.text = "Default issue title.\n" +
"====================\n"
String id
if (issues.size() == 0) id = '0000'
// We want some different defaults for issues due to the parser being
// unable to handle empty title or text.
if (!options.title) options.title = "Default issue title."
if (!options.text) options.text = "Describe the issue here."
// We are also going to find the next id based on the issues already in the
// project.
if (issues.size() == 0) options.id = '0000'
else {
id = (issues.values().max { it.id.toInteger() }).id
id = (id.toInteger() + 1).toString().padLeft(id.length(), '0')
}
def lastId = (issues.values().max { it.id.toInteger() }).id
options.id = (lastId.toInteger() + 1).toString().padLeft(
lastId.length(), '0') }
def issueFile = new File(source, FileIssue.makeFilename(id,
options.category, options.status, options.priority))
// Create an Issue object from the options (we will discard it later).
issue = new Issue(options)
// Create the filename and File object based on the options given.
issueFile = new File(source, FileIssue.makeFilename(
issue.id, issue.category, issue.status, issue.priority))
// Create the actual file on the system
issueFile.createNewFile()
issueFile.write(options.text)
def issue = new FileIssue(issueFile)
// Write the issue to the file created.
issueFile.write(FileIssue.formatIssue(issue))
// Read that new file back in as a FileIssue
issue = new FileIssue(issueFile)
// Add the issue to our collection.
issues[(issue.id)] = issue
return issue
}
return issue }
public FileProject createNewProject(String name) {
def newDir = new File(source, name)
newDir.mkdirs()
return new FileProject(newDir)
}
return new FileProject(newDir) }
public boolean deleteIssue(Issue issue) {
if (!issues[(issue.id)]) return false
@ -81,8 +90,7 @@ class FileProject extends Project {
if (issue instanceof FileIssue)
return issue.deleteFile()
else return true
}
else return true }
public boolean deleteProject(Project project) {
if (!projects[(project.name)]) return false
@ -91,8 +99,7 @@ class FileProject extends Project {
if (project instanceof FileProject)
return project.source.delete()
return true
}
return true }
@Override
public String toString() { return name }