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:
parent
fd94f0e41a
commit
846d1edc74
@ -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
|
#Sat Apr 24 17:08:00 CDT 2010
|
||||||
build.dir=build
|
build.dir=build
|
||||||
src.dir=src
|
src.dir=src
|
||||||
lib.shared.dir=../shared-libs
|
lib.shared.dir=../shared-libs
|
||||||
test.dir=test
|
test.dir=test
|
||||||
build.number=8
|
build.number=10
|
||||||
version=3.1.0
|
version=3.2.0
|
||||||
name=libpit
|
name=libpit
|
||||||
lib.dir=lib
|
lib.dir=lib
|
||||||
lib.local=true
|
lib.local=true
|
||||||
|
@ -6,6 +6,7 @@ class Filter {
|
|||||||
List<Status> status = null
|
List<Status> status = null
|
||||||
List<String> projects = null
|
List<String> projects = null
|
||||||
List<String> ids = null
|
List<String> ids = null
|
||||||
|
Map<String, Object> extendedProperties = null
|
||||||
int priority = 9
|
int priority = 9
|
||||||
boolean acceptProjects = true
|
boolean acceptProjects = true
|
||||||
def issueSorter = defaultIssueSorter
|
def issueSorter = defaultIssueSorter
|
||||||
@ -15,10 +16,18 @@ class Filter {
|
|||||||
public static Closure defaultProjectSorter = { it.name }
|
public static Closure defaultProjectSorter = { it.name }
|
||||||
|
|
||||||
public boolean accept(Issue i) {
|
public boolean accept(Issue i) {
|
||||||
return (i.priority <= priority &&
|
return (
|
||||||
(!categories || categories.contains(i.category)) &&
|
// Needs to meet the priority threshold.
|
||||||
(!status || status.contains(i.status)) &&
|
i.priority <= priority &&
|
||||||
(!ids || ids.contains(i.id)))
|
// 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) {
|
public boolean accept(Project p) {
|
||||||
|
@ -2,7 +2,7 @@ package com.jdbernard.pit
|
|||||||
|
|
||||||
import java.lang.IllegalArgumentException as IAE
|
import java.lang.IllegalArgumentException as IAE
|
||||||
|
|
||||||
public abstract class Issue {
|
public class Issue {
|
||||||
|
|
||||||
protected String id
|
protected String id
|
||||||
protected Category category
|
protected Category category
|
||||||
@ -17,16 +17,15 @@ public abstract class Issue {
|
|||||||
this.id = props.id
|
this.id = props.id
|
||||||
this.category = props.category ?: Category.TASK
|
this.category = props.category ?: Category.TASK
|
||||||
this.status = props.status ?: Status.NEW
|
this.status = props.status ?: Status.NEW
|
||||||
this.priority = props.priority ?: 9
|
this.priority = props.priority ?: 5
|
||||||
this.title = props.title ?: ''
|
this.title = props.title ?: ''
|
||||||
this.text = props.text ?: ''
|
this.text = props.text ?: ''
|
||||||
|
|
||||||
|
// Put all the non-native properties into our extendedProperties map.
|
||||||
def nativeProps =
|
def nativeProps =
|
||||||
["id", "category", "status", "priority", "title", "text"]
|
["id", "category", "status", "priority", "title", "text"]
|
||||||
|
extendedProperties.putAll(props.findAll {
|
||||||
props.each { key, val ->
|
!nativeProps.contains(it.key) })}
|
||||||
if (nativeProps.contains(key)) { return }
|
|
||||||
this.extendedProperties[key] = val }}
|
|
||||||
|
|
||||||
public String getId() { return id; }
|
public String getId() { return id; }
|
||||||
|
|
||||||
|
@ -138,10 +138,10 @@ public class FileIssue extends Issue {
|
|||||||
result.append("=".multiply(issue.title.length()))
|
result.append("=".multiply(issue.title.length()))
|
||||||
result.append("\n\n")
|
result.append("\n\n")
|
||||||
result.append(issue.text)
|
result.append(issue.text)
|
||||||
result.append("\n----\n\n")
|
|
||||||
|
|
||||||
// If there are any extended properties, let's write those.
|
// If there are any extended properties, let's write those.
|
||||||
if (issue.extendedProperties.size() > 0) {
|
if (issue.extendedProperties.size() > 0) {
|
||||||
|
result.append("\n----\n\n")
|
||||||
def extOutput = [:]
|
def extOutput = [:]
|
||||||
def maxKeyLen = 0
|
def maxKeyLen = 0
|
||||||
def maxValLen = 0
|
def maxValLen = 0
|
||||||
@ -169,7 +169,9 @@ public class FileIssue extends Issue {
|
|||||||
result.append("=".multiply(maxKeyLen + 1))
|
result.append("=".multiply(maxKeyLen + 1))
|
||||||
result.append(" ")
|
result.append(" ")
|
||||||
result.append("=".multiply(maxValLen))
|
result.append("=".multiply(maxValLen))
|
||||||
result.append("\n") }}
|
result.append("\n") }
|
||||||
|
|
||||||
|
return result.toString()}
|
||||||
|
|
||||||
protected void writeFile() {
|
protected void writeFile() {
|
||||||
try { source.write(formatIssue(this)) }
|
try { source.write(formatIssue(this)) }
|
||||||
|
@ -23,56 +23,65 @@ class FileProject extends Project {
|
|||||||
child.isHidden()) return // just an issue folder
|
child.isHidden()) return // just an issue folder
|
||||||
|
|
||||||
// otherwise build and add to list
|
// otherwise build and add to list
|
||||||
projects[(child.name)] = new FileProject(child)
|
projects[(child.name)] = new FileProject(child) }
|
||||||
} else if (child.isFile() &&
|
else if (child.isFile() &&
|
||||||
FileIssue.isValidFilename(child.name)) {
|
FileIssue.isValidFilename(child.name)) {
|
||||||
def issue
|
def issue
|
||||||
|
|
||||||
// if exception, then not an issue
|
// if exception, then not an issue
|
||||||
try { issue = new FileIssue(child) } catch (all) { return }
|
try { issue = new FileIssue(child) } catch (all) { return }
|
||||||
|
|
||||||
issues[(issue.id)] = issue
|
issues[(issue.id)] = issue } }}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setName(String name) {
|
public void setName(String name) {
|
||||||
super.setName(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) {
|
public FileIssue createNewIssue(Map options) {
|
||||||
|
Issue issue
|
||||||
|
File issueFile
|
||||||
|
|
||||||
if (!options) options = [:]
|
if (!options) options = [:]
|
||||||
if (!options.category) options.category = Category.TASK
|
|
||||||
if (!options.status) options.status = Status.NEW
|
// We want some different defaults for issues due to the parser being
|
||||||
if (!options.priority) options.priority = 5
|
// unable to handle empty title or text.
|
||||||
if (!options.text) options.text = "Default issue title.\n" +
|
if (!options.title) options.title = "Default issue title."
|
||||||
"====================\n"
|
if (!options.text) options.text = "Describe the issue here."
|
||||||
String id
|
|
||||||
if (issues.size() == 0) id = '0000'
|
// 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 {
|
else {
|
||||||
id = (issues.values().max { it.id.toInteger() }).id
|
def lastId = (issues.values().max { it.id.toInteger() }).id
|
||||||
id = (id.toInteger() + 1).toString().padLeft(id.length(), '0')
|
options.id = (lastId.toInteger() + 1).toString().padLeft(
|
||||||
}
|
lastId.length(), '0') }
|
||||||
|
|
||||||
def issueFile = new File(source, FileIssue.makeFilename(id,
|
// Create an Issue object from the options (we will discard it later).
|
||||||
options.category, options.status, options.priority))
|
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.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
|
issues[(issue.id)] = issue
|
||||||
|
|
||||||
return issue
|
return issue }
|
||||||
}
|
|
||||||
|
|
||||||
public FileProject createNewProject(String name) {
|
public FileProject createNewProject(String name) {
|
||||||
def newDir = new File(source, name)
|
def newDir = new File(source, name)
|
||||||
newDir.mkdirs()
|
newDir.mkdirs()
|
||||||
|
|
||||||
return new FileProject(newDir)
|
return new FileProject(newDir) }
|
||||||
}
|
|
||||||
|
|
||||||
public boolean deleteIssue(Issue issue) {
|
public boolean deleteIssue(Issue issue) {
|
||||||
if (!issues[(issue.id)]) return false
|
if (!issues[(issue.id)]) return false
|
||||||
@ -81,8 +90,7 @@ class FileProject extends Project {
|
|||||||
if (issue instanceof FileIssue)
|
if (issue instanceof FileIssue)
|
||||||
return issue.deleteFile()
|
return issue.deleteFile()
|
||||||
|
|
||||||
else return true
|
else return true }
|
||||||
}
|
|
||||||
|
|
||||||
public boolean deleteProject(Project project) {
|
public boolean deleteProject(Project project) {
|
||||||
if (!projects[(project.name)]) return false
|
if (!projects[(project.name)]) return false
|
||||||
@ -91,8 +99,7 @@ class FileProject extends Project {
|
|||||||
if (project instanceof FileProject)
|
if (project instanceof FileProject)
|
||||||
return project.source.delete()
|
return project.source.delete()
|
||||||
|
|
||||||
return true
|
return true }
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() { return name }
|
public String toString() { return name }
|
||||||
|
Loading…
x
Reference in New Issue
Block a user