Skip to content

Commit

Permalink
Add 1-5 strict arity to update method
Browse files Browse the repository at this point in the history
  • Loading branch information
TrySound committed Nov 19, 2017
1 parent 0a81a19 commit f0e04aa
Show file tree
Hide file tree
Showing 6 changed files with 199 additions and 145 deletions.
3 changes: 2 additions & 1 deletion .eslintrc.json
Expand Up @@ -31,6 +31,7 @@
"es5/no-shorthand-properties": 0,
"es5/no-rest-parameters": 0,
"es5/no-template-literals": 0,
"es5/no-spread": 0
"es5/no-spread": 0,
"es5/no-destructuring": 0
}
}
4 changes: 4 additions & 0 deletions derivable.js.flow
Expand Up @@ -24,6 +24,10 @@ export interface Atom<T> extends Derivable<T> {
set(value: T): void;

update(f: (value: T) => T): void;
update<A>(f: (value: T, A) => T, A): void;
update<A, B>(f: (value: T, A, B) => T, A, B): void;
update<A, B, C>(f: (value: T, A, B, C) => T, A, B, C): void;
update<A, B, C, D>(f: (value: T, A, B, C, D) => T, A, B, C, D): void;

proxy<E>(proxy: Proxy<T, E>): Atom<E>;
};
Expand Down
1 change: 0 additions & 1 deletion src/atom.js
Expand Up @@ -14,7 +14,6 @@ export function Atom(value) {
this._state = UNCHANGED;
this._type = ATOM;
this._equals = null;
return this;
}

