We are entering into the actual project part

So now its time to do the actual application programming.

















The ascii diagram again.

                                 v <-- you are here
   +----------+    +----------+      +----------+    +----------+
   | cli opts | -> | project  | -+-> |  print   | -> | display  |
   +----------+    |  config  |  |   +----------+    +----------+
                   +----------+  |
                                 |   +----------+    +----------+
                                 +-> |   add    | -> |   save   |
                                 |   +----------+    +----------+
                                 |
                                 |   +----------+    +----------+
                                 +-> |    rm    | -> |   save   |
                                     +----------+    +----------+

So this is where the bulk of the program will exist.

















So lets create the file (src/projector.ts)

> src/projector.ts

Lets get a codin! Also remember, we want to build this is a way that makes it easy to test.

Also, we will build the full projector object (more of a note for me than you)

















Here is what the code could look like

import fs from "fs";
import path from "path";
import { ProjectorConfig } from "./config";

type ProjectorData = {
    // todo: if we had other top level items, we could put them here
    // such as settings or links
    projector: {
        [key: string]: {
            [key: string]: string
        }
    }
}

type Value = string | undefined;

const DEFAULT_VALUE = {projector: {}} as ProjectorData;
export class Projector {
    constructor(private config: ProjectorConfig,
                private data: ProjectorData = DEFAULT_VALUE) { }

    getValue(key: string): Value {
        // pwd
        // dirname(pwd) until empty
        let prev: Value = undefined;
        let curr = this.config.pwd;

        let out: Value = undefined;
        do {

            let val = this.data.projector[curr]?.[key];
            if (val !== undefined) {
                out = val;
                break;
            }

            prev = curr;
            curr = path.dirname(curr);
        } while (prev !== curr);

        return out;
    }

    setValue(key: string, value: string) {
        let pwd = this.config.pwd;
        if (!this.data.projector[pwd]) {
            this.data.projector[pwd] = {};
        }

        this.data.projector[pwd][key] = value;
    }

    deleteValue(key: string) {
        delete this.data.projector[this.config.pwd]?.[key];
    }

    static fromConfig(config: ProjectorConfig): Projector {
        let data: ProjectorData = undefined;
        try {
            if (fs.existsSync(config.config)) {
                data = JSON.parse(fs.readFileSync(config.config).toString());
            }
        } catch {
            data = undefined;
        }

        return new Projector(config, data);
    }
}
















Did you eat your vegetables?

Lets test

















Your tests could look like

import { Operation } from "../config";
import { Projector } from "../projector";

function getConfig(pwd: string) {
    return {
        pwd,
        config: "/foo/bar/baz",
        operation: Operation.Add,
        arguments: [],
    };
}

function getData() {
    return {
        projector: {
            "/foo/bar/baz/buzz": {
                "foo": "bar1"
            },
            "/foo/bar/baz": {
                "foo": "bar2"
            },
            "/foo/bar": {
                "foo": "bar3"
            },
            "/foo": {
                "foo": "bar4"
            },
            "/": {
                "foo": "bar5",
                "bar": "bazz1",
            },
        }
    }
}

test("getting values", function() {
    const projector = new Projector(getConfig("/foo/bar"), getData());

    expect(projector.getValue("foo")).toEqual("bar3");
    expect(projector.getValue("blaz")).toEqual(undefined);
    expect(projector.getValue("bar")).toEqual("bazz1");
});

test("setting values", function() {
    const projector = new Projector(getConfig("/foo/bar"), getData());

    expect(projector.getValue("foo")).toEqual("bar3");
    projector.setValue("foo", "barNever");
    expect(projector.getValue("foo")).toEqual("barNever");

    const p2 = new Projector(getConfig("/foo"), getData());
    expect(p2.getValue("foo")).toEqual("bar4");

    const p3 = new Projector(getConfig("/foo/bar/baz"), getData());
    expect(p3.getValue("foo")).toEqual("bar2");
});

test("deleting values", function() {
    const projector = new Projector(getConfig("/foo/bar/baz"), getData());

    expect(projector.getValue("foo")).toEqual("bar2");
    projector.deleteValue("foo");
    expect(projector.getValue("foo")).toEqual("bar3");
    projector.deleteValue("foo");
    expect(projector.getValue("foo")).toEqual("bar3");

    const p2 = new Projector(getConfig("/foo/bar"), getData());
    expect(p2.getValue("foo")).toEqual("bar3");
    p2.deleteValue("foo");
    expect(p2.getValue("foo")).toEqual("bar4");
});
















Onto the Gopher!