Add new "container" type to frptools
This commit is contained in:
parent
18ef6229f1
commit
08e141b468
@ -212,3 +212,33 @@ layoutEventBundle.height(480);
|
|||||||
```
|
```
|
||||||
|
|
||||||
The properties exposed by the bundle can also be updated apart from their grouping.
|
The properties exposed by the bundle can also be updated apart from their grouping.
|
||||||
|
|
||||||
|
# [container](./src/container.js)
|
||||||
|
|
||||||
|
`container` is a wrapper around any container type (object, Set, Map, or Array) while monitoring
|
||||||
|
changes to the container. A container can be subscribed to and `computed` instances can depend on
|
||||||
|
them.
|
||||||
|
|
||||||
|
## Behavior
|
||||||
|
|
||||||
|
Anytime a property is set or a method is gotten and called, the container will check for an updated
|
||||||
|
state and trigger subscribers if it is updated. An hash function must be applied to determine
|
||||||
|
updated status otherwise subscribers will be called on any potential update.
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Creation
|
||||||
|
|
||||||
|
```js
|
||||||
|
const monkeys = contained([], arr => arr.join('$'));
|
||||||
|
const firstMonkey = computed(m => (m.length ? m[0] : null), [monkeys]);
|
||||||
|
firstMonkey.subscribe(console.log.bind.console);
|
||||||
|
```
|
||||||
|
|
||||||
|
### Add a member to the container
|
||||||
|
|
||||||
|
```js
|
||||||
|
monkeys.push('Bill');
|
||||||
|
```
|
||||||
|
|
||||||
|
_firstMonkey_ would be computed and "Bill" would be logged to the console.
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "frptools",
|
"name": "frptools",
|
||||||
"version": "2.1.0",
|
"version": "2.2.0",
|
||||||
"description": "Observable Property and Computed data streams",
|
"description": "Observable Property and Computed data streams",
|
||||||
"main": "lib/index.js",
|
"main": "lib/index.js",
|
||||||
"jsnext:main": "src/index.js",
|
"jsnext:main": "src/index.js",
|
||||||
|
|||||||
61
packages/frptools/spec/container.spec.js
Normal file
61
packages/frptools/spec/container.spec.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
const { container, computed } = require('../lib/index.js');
|
||||||
|
const { hashSet } = require('../lib/util.js');
|
||||||
|
|
||||||
|
describe('A container', () => {
|
||||||
|
it('notifies dependents of updates', () => {
|
||||||
|
let runCount = 0;
|
||||||
|
let currentValue = new Set();
|
||||||
|
const a = container(new Set(), hashSet);
|
||||||
|
const b = computed(s => Array.from(s).reduce((i, acc) => i + acc, 0), [a]);
|
||||||
|
a.subscribe(val => {
|
||||||
|
runCount += 1;
|
||||||
|
expect(hashSet(a)).toEqual(hashSet(currentValue));
|
||||||
|
});
|
||||||
|
currentValue.add(1);
|
||||||
|
a.add(1);
|
||||||
|
expect(runCount).toEqual(1);
|
||||||
|
expect(b()).toEqual(1);
|
||||||
|
currentValue.add(2);
|
||||||
|
a.add(2);
|
||||||
|
expect(runCount).toEqual(2);
|
||||||
|
expect(b()).toEqual(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works for arrays', () => {
|
||||||
|
let runCount = 0;
|
||||||
|
let currentValue = [];
|
||||||
|
const a = container([], arr => arr.join('x'));
|
||||||
|
a.subscribe(val => {
|
||||||
|
runCount += 1;
|
||||||
|
expect(a.join('x')).toEqual(currentValue.join('x'));
|
||||||
|
});
|
||||||
|
currentValue.push(1);
|
||||||
|
a.push(1);
|
||||||
|
expect(runCount).toEqual(1);
|
||||||
|
currentValue.push(2);
|
||||||
|
a.push(2);
|
||||||
|
expect(runCount).toEqual(2);
|
||||||
|
currentValue.push(3);
|
||||||
|
a._.push(3);
|
||||||
|
expect(runCount).toEqual(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('._ returns the proxied element', () => {
|
||||||
|
let runCount = 0;
|
||||||
|
let currentValue = new Set();
|
||||||
|
const a = container(new Set(), hashSet);
|
||||||
|
a.subscribe(val => {
|
||||||
|
runCount += 1;
|
||||||
|
expect(hashSet(a)).toEqual(hashSet(currentValue));
|
||||||
|
});
|
||||||
|
currentValue.add(1);
|
||||||
|
a.add(1);
|
||||||
|
expect(runCount).toEqual(1);
|
||||||
|
currentValue.add(2);
|
||||||
|
a.add(2);
|
||||||
|
expect(runCount).toEqual(2);
|
||||||
|
currentValue.add(3);
|
||||||
|
a._.add(3);
|
||||||
|
expect(runCount).toEqual(2);
|
||||||
|
});
|
||||||
|
});
|
||||||
@ -67,4 +67,4 @@ export function computed(fn, dependencies = [], hash = id) {
|
|||||||
return accessor;
|
return accessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
const runParam = a => a();
|
const runParam = a => (typeof a === 'function' ? a() : a);
|
||||||
|
|||||||
58
packages/frptools/src/container.js
Normal file
58
packages/frptools/src/container.js
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
export function container(store, hash) {
|
||||||
|
const subscribers = new Set();
|
||||||
|
let id = hash(store);
|
||||||
|
|
||||||
|
const containerMethods = {
|
||||||
|
subscribe: fn => {
|
||||||
|
subscribers.add(fn);
|
||||||
|
return () => {
|
||||||
|
subscribers.delete(fn);
|
||||||
|
return subscribers.size;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
unsubscribeAll: () => subscribers.clear()
|
||||||
|
};
|
||||||
|
containerMethods._d = containerMethods.subscribe;
|
||||||
|
|
||||||
|
function checkUpdate() {
|
||||||
|
const newId = hash(store);
|
||||||
|
if (id !== newId) {
|
||||||
|
id = newId;
|
||||||
|
subscribers.forEach(s => s(store));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const p = new Proxy(store, {
|
||||||
|
apply: (target, context, args) => {
|
||||||
|
return target;
|
||||||
|
},
|
||||||
|
get: (target, name) => {
|
||||||
|
if (name in containerMethods) {
|
||||||
|
return containerMethods[name];
|
||||||
|
}
|
||||||
|
if (name === '_') {
|
||||||
|
return target;
|
||||||
|
}
|
||||||
|
const thing = target[name];
|
||||||
|
if (typeof thing === 'function') {
|
||||||
|
return (...args) => {
|
||||||
|
const ret = target[name](...args);
|
||||||
|
checkUpdate();
|
||||||
|
return ret;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return thing;
|
||||||
|
},
|
||||||
|
set: (target, name, newVal) => {
|
||||||
|
if (name in containerMethods) {
|
||||||
|
throw new ReferenceError(`Cannot set ${name} in ${target}`);
|
||||||
|
}
|
||||||
|
target[name] = newVal;
|
||||||
|
checkUpdate();
|
||||||
|
|
||||||
|
return newVal;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return p;
|
||||||
|
}
|
||||||
@ -1,3 +1,4 @@
|
|||||||
export { prop } from './property';
|
export { prop } from './property';
|
||||||
export { computed } from './computed';
|
export { computed } from './computed';
|
||||||
export { bundle } from './bundle';
|
export { bundle } from './bundle';
|
||||||
|
export { container } from './container';
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user