diff --git a/issues/libpit/0002c6.rst b/issues/libpit/0002c6.rst
index be412bc..1c87fc8 100644
--- a/issues/libpit/0002c6.rst
+++ b/issues/libpit/0002c6.rst
@@ -1,5 +1,5 @@
-Add the ability to change an issue's status
-===========================================
+Add the ability to change an issue's category
+=============================================
The -l/--list options lists issues, add a set of options to reclassify
an issue from one category to another. The most common is CLOSED, but
diff --git a/issues/libpit/0003c6.rst b/issues/libpit/0003c6.rst
index c740c7a..155a3a1 100644
--- a/issues/libpit/0003c6.rst
+++ b/issues/libpit/0003c6.rst
@@ -1,4 +1,2 @@
-Add the ability to re-prioritize a set of issues
-================================================
-
-Add a mode that changes the priority of a selection of issues.
+Add the ability to re-prioritize an issue.
+==========================================
diff --git a/issues/libpit/0004f7.rst b/issues/libpit/0004c7.rst
similarity index 72%
rename from issues/libpit/0004f7.rst
rename to issues/libpit/0004c7.rst
index 21e9f8f..f736655 100644
--- a/issues/libpit/0004f7.rst
+++ b/issues/libpit/0004c7.rst
@@ -1,2 +1,4 @@
Add the ability to enter new issues
===================================
+
+Implemented on 2010/02/17
diff --git a/issues/libpit/0005t1.rst b/issues/libpit/0005t5.rst
similarity index 100%
rename from issues/libpit/0005t1.rst
rename to issues/libpit/0005t5.rst
diff --git a/issues/libpit/0006t1.rst b/issues/libpit/0006t5.rst
similarity index 100%
rename from issues/libpit/0006t1.rst
rename to issues/libpit/0006t5.rst
diff --git a/issues/libpit/0007t1.rst b/issues/libpit/0007t5.rst
similarity index 100%
rename from issues/libpit/0007t1.rst
rename to issues/libpit/0007t5.rst
diff --git a/issues/libpit/0008t1.rst b/issues/libpit/0008t5.rst
similarity index 100%
rename from issues/libpit/0008t1.rst
rename to issues/libpit/0008t5.rst
diff --git a/issues/libpit/0009t1.rst b/issues/libpit/0009t5.rst
similarity index 100%
rename from issues/libpit/0009t1.rst
rename to issues/libpit/0009t5.rst
diff --git a/issues/libpit/0010t1.rst b/issues/libpit/0010t5.rst
similarity index 100%
rename from issues/libpit/0010t1.rst
rename to issues/libpit/0010t5.rst
diff --git a/issues/libpit/0011t2.rst b/issues/libpit/0011t6.rst
similarity index 100%
rename from issues/libpit/0011t2.rst
rename to issues/libpit/0011t6.rst
diff --git a/issues/libpit/0012t1.rst b/issues/libpit/0012c1.rst
similarity index 63%
rename from issues/libpit/0012t1.rst
rename to issues/libpit/0012c1.rst
index 58cf3b7..bc9da41 100644
--- a/issues/libpit/0012t1.rst
+++ b/issues/libpit/0012c1.rst
@@ -1,2 +1,4 @@
Add unit tests for changing an issue's category.
================================================
+
+Test defined in test/com/jdbernard/pit/IssueTest.groovy
diff --git a/issues/libpit/0013t1.rst b/issues/libpit/0013c1.rst
similarity index 62%
rename from issues/libpit/0013t1.rst
rename to issues/libpit/0013c1.rst
index bdf375c..85cf118 100644
--- a/issues/libpit/0013t1.rst
+++ b/issues/libpit/0013c1.rst
@@ -1,2 +1,5 @@
Add unit tests for changing an issue's priority.
================================================
+
+
+Test defined in test/com/jdbernard/pit/IssueTest.groovy
diff --git a/issues/libpit/0014t1.rst b/issues/libpit/0014c1.rst
similarity index 61%
rename from issues/libpit/0014t1.rst
rename to issues/libpit/0014c1.rst
index d904a7e..1891b0e 100644
--- a/issues/libpit/0014t1.rst
+++ b/issues/libpit/0014c1.rst
@@ -1,3 +1,4 @@
Add unit tests covering Issue construction.
===========================================
+Test defined in test/com/jdbernard/pit/IssueTest.groovy
diff --git a/issues/libpit/0015t1.rst b/issues/libpit/0015t5.rst
similarity index 100%
rename from issues/libpit/0015t1.rst
rename to issues/libpit/0015t5.rst
diff --git a/issues/pit-cli/0003c5.rst b/issues/pit-cli/0003c5.rst
new file mode 100644
index 0000000..5bdd8d9
--- /dev/null
+++ b/issues/pit-cli/0003c5.rst
@@ -0,0 +1,2 @@
+Add the ability to change an Issue's priority.
+==============================================
diff --git a/issues/pit-cli/0004c5.rst b/issues/pit-cli/0004c5.rst
new file mode 100644
index 0000000..91c265e
--- /dev/null
+++ b/issues/pit-cli/0004c5.rst
@@ -0,0 +1,2 @@
+Add the ability to change an Issue's category.
+==============================================
diff --git a/issues/pit-cli/0005f4.rst b/issues/pit-cli/0005f4.rst
new file mode 100644
index 0000000..564c4e7
--- /dev/null
+++ b/issues/pit-cli/0005f4.rst
@@ -0,0 +1,2 @@
+Add the ability to create a new issue.
+======================================
diff --git a/libpit/build.xml b/libpit/build.xml
index d6077a0..c40f8a2 100644
--- a/libpit/build.xml
+++ b/libpit/build.xml
@@ -14,6 +14,12 @@
+
+
+
+
+
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
ids = null
int priority = 9
boolean acceptProjects = true
- Closure projectSorter
- Closure issueSorter
+ Closure projectSorter = defaultIssueSorter
+ Closure issueSorter = defaultProjectSorter
+
+ public static Closure defaultIssueSorter = { it.id.toInteger() }
+ public static Closure defaultProjectSorter = { it.name }
public boolean accept(Issue i) {
return (i.priority <= priority &&
diff --git a/libpit/src/com/jdbernard/pit/Issue.groovy b/libpit/src/com/jdbernard/pit/Issue.groovy
index 5c0ef4e..8d21d9f 100644
--- a/libpit/src/com/jdbernard/pit/Issue.groovy
+++ b/libpit/src/com/jdbernard/pit/Issue.groovy
@@ -26,16 +26,22 @@ public class Issue {
void setCategory(Category c) {
this.category = c
- source.renameTo(getFilename())
+ source.renameTo(new File(source.canonicalFile.parentFile, getFilename()))
}
void setPriority(int p) {
if (p < 0) priority = 0
else if (p > 9) priority = 9
else priority = p
- source.renameTo(getFilename())
+ source.renameTo(new File(source.canonicalFile.parentFile, getFilename()))
}
- String getFilename() { return id + category.symbol + priority + ".rst"; }
+ String getFilename() { return makeFilename(id, category, priority) }
+ static String makeFilename(String id, Category category, int priority) {
+ return id + category.symbol + priority + ".rst";
+ }
+
+ @Override
+ String toString() { return "${id}(${priority}): ${category} ${title}" }
}
diff --git a/libpit/src/com/jdbernard/pit/Project.groovy b/libpit/src/com/jdbernard/pit/Project.groovy
index 37fb7c1..9565790 100644
--- a/libpit/src/com/jdbernard/pit/Project.groovy
+++ b/libpit/src/com/jdbernard/pit/Project.groovy
@@ -38,16 +38,26 @@ class Project {
}
}
- public void rename(String newName) { source.renameTo(newName) }
+ public void rename(String newName) {
+ this.name = newName
+ source.renameTo(new File(source.canonicalFile.parentFile, newName))
+ }
+
+ public void setName(String name) { rename(name) }
- public void eachIssue(Closure c) {
- for (i in issues.values()) c.call(i)
- for (p in projects.values()) p.eachIssue(c)
+ public void eachIssue(Filter filter = null, Closure c) {
+ def sorter = filter?.issueSorter ?: Filter.defaultIssueSorter
+ for (i in issues.values().sort(sorter)) c.call(i)
+ }
+
+ public void eachProject(Filter filter = null, Closure c) {
+ def sorter = filter?.projectSorter ?: Filter.defaultProjectSorter
+ for (p in projects.values().sort(sorter)) c.call(p)
}
public void each(Filter filter = null, Closure c) {
- def is = filter?.issueSorter ?: { it.id.toInteger() }
- def ps = filter?.projectSorter ?: { it.name }
+ def is = filter?.issueSorter ?: Filter.defaultIssueSorter
+ def ps = filter?.projectSorter ?: Filter.defaultProjectSorter
for (issue in issues.values().sort(is)) {
if (filter && !filter.accept(issue))
@@ -61,23 +71,25 @@ class Project {
return
c.call(project)
- project.each(c)
}
}
- public void list(Map options = [:]) {
- if (!options.offset) options.offset = ""
- if (!options.verbose) options.verbose = false
+ public Issue createNewIssue(Map options) {
+ if (!options.category) options.category = Category.TASK
+ if (!options.priority) options.priority = 5
+ if (!options.text) options.text = "Default issue title.\n" +
+ "====================\n"
+ String id = (issues.values().max { it.id.toInteger() }).id
- each(options.filter) {
- if (it instanceof Project) {
- println "\n${options.offset}${it.name}"
- println "${options.offset}${'-'.multiply(it.name.length())}"
- } else {
- println "${options.offset}${it.id}(${it.priority}): " +
- "${it.category} ${it.title}"
- if (options.verbose) println "\n${it.text}"
- }
- }
+ def issueFile = new File(source, Issue.makeFilename(id, options.category, options.priority))
+ assert !issueFile.exists()
+ issueFile.createNewFile()
+ issueFile.write(options.text)
+
+ return new Issue(issueFile)
}
+
+ @Override
+ String toString() { return name }
+
}
diff --git a/libpit/test/com/jdbernard/pit/FilterTest.groovy b/libpit/test/com/jdbernard/pit/FilterTest.groovy
new file mode 100644
index 0000000..cf9c89e
--- /dev/null
+++ b/libpit/test/com/jdbernard/pit/FilterTest.groovy
@@ -0,0 +1,12 @@
+package com.jdbernard.pit
+
+import org.junit.Test
+import static org.junit.Assert.assertTrue
+
+class FilterTest {
+
+ @Test void emptyTest() {
+
+ }
+
+}
diff --git a/libpit/test/com/jdbernard/pit/IssueTest.groovy b/libpit/test/com/jdbernard/pit/IssueTest.groovy
index 4a45720..2221e11 100644
--- a/libpit/test/com/jdbernard/pit/IssueTest.groovy
+++ b/libpit/test/com/jdbernard/pit/IssueTest.groovy
@@ -1,6 +1,10 @@
package com.jdbernard.pit
-class IssueTest
+import org.junit.*
+import static org.junit.Assert.assertTrue
+import static org.junit.Assert.assertFalse
+
+class IssueTest {
def issues
File testDir
@@ -23,43 +27,60 @@ class IssueTest
issueFile.write(
"Obtain donuts.\n" +
"==============\n\n" +
- "The office is seriously lacking in sugary donuts.\n\n
+ "The office is seriously lacking in sugary donuts.\n\n" +
"We must rectify this at once!")
issues << new Issue(issueFile)
}
- @AfterClass void deleteIssueFiles() {
+ @After void deleteIssueFiles() {
testDir.deleteDir()
}
@Test void testSetCategory() {
- assert issues[0].category == Category.FEATURE
- assert issues[1].category == Category.TASK
+ assertTrue issues[0].category == Category.FEATURE
+ assertTrue issues[1].category == Category.TASK
- issues[0].category == Category.CLOSED
- issues[1].category == Category.TASK
+ issues[0].category = Category.CLOSED
+ issues[1].category = Category.BUG
- assert issues[0].category == Category.CLOSED
- assert issues[1].category == Category.BUG
+ assertTrue issues[0].category == Category.CLOSED
+ assertTrue issues[1].category == Category.BUG
- assert new File(testDir, '0001c1.rst').exists()
- assert new File(testDir, '0002b5.rst').exists()
+ assertTrue new File(testDir, '0001c1.rst').exists()
+ assertTrue new File(testDir, '0002b5.rst').exists()
assertFalse new File(testDir, '0001f1.rst').exists()
assertFalse new File(testDir, '0002t5.rst').exists()
}
- @Test void testIssueConstructor() {
+ @Test void testSetPriority() {
+
+ assertTrue issues[0].priority == 1
+ assertTrue issues[1].priority == 5
+
+ issues[0].priority = 2
+ issues[1].priority = 9
+
+ assertTrue issues[0].priority == 2
+ assertTrue issues[1].priority == 9
+
+ assertTrue new File(testDir, '0001f2.rst').exists()
+ assertTrue new File(testDir, '0002t9.rst').exists()
+ assertFalse new File(testDir, '0001f1.rst').exists()
+ assertFalse new File(testDir, '0002t5.rst').exists()
+ }
+
+ @Test void testConstruction() {
File issueFile = new File(testDir, '0001f1.rst')
Issue issue = new Issue(issueFile)
- assert issue.id == "0001"
- assert issue.category == Category.FEATURE
- assert issue.priority == 1
- assert issue.title == "Add the killer feature to the killer app."
- assert issue.text == "Add the killer feature to the killer app.\n" +
+ assertTrue issue.id == "0001"
+ assertTrue issue.category == Category.FEATURE
+ assertTrue issue.priority == 1
+ assertTrue issue.title == "Add the killer feature to the killer app."
+ assertTrue issue.text == "Add the killer feature to the killer app.\n" +
"=========================================\n\n" +
"Make our killer app shine!."
- assert issue.source == issueFile
+ assertTrue issue.source == issueFile
}
}
diff --git a/libpit/test/com/jdbernard/pit/ProjectTest.groovy b/libpit/test/com/jdbernard/pit/ProjectTest.groovy
new file mode 100644
index 0000000..044c474
--- /dev/null
+++ b/libpit/test/com/jdbernard/pit/ProjectTest.groovy
@@ -0,0 +1,115 @@
+package com.jdbernard.pit
+
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import static org.junit.Assert.assertTrue
+import static org.junit.Assert.assertFalse
+
+class ProjectTest {
+
+ File testDir
+ Project rootProj
+
+ @Before void createTestProjects() {
+ testDir = new File('testdir')
+ testDir.mkdirs()
+
+ def issueFile = new File(testDir, '0001t5.rst')
+ issueFile.createNewFile()
+ issueFile.write('Test Issue 1\n' +
+ '============\n\n' +
+ 'This is the first test issue.')
+
+ issueFile = new File(testDir, '0002b5.rst')
+ issueFile.createNewFile()
+ issueFile.write('Test Bug\n' +
+ '========\n\n' +
+ 'Yeah, it is a test bug.')
+
+ issueFile = new File(testDir, '0003f2.rst')
+ issueFile.createNewFile()
+ issueFile.write('Important Feature Request\n' +
+ '=========================\n\n' +
+ 'Here is our sweet feature. Please implement it!')
+
+ def subDir = new File(testDir, 'subproj1')
+ subDir.mkdirs()
+
+ issueFile = new File(subDir, '0001f3.rst')
+ issueFile.createNewFile()
+ issueFile.write('First feature in subproject\n' +
+ '===========================\n\n' +
+ 'Please make the grubblers grobble.')
+
+ issueFile = new File(subDir, '0002b4.rst')
+ issueFile.createNewFile()
+ issueFile.write('Zippners are not zippning.\n' +
+ '==========================\n\n' +
+ 'For some reason, the Zippners are bilperring, not zippning.')
+
+ rootProj = new Project(testDir)
+ }
+
+ @After void deleteTestProjects() {
+ testDir.delete()
+ }
+
+ @Test void testConstruction() {
+ Project proj = new Project(testDir, null)
+
+ assertTrue proj.name == 'testdir'
+ assertTrue proj.issues.size() == 3
+ assertTrue proj.projects.size() == 1
+
+ // Issue construction in general is under test in IssueTest
+ // just check that the issues actually exists
+ assertTrue proj.issues['0001'].id == '0001'
+ assertTrue proj.issues['0001'].title == 'Test Issue 1'
+
+ assertTrue proj.issues['0002'].id == '0002'
+ assertTrue proj.issues['0002'].title == 'Test Bug'
+
+ assertTrue proj.issues['0003'].id == '0003'
+ assertTrue proj.issues['0003'].title == 'Important Feature Request'
+
+ // check sub-project behaviour
+ assertTrue proj.projects.subproj1 != null
+ assertTrue proj.projects.subproj1.name == 'subproj1'
+ assertTrue proj.projects.subproj1.issues.size() == 2
+ assertTrue proj.projects.subproj1.projects.size() == 0
+ assertTrue proj.projects.subproj1.issues['0001'].id == '0001'
+ assertTrue proj.projects.subproj1.issues['0001'].title == 'First feature in subproject'
+ assertTrue proj.projects.subproj1.issues['0002'].id == '0002'
+ assertTrue proj.projects.subproj1.issues['0002'].title == 'Zippners are not zippning.'
+ }
+
+ @Test void testRename() {
+ assert rootProj.name == 'testdir'
+
+ rootProj.rename('renamedTestDir')
+
+ assertTrue rootProj.name == 'renamedTestDir'
+ assertTrue new File('renamedTestDir').exists()
+ }
+
+ /*@Test void testEachIssue() {
+ def expectedList = [rootProj.issues['0001'],
+ rootProj.issues['0002'], rootProj.issues['0003']]
+
+ // sort using default ordering (ids ascending)
+ def actualList = []
+ rootProj.eachIssue { actualList << it }
+
+ assertArrayEquals expectedList, actualList
+
+ // sort using reverse ordering (ids descending)
+ expectedList = expectedList.reverse()
+ actualList = []
+
+ rootProj.eachIssue(
+ new Filter(issueSorter: { -(it.id.toInteger()) }))
+ { actualList << it }
+ }*/
+
+}
diff --git a/pit-cli/build.xml b/pit-cli/build.xml
index 9f669c6..02aa1de 100644
--- a/pit-cli/build.xml
+++ b/pit-cli/build.xml
@@ -9,13 +9,29 @@
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -80,7 +96,7 @@
-
+
+ println "${offset}${issue}"
+ if (opts.v) {
+ println ""
+ issue.text.eachLine { println "${offset} ${it}" }
+ println ""
+ }
+ }
+
+ def projectPrinter
+ projectPrinter = { project, offset ->
+ println "\n${offset}${project.name}"
+ println "${offset}${'-'.multiply(project.name.length())}"
+ project.eachIssue { issuePrinter.call(it, offset) }
+ project.eachProject { projectPrinter.call(it, offset + " ") }
+ }
+
+ issuedb.eachIssue { issuePrinter.call(it, "") }
+ issuedb.eachProject { projectPrinter.call(it, "") }
+
+}
+
+// change priority second
+else if (opts.P) {
+ def priority
+ try { priority = max(0, min(9, opts.P.toInteger())) }
+ catch (e) { println "Invalid priority: ${opts.P}"; return 1 }
+
+ walkProject(issuedb) { it.priority = priority }
+}
+// change category third
+else if (opts.C) {
+ def cat
+ try { cat = Category.toCategory(opts.C) }
+ catch (e) { println "Invalid category: ${opts.C}"; return 1 }
+
+ walkProject(issuedb) { it.category = cat }
+}
+// new entry last
+else if (opts.n) {
+ def cat, priority
+ String text = ""
+ Issue ussie
+ def sin = System.in.newReader()
+
+ while(true) {
+ try {
+ print "Category (bug, feature, task, closed): "
+ cat = Category.toCategory(sin.readLine())
+ break
+ } catch (e) {
+ println "Invalid category: " + e.getLocalizedMessage()
+ println "Valid options are: \n${Category.values().join(', ')}\n " +
+ "(abbreviations are accepted.)"
+ }
+ }
+
+ while (true) {
+ try {
+ print "Priority (0-9): "
+ priority = max(0, min(9, sin.readLine().toInteger()))
+ break
+ } catch (e) { println "Not a valid value." }
+ }
+
+ println "Enter issue (use EOF of ^D to end): "
+ try {
+ sin.eachLine { line ->
+ def m = line =~ /(.*)EOF.*/
+ if (m) {
+ text << m[0][1]
+ sin.close()
+ } else text << line
+ }
+ } catch (e) {}
+
+ issue = issuedb.createNewIssue(category: cat, priority: priority, text: text)
+
+ println "New issue created: "
+ println issue
+}
+
+def walkProject(Project p, Closure c) {
+ p.eachIssue(c)
+ p.eachProject { walkProject(it, c) }
+}
diff --git a/pit-cli/src/com/jdbernard/pit/PersonalIssurTrackerCLI.groovy b/pit-cli/src/com/jdbernard/pit/PersonalIssurTrackerCLI.groovy
deleted file mode 100644
index 8dce79b..0000000
--- a/pit-cli/src/com/jdbernard/pit/PersonalIssurTrackerCLI.groovy
+++ /dev/null
@@ -1,51 +0,0 @@
-package com.jdbernard.pit
-
-def cli = new CliBuilder(usage: '')
-cli.h(longOpt: 'help', 'Show help information.')
-cli.v(longOpt: 'verbose', 'Show verbose task information')
-cli.l(longOpt: 'list', 'List issues. Unless otherwise specified it lists all '
- + 'sub projects and all unclosed issue categories.')
-cli.i(argName: 'id', longOpt: 'id', args: 1,
- 'Filter issues by id. Accepts a comma-delimited list.')
-cli.c(argName: 'category', longOpt: 'category', args: 1,
- 'Filter issues by category (bug, feature, task, closed). Accepts a '
- + 'comma-delimited list.')
-cli.p(argName: 'priority', longOpt: 'priority', args: 1,
- 'Filter issues by priority. This acts as a threshhold, listing all issues '
- + 'greater than or equal to the given priority.')
-cli.r(argName: 'project', longOpt: 'project', args: 1,
- 'Filter issues by project (relative to the current directory). Accepts a '
- + 'comma-delimited list.')
-cli.s(longOpt: 'show-subprojects',
- 'Include sup projects in listing (default behaviour)')
-cli.S(longOpt: 'no-subprojects', 'Do not list subprojects.')
-
-def opts = cli.parse(args)
-def issuedb = [:]
-
-if (!opts) System.exit(1) // better solution?
-
-if (opts.h) cli.usage()
-
-def categories = ['bug','feature','task']
-if (opts.c) categories = opts.c.split(/[,\s]/)
-categories = categories.collect { Category.toCategory(it) }
-
-// build issue list
-issuedb = new Project(new File('.'),
- new Filter('categories': categories,
- 'priority': (opts.p ? opts.p.toInteger() : 9),
- 'projects': (opts.r ? opts.r.toLowerCase().split(/[,\s]/).asType(List.class) : []),
- 'ids': (opts.i ? opts.i.split(/[,\s]/).asType(List.class) : []),
- 'acceptProjects': (opts.s || !opts.S)))
-
-// list first
-if (opts.l) issuedb.list('verbose': opts.v)
-
-// change priority second
-//else if (opts.cp)
-
-// change category third
-//else if (opts.cc)
-
-// new entry last