Skip to content

Commit

Permalink
feat(atom): add nested path getter / setter compilers
Browse files Browse the repository at this point in the history
- update cursor ctor to also accept paths
- update readme example
  • Loading branch information
postspectacular committed Jan 29, 2018
1 parent 1ae5f63 commit 5dce8a2
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 7 deletions.
19 changes: 14 additions & 5 deletions packages/atom/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,22 @@ a.reset(42);

```typescript
// main state
main = new atom.Atom({a: 23, b: 42});
main = new atom.Atom({ a: { b: { c: 23 }, d: { e: 42 } }, f: 66 });

// cursor to `a` value
// requires both a lookup & update function to given value
// cursor to `c` value
cursor = new atom.Cursor(main, "a.b.c");
// or
cursor = new atom.Cursor(main, ["a","b","c"]);

// alternatively provide path implicitly via lookup & update functions
// both fns will be called with cursor's parent state
// the updater MUST NOT mutate in place
cursor = new atom.Cursor(main, (state) => state.a, (state, x) => ({...state, a: x}));
// this allows the cursor implementation to work with any data structure
// as long as the updater DOES NOT mutate in place
cursor = new atom.Cursor(
main,
(s) => s.a.b.c,
(s, x) => ({...s, a: {...s.a, b: {...s.a.b, c: x}}})
);

// add watch just as with Atom
cursor.addWatch("foo", console.log);
Expand Down
18 changes: 17 additions & 1 deletion packages/atom/src/cursor.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { IID, IRelease, Watch } from "@thi.ng/api/api";
import { isArray } from "@thi.ng/checks/is-array";
import { isFunction } from "@thi.ng/checks/is-function";
import { isString } from "@thi.ng/checks/is-string";

import { IAtom, SwapFn } from "./api";
import { Atom } from "./atom";
import { getter, setter } from "./path";

export class Cursor<T> implements
IAtom<T>,
Expand All @@ -16,10 +21,21 @@ export class Cursor<T> implements
protected lookup: (s: any) => T;
protected selfUpdate: boolean;

constructor(parent: IAtom<any>, lookup: (s: any) => T, update: (s: any, v: T) => any) {
constructor(parent: IAtom<any>, path: PropertyKey | PropertyKey[]);
constructor(parent: IAtom<any>, lookup: (s: any) => T, update: (s: any, v: T) => any);
constructor(parent: IAtom<any>, ...opts: any[]) {
this.parent = parent;
this.id = `cursor-${Cursor.NEXT_ID++}`;
this.selfUpdate = false;
let lookup, update;
if (isString(opts[0]) || isArray(opts[0])) {
lookup = getter(opts[0]);
update = setter(opts[0]);
} else if (isFunction(opts[0]) && isFunction(opts[1])) {
[lookup, update] = opts;
} else {
throw new Error("illegal args");
}
this.local = new Atom<T>(lookup(parent.deref()));
this.local.addWatch(this.id, (_, prev, curr) => {
if (prev !== curr) {
Expand Down
3 changes: 2 additions & 1 deletion packages/atom/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from "./atom";
export * from "./cursor";
export * from "./cursor";
export * from "./path";
30 changes: 30 additions & 0 deletions packages/atom/src/path.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { isArray } from "@thi.ng/checks/is-array";
import { isString } from "@thi.ng/checks/is-string";

function compS(k, f) {
return (s, v) => ({ ...s, [k]: f((s || {})[k], v) });
}

function compG(k, f) {
return (s) => s ? f(s[k]) : undefined;
}

export function getter(path: PropertyKey | PropertyKey[]) {
const ks = isArray(path) ? path : isString(path) ? path.split(".") : [path],
kl = ks.pop();
let f = (s) => s ? s[kl] : undefined;
for (let i = ks.length - 1; i >= 0; i--) {
f = compG(ks[i], f);
}
return f;
}

export function setter(path: PropertyKey | PropertyKey[]) {
const ks = isArray(path) ? path : isString(path) ? path.split(".") : [path],
kl = ks.pop();
let f = (s, v) => ({ ...(s || {}), [kl]: v });
for (let i = ks.length - 1; i >= 0; i--) {
f = compS(ks[i], f);
}
return f;
}

0 comments on commit 5dce8a2

Please sign in to comment.