util.assign(Atom.prototype, {
Expand Down
19 changes: 17 additions & 2 deletions src/mutable.js
@@ -1,8 +1,23 @@
import { Proxy } from "./proxy";

export const mutablePrototype = {
update(f) {
return this.set(f(this.get()));
update(f, a, b, c, d) {
switch (arguments.length) {
case 0:
throw Error("update method accepts at least 1 argument");
case 1:
return this.set(f(this.get()));
case 2:
return this.set(f(this.get(), a));
case 3:
return this.set(f(this.get(), a, b));
case 4:
return this.set(f(this.get(), a, b, c));
case 5:
return this.set(f(this.get(), a, b, c, d));
default:
throw Error("update method accepts only 5 arguments");
}
},

proxy(monoProxyMapping) {
Expand Down
294 changes: 153 additions & 141 deletions test/atom.test.js
@@ -1,161 +1,173 @@
"use strict";

const derivable = require("../dist/derivable");
const { atom, transact } = require("../dist/derivable");

describe("the humble atom", () => {
let n;

beforeEach(() => {
n = derivable.atom(0);
});
test("can be dereferenced via .get to obtain its current state", () => {
expect(atom(0).get()).toBe(0);
});

it("can be dereferenced via .get to obtain its current state", () => {
expect(n.get()).toBe(0);
});
test("can be .set to change its current state", () => {
const n = atom(0);
n.set(1);
expect(n.get()).toBe(1);
});

it("can be .set to change its current state", () => {
n.set(1);
expect(n.get()).toBe(1);
});
test("can .update (a la swap in clojure)", () => {
const n = atom(0);
n.set(1);
const double = x => x * 2;
n.update(double);
expect(n.get()).toBe(2);
n.update(double);
expect(n.get()).toBe(4);

expect(() => {
n.update();
}).toThrow();
});

it("can .update (a la swap in clojure)", () => {
n.set(1);
const double = x => x * 2;
n.update(double);
expect(n.get()).toBe(2);
n.update(double);
expect(n.get()).toBe(4);
});
test("update with arguments", () => {
const n = atom(0);
const sum = (value, ...args) => args.reduce((acc, d) => acc + d, value);
n.update(sum, 1);
expect(n.get()).toBe(1);
n.update(sum, 1, 2);
expect(n.get()).toBe(4);
n.update(sum, 1, 2, 3);
expect(n.get()).toBe(10);
n.update(sum, 1, 2, 3, 4);
expect(n.get()).toBe(20);
expect(() => {
n.update(sum, 1, 2, 3, 4, 5);
}).toThrow();
});

it("can take on temporary values inside a transaction", () => {
const a = derivable.atom("a");
derivable.transact(abort1 => {
a.set("b");
expect(a.get()).toBe("b");
derivable.transact(abort2 => {
a.set("c");
expect(a.get()).toBe("c");
abort2();
});
expect(a.get()).toBe("b");
abort1();
test("can take on temporary values inside a transaction", () => {
const a = atom("a");
transact(abort1 => {
a.set("b");
expect(a.get()).toBe("b");
transact(abort2 => {
a.set("c");
expect(a.get()).toBe("c");
abort2();
});
expect(a.get()).toBe("a");
expect(a.get()).toBe("b");
abort1();
});
expect(a.get()).toBe("a");
});

it("should be able to go back to its original value with no ill effects", () => {
const a = derivable.atom("a");
let reacted = false;
a.react(
() => {
reacted = true;
},
{ skipFirst: true }
);

// no reaction to begin with
expect(reacted).toBe(false);

derivable.transact(() => {
a.set("b");
a.set("a");
});

// no reaction should take place
expect(reacted).toBe(false);
test("should be able to go back to its original value with no ill effects", () => {
const a = atom("a");
let reacted = false;
a.react(
() => {
reacted = true;
},
{ skipFirst: true }
);

// no reaction to begin with
expect(reacted).toBe(false);

transact(() => {
a.set("b");
a.set("a");
});

it("can keep transaction values if they are't aborted", () => {
const a = derivable.atom("a");
derivable.transact(() => {
a.set("b");
derivable.transact(() => {
a.set("c");
});
expect(a.get()).toBe("c");
// no reaction should take place
expect(reacted).toBe(false);
});

test("can keep transaction values if they are't aborted", () => {
const a = atom("a");
transact(() => {
a.set("b");
transact(() => {
a.set("c");
});
expect(a.get()).toBe("c");
});
expect(a.get()).toBe("c");
});

it("can include an equality-checking function", () => {
const a = derivable.atom(0);
const b = a.withEquality(() => false);
// creates a brand new atom
expect(a).not.toBe(b);

let numReactions = 0;
a.react(
() => {
numReactions++;
},
{ skipFirst: true }
);
b.react(
() => {
numReactions++;
},
{ skipFirst: true }
);

expect(numReactions).toBe(0);
a.set(0);
expect(numReactions).toBe(0);
a.set(0);
expect(numReactions).toBe(0);

b.set(0);
expect(numReactions).toBe(1);
b.set(0);
expect(numReactions).toBe(2);
});
test("can include an equality-checking function", () => {
const a = atom(0);
const b = a.withEquality(() => false);
// creates a brand new atom
expect(a).not.toBe(b);

let numReactions = 0;
a.react(
() => {
numReactions++;
},
{ skipFirst: true }
);
b.react(
() => {
numReactions++;
},
{ skipFirst: true }
);

expect(numReactions).toBe(0);
a.set(0);
expect(numReactions).toBe(0);
a.set(0);
expect(numReactions).toBe(0);

b.set(0);
expect(numReactions).toBe(1);
b.set(0);
expect(numReactions).toBe(2);
});

it("only likes functions or falsey things for equality functions", () => {
derivable.atom(4).withEquality("");
expect(() => {
derivable.atom(4).withEquality("yo");
}).toThrow();
derivable.atom(4).withEquality(0);
expect(() => {
derivable.atom(4).withEquality(7);
}).toThrow();
derivable.atom(4).withEquality(null);
derivable.atom(4).withEquality(void 0);
});
test("only likes functions or falsey things for equality functions", () => {
atom(4).withEquality("");
expect(() => {
atom(4).withEquality("yo");
}).toThrow();
atom(4).withEquality(0);
expect(() => {
atom(4).withEquality(7);
}).toThrow();
atom(4).withEquality(null);
atom(4).withEquality(void 0);
});

describe("the concurrent modification of _reactors bug", () => {
it("doesnt happen any more", () => {
const $A = derivable.atom(false);
const $B = derivable.atom(false);

let A_success = false;
let C_success = false;

$A.react(
() => {
A_success = true;
},
{ from: $A }
);

const $C = $A.derive(a => a && $B.get());

$C.react(
() => {
C_success = true;
},
{ from: $C }
);

expect(A_success).toBe(false);
expect(C_success).toBe(false);
// used to be that this would cause the 'from' controller on C to be ignored
// during the ._maybeReact iteration in .set
$A.set(true);
expect(A_success).toBe(true);
expect(C_success).toBe(false);
$B.set(true);

expect(C_success).toBe(true);
});
test("the concurrent modification of _reactors bug doesnt happen any more", () => {
const $A = atom(false);
const $B = atom(false);

let A_success = false;
let C_success = false;

$A.react(
() => {
A_success = true;
},
{ from: $A }
);

const $C = $A.derive(a => a && $B.get());

$C.react(
() => {
C_success = true;
},
{ from: $C }
);

expect(A_success).toBe(false);
expect(C_success).toBe(false);
// used to be that this would cause the 'from' controller on C to be ignored
// during the ._maybeReact iteration in .set
$A.set(true);
expect(A_success).toBe(true);
expect(C_success).toBe(false);
$B.set(true);

expect(C_success).toBe(true);
});
23 changes: 23 additions & 0 deletions test_flow/test_flow.js
Expand Up @@ -19,6 +19,29 @@ const testAtom = () => {
a.update((d: string) => d);
// $ExpectError
a.update(d => String(d));
// $ExpectError
a.update();

a.update((d, x) => d + x, 1);
// $ExpectError
a.update((d, x) => d + x, "1");

a.update((d, x, y) => d + x + y, 1, 2);
// $ExpectError
a.update((d, x, y) => d + x + y, 1, "2");

a.update((d, x, y, z) => d + x + y + z, 1, 2, 3);
// $ExpectError
a.update((d, x, y, z) => d + x + y + z, 1, 2, "3");

a.update((d, x, y, z, u) => d + x + y + z + u, 1, 2, 3, 4);
// $ExpectError
a.update((d, x, y, z, u) => d + x + y + z + u, 1, 2, 3, "4");

// TODO should be an error
a.update((d, x, y, z, u, w) => d + x + y + u + w, 1, 2, 3, 4);
// $ExpectError
a.update((d, x, y, z, u, w) => d + x + y + u + w, 1, 2, 3, 4, 5);
};

function testDeriveMethod() {
Expand Down

0 comments on commit f0e04aa

Please sign in to comment.