Saturday, September 14, 2013

little to do

I finally have an initial version of a little to do application running using the "tree control flow" stuff I described earlier.

LittleToDo is a simple app, but it took me a little while to wire it all up, and I think it has a couple interesting features. First - littleToDo implements nested to-do's, so each to-do item can itself have a list of child to-do's, and grand children, etc. The UI currently only allows four or five generations, but increasing that limit just requires changing a constant in the code.

The other interesting features are under the hood. Most of the code is implemented as typescript YUI modules. I had to tweak the YUI typescript definition file I'm using to make the typescript YUI module trick work with typescript's 0.9.1.1 release (which tightens up some syntax rules that were more relaxed in typescript 0.8), but I eventually wound up with code like this:

...

import importY = require("../../libts/yui");
importY; // workaround for typescript bug: https://typescript.codeplex.com/workitem/1531
import Y = importY.Y;
Y = exports;

import littleAsset = require("littleAsset");
littleAsset;
import ax = littleAsset.littleware.asset;


/**
 * @module littleware-asset-base
 * @namespace littleware.asset
 */
export module littleware.asset.manager {

...

    /**
     * API tool for interacting with the asset repo.
     * @class AssetManager
     */
    export interface AssetManager {
        /**
         * @method addListener
         * @param listener {function}
         * @return YUI subscription with "detach" method to unsubscribe
         */
        addListener(listener: (ev: RefEvent) => void ): Y.EventHandle;

        /**
         * @method saveAsset
         * @return {Y.Promise[AssetRef]}
         */
        saveAsset(value: ax.Asset, updateComment: string): Y.Promise<AssetRef>;
        /**
         * @method deleteAsset
         * @return {Y.Promise[void]}
         */
        deleteAsset(id: string, deleteComment: string): Y.Promise<void>;

        /**
         * Return a reference to the asset with the given id if any - otherwise null.
         * @method loadAsset
         * @return {Y.Promise[AssetRef]}
         */
        loadAsset(id: string): Y.Promise<AssetRef>;

        /**
         * Load the child of the given parent with the given name -
         * every child has a unique name within its set of siblings.
         * Usually implemented as a shortcut for loadAsset( listChildren( parentId )[name] ).
         * @method loadChild
         * @param parentId {string}
         * @param name {string}
         * @return {Y.Promise{AssetRef}}
         */
        loadChild(parentId: string, name: string): Y.Promise<AssetRef>;

        /**
         * Load the asset at the given path if any - same as loadSubpath( null, path )
         * @method loadPath
         * @param path {string}
         * @return {Y.Promise{AssetRef}}
         */
        loadPath(path: string): Y.Promise<AssetRef>;

        /**
         * Load the asset at the given path below the given root
         * @method loadSubPath
         * @param rootId {string} id of the asset to treat as the root, or null to use forest root
         * @param path {string}
         * @return {Y.Promise{AssetRef}}
         */
        loadSubpath(rootId: string, path: string): Y.Promise<AssetRef>;

        /**
         * Like loadSubpath, but allows specification of a builder function 
         * for each segment of the path to create a missing asset if it doesn't exist -
         * buildBranch sets the builder's fromId and name properties.
         * @method buildBranch
         * @param rootId {string} id of the asset to treat as the root, or null to use forest root
         * @param branch {{name:string,builder:(parent:Asset)=>AssetBuilder}[]}
         * @return {Y.Promise{AssetRef[]}}
         */
        buildBranch(rootId: string, branch: { name: string; builder: (parent: ax.Asset) => ax.AssetBuilder; }[]): Y.Promise<AssetRef[]>;


        /**
         * List the children if any under the given parent node
         * @method listChildren
         * @param parentId {string}
         * @return {Y.Promise{NameIdListRef}}
         */
        listChildren(parentId: string): Y.Promise<NameIdListRef>;

        /**
         * List the root (littleware.HOME-TYPE) nodes - shortcut for listChildren(null)
         * @method listRoots
         * @return {Y.Promise{NameIdListRef}}
         */
        listRoots(): Y.Promise<NameIdListRef>;
    }

...

The current implementation of the above AssetManager interface (which underlies the data-management for the app) just saves assets (nodes in a data tree) to the browser's local storage, but the API returns promises, so it will be easy in the future to swap in an implementation that saves data via AJAX to a remote service. The AssetManager also closely resembles littleware's asset manager and asset search API's (that javadoc is out of date, but that's about right), so I'm hoping to implement a littleware backend for the next release of "little to do" with littleId based authentication. We'll see how it goes ...

No comments: