Initial version (0.1) supporting 'process' command.
This commit is contained in:
@@ -0,0 +1,2 @@
|
|||||||
|
*.sw?
|
||||||
|
build/
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<project name="gtd-cli" basedir="." default="ng-deploy">
|
||||||
|
|
||||||
|
<property file="project.properties"/>
|
||||||
|
<import file="jdb-build-1.10.xml"/>
|
||||||
|
|
||||||
|
<target name="init">
|
||||||
|
<mkdir dir="${build.dir}/main/classes"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="ng-deploy" depends="build">
|
||||||
|
<!-- Stop the Nailgun Server -->
|
||||||
|
<exec executable="cmd" os="Windows XP">
|
||||||
|
<arg value="/c"/>
|
||||||
|
<arg value="ng-stop"/>
|
||||||
|
</exec>
|
||||||
|
|
||||||
|
<exec executable="ng-stop" os="Linux"/>
|
||||||
|
|
||||||
|
<!-- delete old copies -->
|
||||||
|
<delete>
|
||||||
|
<fileset dir="${nailgun.classpath.dir}">
|
||||||
|
<include name="${name}*.jar"/>
|
||||||
|
</fileset>
|
||||||
|
</delete>
|
||||||
|
|
||||||
|
<!-- copy new build -->
|
||||||
|
<copy todir="${nailgun.classpath.dir}">
|
||||||
|
<fileset dir="${build.dir}/lib/runtime/jar"/>
|
||||||
|
<fileset dir="${build.dir}">
|
||||||
|
<include name="${name}-${version}.${build.number}.jar"/>
|
||||||
|
</fileset>
|
||||||
|
</copy>
|
||||||
|
|
||||||
|
<!-- start the NG server up again. -->
|
||||||
|
<exec executable="cmd" os="Windows XP">
|
||||||
|
<arg value="/c"/>
|
||||||
|
<arg value="ng-start"/>
|
||||||
|
</exec>
|
||||||
|
|
||||||
|
<exec executable="ng-start" os="Linux"/>
|
||||||
|
</target>
|
||||||
|
</project>
|
||||||
@@ -0,0 +1,248 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<project name="Jonathan Bernard Build Common"
|
||||||
|
xmlns:ivy="antlib:org.apache.ivy.ant">
|
||||||
|
|
||||||
|
<property environment="env"/>
|
||||||
|
|
||||||
|
<!--======== INIT TARGETS ========-->
|
||||||
|
<target name="-init" depends="-common-init,init"/>
|
||||||
|
|
||||||
|
<target name="-common-init">
|
||||||
|
<!-- Set default values for some key properties. Since properties are
|
||||||
|
write once, any value set before this point takes precedence. -->
|
||||||
|
|
||||||
|
<property name="versioning.file" value="project.properties"/>
|
||||||
|
|
||||||
|
<property name="src.dir" value="${basedir}/src"/>
|
||||||
|
<property name="build.dir" value="${basedir}/build"/>
|
||||||
|
<property name="lib.dir" value="${basedir}/lib"/>
|
||||||
|
<property name="resources.dir" value="${basedir}/resources"/>
|
||||||
|
<property name="splash.image" value="splash.png"/>
|
||||||
|
|
||||||
|
<!--======== PATHS ========-->
|
||||||
|
<path id="groovy.classpath">
|
||||||
|
<fileset dir="${env.GROOVY_HOME}/lib">
|
||||||
|
<include name="*.jar"/>
|
||||||
|
</fileset>
|
||||||
|
</path>
|
||||||
|
|
||||||
|
<path id="groovy.embeddable">
|
||||||
|
<fileset dir="${env.GROOVY_HOME}/embeddable">
|
||||||
|
<include name="*.jar"/>
|
||||||
|
</fileset>
|
||||||
|
</path>
|
||||||
|
|
||||||
|
<path id="compile-libs">
|
||||||
|
<fileset dir="${build.dir}/lib/compile/jar">
|
||||||
|
<include name="*.jar"/>
|
||||||
|
</fileset>
|
||||||
|
</path>
|
||||||
|
|
||||||
|
<path id="runtime-libs">
|
||||||
|
<fileset dir="${build.dir}/lib/runtime/jar">
|
||||||
|
<include name="*.jar"/>
|
||||||
|
</fileset>
|
||||||
|
</path>
|
||||||
|
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="-init-groovy">
|
||||||
|
<taskdef name="groovyc" classpathref="groovy.classpath"
|
||||||
|
classname="org.codehaus.groovy.ant.Groovyc"/>
|
||||||
|
|
||||||
|
<taskdef name="groovy" classpathref="groovy.classpath"
|
||||||
|
classname="org.codehaus.groovy.ant.Groovy"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="init"/>
|
||||||
|
|
||||||
|
<target name="clean" depends="-init">
|
||||||
|
<delete dir="${build.dir}"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!--======== LIBRARY TARGETS ========-->
|
||||||
|
<target name="-lib" depends="-lib-local,-lib-ivy,lib"/>
|
||||||
|
|
||||||
|
<target name="lib"/>
|
||||||
|
|
||||||
|
<target name="-init-ivy">
|
||||||
|
<ivy:settings id="ivy.settings" file="ivysettings.xml"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="-lib-ivy" depends="-init-ivy" unless="${lib.local}">
|
||||||
|
<ivy:retrieve settingsRef="ivy.settings"
|
||||||
|
pattern="${lib.dir}/[conf]/[type]/[artifact]-[revision].[ext]"
|
||||||
|
conf="compile,runtime"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="-lib-groovy" if="${lib.local}">
|
||||||
|
<copy todir="${build.dir}/lib/runtime/jar">
|
||||||
|
<fileset dir="${env.GROOVY_HOME}/embeddable"/>
|
||||||
|
</copy>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="-lib-local" if="${lib.local}">
|
||||||
|
<echo message="Resolving libraries locally."/>
|
||||||
|
<mkdir dir="${build.dir}/lib/compile/jar"/>
|
||||||
|
<mkdir dir="${build.dir}/lib/runtime/jar"/>
|
||||||
|
<copy todir="${build.dir}/lib/compile/jar" failonerror="false">
|
||||||
|
<fileset dir="${lib.dir}/compile/jar"/>
|
||||||
|
</copy>
|
||||||
|
|
||||||
|
<copy todir="${build.dir}/lib/runtime/jar" failonerror="false">
|
||||||
|
<fileset dir="${lib.dir}/runtime/jar"/>
|
||||||
|
</copy>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!--======== VERSIONING TARGETS ========-->
|
||||||
|
<target name="increment-build-number" depends="-init">
|
||||||
|
<propertyfile file="${versioning.file}">
|
||||||
|
<entry key="build.number" default="0" type="int" value="1"
|
||||||
|
operation="+"/>
|
||||||
|
</propertyfile>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="set-version" depends="-init">
|
||||||
|
<input
|
||||||
|
message="The current version is ${version}. Enter a new version: "
|
||||||
|
addproperty="new-version"/>
|
||||||
|
<propertyfile file="${versioning.file}">
|
||||||
|
<entry key="version" value="${new-version}" operation="="
|
||||||
|
type="string"/>
|
||||||
|
<entry key="build.number" value="0" type="int" operation="="/>
|
||||||
|
</propertyfile>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!--======== COMPILATION TARGETS ========-->
|
||||||
|
<target name="-compile-groovy" depends="-init,-init-groovy,-lib,-lib-groovy">
|
||||||
|
<mkdir dir="${build.dir}/main/classes"/>
|
||||||
|
<groovyc srcdir="${src.dir}/main" destdir="${build.dir}/main/classes"
|
||||||
|
includeAntRuntime="false" fork="true">
|
||||||
|
|
||||||
|
<classpath>
|
||||||
|
<path refid="groovy.classpath"/>
|
||||||
|
<path refid="compile-libs"/>
|
||||||
|
</classpath>
|
||||||
|
<javac/>
|
||||||
|
</groovyc>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="-compile-java" depends="-init,-lib">
|
||||||
|
<mkdir dir="${build.dir}/main/classes"/>
|
||||||
|
<javac srcdir="${src.dir}/main" destdir="${build.dir}/main/classes"
|
||||||
|
includeAntRuntime="false" classpathref="compile-libs"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="compile" depends="-compile-groovy"/>
|
||||||
|
|
||||||
|
<!--======== JUNIT TARGETS ========-->
|
||||||
|
<target name="-compile-tests-groovy" depends="-init,compile">
|
||||||
|
<mkdir dir="${build.dir}/test/classes"/>
|
||||||
|
<groovyc srcdir="${src.dir}/test" destdir="${build.dir}/test/classes"
|
||||||
|
includeAntRuntime="false" fork="true">
|
||||||
|
|
||||||
|
<classpath>
|
||||||
|
<path refid="groovy.classpath"/>
|
||||||
|
<path refid="compile-libs"/>
|
||||||
|
<path location="${build.dir}/main/classes"/>
|
||||||
|
</classpath>
|
||||||
|
</groovyc>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="-compile-tests-java" depends="-init,compile">
|
||||||
|
<mkdir dir="${build.dir}/test/classes"/>
|
||||||
|
<javac srcdir="${src.dir}/test" destdir="${build.dir}/test/classes"
|
||||||
|
includeAntRuntime="false">
|
||||||
|
<classpath>
|
||||||
|
<path refid="compile-libs"/>
|
||||||
|
<path location="${build.dir}/main/classes"/>
|
||||||
|
</classpath>
|
||||||
|
</javac>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="compile-tests" depends="-compile-tests-groovy"/>
|
||||||
|
|
||||||
|
<target name="run-tests" depends="compile-tests,resources-test">
|
||||||
|
<junit printsummary="true">
|
||||||
|
<classpath>
|
||||||
|
<path refid="groovy.classpath"/>
|
||||||
|
<path refid="compile-libs"/>
|
||||||
|
<path location="${build.dir}/main/classes"/>
|
||||||
|
<path location="${build.dir}/test/classes"/>
|
||||||
|
</classpath>
|
||||||
|
<formatter type="plain" usefile="false"/>
|
||||||
|
<batchtest>
|
||||||
|
<fileset dir="${build.dir}/test/classes">
|
||||||
|
<include name="**/*"/>
|
||||||
|
</fileset>
|
||||||
|
</batchtest>
|
||||||
|
</junit>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!--======== RESOURCES TARGETS ========-->
|
||||||
|
|
||||||
|
<target name="resources" depends="-init">
|
||||||
|
<mkdir dir="${build.dir}/main/classes"/>
|
||||||
|
<copy todir="${build.dir}/main/classes" failonerror="false">
|
||||||
|
<fileset dir="${resources.dir}/main/"/>
|
||||||
|
</copy>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="resources-test" depends="-init">
|
||||||
|
<mkdir dir="${build.dir}/test/classes"/>
|
||||||
|
<copy todir="${build.dir}/test/classes" failonerror="false">
|
||||||
|
<fileset dir="${resources.dir}/test/"/>
|
||||||
|
</copy>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<!--======== BUILD TARGETS ========-->
|
||||||
|
<target name="-build-modular-lib" unless="executable.jar"
|
||||||
|
depends="compile,increment-build-number,resources">
|
||||||
|
|
||||||
|
<jar destfile="${build.dir}/${name}-${version}.${build.number}.jar"
|
||||||
|
basedir="${build.dir}/main/classes"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="-build-modular-executable" if="executable.jar"
|
||||||
|
depends="compile,increment-build-number,resources">
|
||||||
|
|
||||||
|
<pathconvert property="jar.classpath" pathsep=" " refid="runtime-libs">
|
||||||
|
<mapper>
|
||||||
|
<chainedmapper>
|
||||||
|
<!-- remove absolute path -->
|
||||||
|
<flattenmapper />
|
||||||
|
|
||||||
|
<!-- add lib/ prefix -->
|
||||||
|
<globmapper from="*" to="lib/*" />
|
||||||
|
</chainedmapper>
|
||||||
|
</mapper>
|
||||||
|
</pathconvert>
|
||||||
|
|
||||||
|
<jar destfile="${build.dir}/${name}-${version}.${build.number}.jar"
|
||||||
|
basedir="${build.dir}/main/classes">
|
||||||
|
|
||||||
|
<manifest>
|
||||||
|
<attribute name="Main-Class" value="${main.class}"/>
|
||||||
|
<attribute name="Class-Path" value="${jar.classpath}"/>
|
||||||
|
<attribute name="SplashScreen-Image" value="${splash.image}"/>
|
||||||
|
</manifest>
|
||||||
|
</jar>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="-build-modular"
|
||||||
|
depends="-build-modular-lib,-build-modular-executable"/>
|
||||||
|
|
||||||
|
<target name="-build-packed-libs"
|
||||||
|
depends="compile,increment-build-number,resources">
|
||||||
|
|
||||||
|
<unjar destdir="${build.dir}/main/classes">
|
||||||
|
<fileset dir="${build.dir}/lib/runtime/jar"/>
|
||||||
|
</unjar>
|
||||||
|
|
||||||
|
<jar destfile="${build.dir}/${name}-${version}.${build.number}.jar"
|
||||||
|
basedir="${build.dir}/main/classes"/>
|
||||||
|
</target>
|
||||||
|
|
||||||
|
<target name="build" depends="-build-modular"/>
|
||||||
|
|
||||||
|
</project>
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,7 @@
|
|||||||
|
#Sat, 27 Apr 2013 23:11:31 -0500
|
||||||
|
lib.local=true
|
||||||
|
name=jdb-gtd
|
||||||
|
version=0.1
|
||||||
|
nailgun.classpath.dir=/home/jdbernard/programs/nailgun/classpath
|
||||||
|
|
||||||
|
build.number=24
|
||||||
@@ -0,0 +1,294 @@
|
|||||||
|
package com.jdblabs.gtd.cli
|
||||||
|
|
||||||
|
import com.jdbernard.util.LightOptionParser
|
||||||
|
import com.martiansoftware.nailgun.NGContext
|
||||||
|
import org.joda.time.DateMidnight
|
||||||
|
import org.joda.time.DateTime
|
||||||
|
import org.slf4j.Logger
|
||||||
|
import org.slf4j.LoggerFactory
|
||||||
|
|
||||||
|
public class GTDCLI {
|
||||||
|
|
||||||
|
public static final String VERSION = "0.1"
|
||||||
|
private static String EOL = System.getProperty("line.separator")
|
||||||
|
|
||||||
|
private static GTDCLI nailgunInst
|
||||||
|
|
||||||
|
private int terminalWidth
|
||||||
|
private Scanner stdin
|
||||||
|
private File workingDir
|
||||||
|
private Logger log = LoggerFactory.getLogger(getClass())
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
GTDCLI inst = new GTDCLI(new File(System.getProperty("user.home"),
|
||||||
|
".gtdclirc"))
|
||||||
|
|
||||||
|
if (args.length > 0) args[-1] = args[-1].trim()
|
||||||
|
inst.run(args) }
|
||||||
|
|
||||||
|
public static void nailMain(NGContext context) {
|
||||||
|
if (nailgunInst == null)
|
||||||
|
nailgunInst = new GTDCLI(new File(
|
||||||
|
System.getProperty("user.home"), ".gtdclirc"))
|
||||||
|
else nailgunInst.stdin = new Scanner(context.in)
|
||||||
|
|
||||||
|
// trim the last argument, not all cli's are well-behaved
|
||||||
|
if (context.args.length > 0) context.args[-1] = context.args[-1].trim()
|
||||||
|
|
||||||
|
nailgunInst.run(context.args) }
|
||||||
|
|
||||||
|
public static void reconfigure(String[] args) {
|
||||||
|
if (nailgunInst == null) main(args)
|
||||||
|
else {
|
||||||
|
nailgunInst = null
|
||||||
|
nailgunInst = new GTDCLI(new File(
|
||||||
|
System.getProperty("user.home"), ".gritterrc"))
|
||||||
|
|
||||||
|
nailgunInst.run(args) } }
|
||||||
|
|
||||||
|
public GTDCLI(File configFile) {
|
||||||
|
|
||||||
|
// parse the config file
|
||||||
|
def config = [:]
|
||||||
|
if (configFile.exists())
|
||||||
|
config = new ConfigSlurper().parse(configFile.toURL())
|
||||||
|
|
||||||
|
// configure the terminal width
|
||||||
|
terminalWidth = (System.getenv().COLUMNS ?: config.terminalWidth ?: 79) as int
|
||||||
|
|
||||||
|
workingDir = config.defaultDirectory ?
|
||||||
|
new File(config.defaultDirectory) :
|
||||||
|
new File('.')
|
||||||
|
|
||||||
|
stdin = new Scanner(System.in) }
|
||||||
|
|
||||||
|
protected void run(String[] args) {
|
||||||
|
|
||||||
|
def cliDefinition = [
|
||||||
|
h: [longName: 'help'],
|
||||||
|
d: [longName: 'directory', arguments: 1],
|
||||||
|
v: [longName: 'version']]
|
||||||
|
|
||||||
|
def opts = LightOptionParser.parseOptions(cliDefinition, args as List)
|
||||||
|
|
||||||
|
if (opts.h) { printUsage(null); return }
|
||||||
|
if (opts.v) { println "GTD CLI v$VERSION"; return }
|
||||||
|
if (opts.d) workingDir = new File(opts.d)
|
||||||
|
|
||||||
|
def parsedArgs = (opts.args as List) as LinkedList
|
||||||
|
|
||||||
|
if (parsedArgs.size() < 1) printUsage()
|
||||||
|
|
||||||
|
log.debug("argument list: {}", parsedArgs)
|
||||||
|
|
||||||
|
while (parsedArgs.peek()) {
|
||||||
|
def command = parsedArgs.poll()
|
||||||
|
|
||||||
|
switch (command.toLowerCase()) {
|
||||||
|
case ~/help/: printUsafe(parsedArgs); break
|
||||||
|
case ~/process/: process(parsedArgs); break
|
||||||
|
default:
|
||||||
|
parsedArgs.addFirst(command)
|
||||||
|
process(parsedArgs)
|
||||||
|
break } } }
|
||||||
|
|
||||||
|
protected void process(LinkedList args) {
|
||||||
|
def rootDir = workingDir
|
||||||
|
|
||||||
|
def path = args.poll()
|
||||||
|
if (path) {
|
||||||
|
givenDir = new File(path)
|
||||||
|
if (givenDir.exists() && givenDir.isDirectory()) rootDir = givenDir
|
||||||
|
else { println "'$path' is not a valid directory."; return }}
|
||||||
|
|
||||||
|
def findGtdDir = { dirName ->
|
||||||
|
def dir = new File(rootDir, dirName)
|
||||||
|
if (!dir.exists() || !dir.isDirectory()) {
|
||||||
|
println "'${rootDir.canonicalPath}' is not a valid GTD " +
|
||||||
|
"directory (missing the '$dirName' folder)."
|
||||||
|
return null }
|
||||||
|
else return dir }
|
||||||
|
|
||||||
|
// check to see if this is the parent GTD folder, in which case it
|
||||||
|
// should contain `in`, `incubate`, `next-actions`, `projects`,
|
||||||
|
// `tickler`, and `waiting` folders
|
||||||
|
def inDir, incubateDir, actionsDir, projectsDir, ticklerDir,
|
||||||
|
waitingDir, doneDir
|
||||||
|
|
||||||
|
if (!(inDir = findGtdDir("in")) ||
|
||||||
|
!(incubateDir = findGtdDir("incubate")) ||
|
||||||
|
!(doneDir = findGtdDir("done")) ||
|
||||||
|
!(actionsDir = findGtdDir("next-actions")) ||
|
||||||
|
!(projectsDir = findGtdDir("projects")) ||
|
||||||
|
!(ticklerDir = findGtdDir("tickler")) ||
|
||||||
|
!(waitingDir = findGtdDir("waiting")))
|
||||||
|
return
|
||||||
|
|
||||||
|
// Start processing items
|
||||||
|
inDir.listFiles().collect { new Item(it) }.each { item ->
|
||||||
|
|
||||||
|
println ""
|
||||||
|
def response
|
||||||
|
def readline = {stdin.nextLine().trim()}
|
||||||
|
def prompt = { msg ->
|
||||||
|
if (msg instanceof List) msg = msg.join(EOL)
|
||||||
|
msg += "> "
|
||||||
|
print msg
|
||||||
|
def line
|
||||||
|
|
||||||
|
while(!(line = readline())) print msg
|
||||||
|
|
||||||
|
return line }
|
||||||
|
|
||||||
|
// 1. Is it actionable?
|
||||||
|
if (!item.title) item.title = filenameToString(item.file)
|
||||||
|
response = prompt([">> $item", "Is it actionable?"]).toLowerCase()
|
||||||
|
|
||||||
|
// Not actionable
|
||||||
|
if (!(response ==~ /yes|y/)) {
|
||||||
|
response = prompt("Incubate or trash?").toLowerCase()
|
||||||
|
|
||||||
|
// Trash
|
||||||
|
if ("trash" =~ response) item.file.delete()
|
||||||
|
|
||||||
|
// Incubate
|
||||||
|
else {
|
||||||
|
println "Enter extra info. One 'key: value' pair per line."
|
||||||
|
println "(ex: date: YYYY-MM-DD, details)"
|
||||||
|
println "End with an empty line."
|
||||||
|
print "> "
|
||||||
|
while (response = readline()) {
|
||||||
|
if (!response =~ /[:=]/) continue
|
||||||
|
def parts = response.split(/[:=]/)
|
||||||
|
item[parts[0].trim().toLowerCase()] =
|
||||||
|
PropertyHelp.parse(parts[1].trim())
|
||||||
|
print "> " }
|
||||||
|
|
||||||
|
def oldFile = item.file
|
||||||
|
item.file = new File(incubateDir, item.file.name)
|
||||||
|
item.save()
|
||||||
|
oldFile.delete() }
|
||||||
|
|
||||||
|
// Actionable
|
||||||
|
} else {
|
||||||
|
response = prompt("Will it take less than 2 minutes?").toLowerCase()
|
||||||
|
|
||||||
|
// Do it now
|
||||||
|
if (response ==~ /yes|y/) {
|
||||||
|
println "Do it now."; print "> "
|
||||||
|
readline();
|
||||||
|
|
||||||
|
def date = new DateMidnight().toString("YYYY-MM-dd")
|
||||||
|
def oldFile = item.file
|
||||||
|
item.file = new File(doneDir, "$date-${item.file.name}")
|
||||||
|
item.save()
|
||||||
|
oldFile.delete()
|
||||||
|
return }
|
||||||
|
|
||||||
|
// > 2 minutes
|
||||||
|
item.outcome = prompt("What is the desired outcome?")
|
||||||
|
|
||||||
|
println "Enter extra info. One 'key: value' pair per line."
|
||||||
|
println "(ex: date: YYYY-MM-DD, details)"
|
||||||
|
println "End with an empty line."
|
||||||
|
print "> "
|
||||||
|
|
||||||
|
while (response = readline()) {
|
||||||
|
if (!(response =~ /[:=]/)) continue
|
||||||
|
def parts = response.split(/[:=]/)
|
||||||
|
item[parts[0].trim().toLowerCase()] =
|
||||||
|
PropertyHelp.parse(parts[1].trim())
|
||||||
|
print "> " }
|
||||||
|
|
||||||
|
response = prompt("Too big for one action?").toLowerCase()
|
||||||
|
|
||||||
|
// Needs to be a project
|
||||||
|
if (response ==~ /yes|y/) {
|
||||||
|
def oldFile = item.file
|
||||||
|
item.file = new File(projectsDir,
|
||||||
|
stringToFilename(item.outcome))
|
||||||
|
item.save()
|
||||||
|
oldFile.delete()
|
||||||
|
println "Moved to projects." }
|
||||||
|
|
||||||
|
// Is a single action
|
||||||
|
else {
|
||||||
|
response = prompt("Delegate, defer, or tickler?").
|
||||||
|
toLowerCase()
|
||||||
|
|
||||||
|
// Delegate
|
||||||
|
if (response =~ /del/) {
|
||||||
|
|
||||||
|
item.action = prompt([
|
||||||
|
"Next action (who needs to do what).", ""])
|
||||||
|
|
||||||
|
def oldFile = item.file
|
||||||
|
item.file = new File(waitingDir,
|
||||||
|
stringToFilename(item.action))
|
||||||
|
item.save()
|
||||||
|
oldFile.delete()
|
||||||
|
|
||||||
|
println "Moved to ${waitingDir.name} folder." }
|
||||||
|
|
||||||
|
|
||||||
|
// Defer
|
||||||
|
else if (response =~ /def/) {
|
||||||
|
item.action = prompt(["Next action.", ""])
|
||||||
|
|
||||||
|
def oldFile = item.file
|
||||||
|
item.file = new File(actionsDir,
|
||||||
|
stringToFilename(item.action))
|
||||||
|
item.save()
|
||||||
|
oldFile.delete()
|
||||||
|
|
||||||
|
println "Moved to the ${actionsDir.name} folder."
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tickle
|
||||||
|
else {
|
||||||
|
item.action = prompt(["Next action.", ""])
|
||||||
|
item.tickle = prompt([
|
||||||
|
"When do you want it to become active?",
|
||||||
|
"(YYYY-MM-DD)"])
|
||||||
|
|
||||||
|
def oldFile = item.file
|
||||||
|
item.file = new File(ticklerDir,
|
||||||
|
stringToFilename(item.action))
|
||||||
|
item.save()
|
||||||
|
oldFile.delete()
|
||||||
|
println "Moved to the ${ticklerDir.name} folder." } } } } }
|
||||||
|
|
||||||
|
protected void printUsage(LinkedList args) {
|
||||||
|
|
||||||
|
if (!args) {
|
||||||
|
println "Jonathan Bernard's Getting Things Done CLI v$VERSION"
|
||||||
|
println "usage: gtd [option...] <command>..."
|
||||||
|
println ""
|
||||||
|
println "options are:"
|
||||||
|
println ""
|
||||||
|
println " -h, --help Print this usage information."
|
||||||
|
println " -d, --directory Set the GTD root directory."
|
||||||
|
println " -v, --version Print the GTD CLI version."
|
||||||
|
println ""
|
||||||
|
println "top-leve commands:"
|
||||||
|
println ""
|
||||||
|
println " process Process inbox items systematically."
|
||||||
|
println " help <command> Print detailed help about a command."
|
||||||
|
} else {
|
||||||
|
def command = args.poll()
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
//switch(command.toLowerCase()) {
|
||||||
|
// case ~/process/:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String filenameToString(File f) {
|
||||||
|
return f.name.replaceAll(/[-_]/, " ").capitalize() }
|
||||||
|
|
||||||
|
static String stringToFilename(String s) {
|
||||||
|
return s.replaceAll(/\s/, '-').
|
||||||
|
replaceAll(/[';:]/, '').
|
||||||
|
toLowerCase() }
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package com.jdblabs.gtd.cli
|
||||||
|
|
||||||
|
public class Item {
|
||||||
|
|
||||||
|
public File file
|
||||||
|
public Map properties = [:]
|
||||||
|
|
||||||
|
public Item(File f) {
|
||||||
|
this.file = f
|
||||||
|
|
||||||
|
def javaProps = new Properties()
|
||||||
|
f.withReader { reader -> javaProps.load(reader) }
|
||||||
|
javaProps.each { k, v -> properties[k] = PropertyHelp.parse(v) } }
|
||||||
|
|
||||||
|
public void save() {
|
||||||
|
def javaProps = new Properties()
|
||||||
|
properties.each { k, v -> javaProps[k] = PropertyHelp.format(v) }
|
||||||
|
file.withOutputStream { os -> javaProps.store(os, "") } }
|
||||||
|
|
||||||
|
public def propertyMissing(String name, def value) {
|
||||||
|
properties[name] = value }
|
||||||
|
|
||||||
|
public def propertyMissing(String name) { return properties[name] }
|
||||||
|
|
||||||
|
public String toString() {
|
||||||
|
if (properties.action) return properties.action
|
||||||
|
if (properties.outcome) return properties.outcome
|
||||||
|
if (properties.title) return properties.title
|
||||||
|
return file.name.replaceAll(/[-_]/, " ").capitalize() }
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
package com.jdblabs.gtd.cli
|
||||||
|
|
||||||
|
import org.joda.time.DateMidnight
|
||||||
|
import org.joda.time.DateTime
|
||||||
|
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
|
||||||
|
public enum PropertyHelp {
|
||||||
|
|
||||||
|
// Property types should be ordered here in order of decreasing specificity.
|
||||||
|
// That is, subclasses should come before the more general class so that
|
||||||
|
// objects are converted using the most specific class that
|
||||||
|
// PropertyHelp knows how to work with.
|
||||||
|
DATE_MIDNIGHT(/^\d{4}-\d{2}-\d{2}$/, DateMidnight,
|
||||||
|
{ v -> DateMidnight.parse(v) },
|
||||||
|
{ d -> d.toString("YYYY-MM-dd") }),
|
||||||
|
DATETIME(/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}$/, DateTime,
|
||||||
|
{ v -> DateTime.parse(v) },
|
||||||
|
{ d -> d.toString("YYYY-MM-dd'T'HH:mm:ss") }),
|
||||||
|
// We never want to parse a value into a java.util.Date or
|
||||||
|
// java.util.Calendar object (we are using Joda Time instead of the
|
||||||
|
// standard Java Date and Calendar objects) but we do want to be able to
|
||||||
|
// handle if someone gives us a Date or Calendar object.
|
||||||
|
DATE(NEVER_MATCH, Date,
|
||||||
|
{ v -> v }, // never called
|
||||||
|
{ d -> dateFormat.format(d) }),
|
||||||
|
CALENDAR(NEVER_MATCH, Calendar,
|
||||||
|
{ v -> v }, // never called
|
||||||
|
{ c ->
|
||||||
|
def df = dateFormat.clone()
|
||||||
|
df.calendar = c
|
||||||
|
df.format(c.time) }),
|
||||||
|
|
||||||
|
INTEGER(NEVER_MATCH, Integer,
|
||||||
|
{ v -> v as Integer }, // never called
|
||||||
|
{ i -> i as String }),
|
||||||
|
LONG(/^\d+$/, Long,
|
||||||
|
{ v -> v as Long },
|
||||||
|
{ l -> l as String }),
|
||||||
|
FLOAT(NEVER_MATCH, Float,
|
||||||
|
{ v -> v as Float}, // never called
|
||||||
|
{ f -> f as String}),
|
||||||
|
DOUBLE(/^\d+\.\d+$/, Double,
|
||||||
|
{ v -> v as Double },
|
||||||
|
{ d -> d as String });
|
||||||
|
|
||||||
|
String pattern;
|
||||||
|
Class klass;
|
||||||
|
def parseFun, formatFun;
|
||||||
|
|
||||||
|
private static SimpleDateFormat dateFormat =
|
||||||
|
new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
|
||||||
|
|
||||||
|
// This pattern for can never match (is uses negative lookahead to
|
||||||
|
// contradict itself).
|
||||||
|
private static String NEVER_MATCH = /(?!x)x/;
|
||||||
|
|
||||||
|
|
||||||
|
public PropertyHelp(String pattern, Class klass, def parseFun,
|
||||||
|
def formatFun) {
|
||||||
|
this.pattern = pattern
|
||||||
|
this.klass = klass
|
||||||
|
this.parseFun = parseFun
|
||||||
|
this.formatFun = formatFun }
|
||||||
|
|
||||||
|
public boolean matches(String prop) { return prop ==~ pattern }
|
||||||
|
|
||||||
|
public boolean matches(Class klass) { return this.klass == klass }
|
||||||
|
|
||||||
|
public static Object parse(String value) {
|
||||||
|
def propertyType = PropertyHelp.values().find {
|
||||||
|
it.matches(value) }
|
||||||
|
|
||||||
|
return propertyType ? propertyType.parseFun(value) : value }
|
||||||
|
|
||||||
|
public static String format(def object) {
|
||||||
|
def propertyType = PropertyHelp.values().find {
|
||||||
|
it.klass.isInstance(object) }
|
||||||
|
|
||||||
|
return propertyType ? propertyType.formatFun(object) : object.toString() }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user