Step zero of work toward imperative-syndicate/js
This commit is contained in:
commit
e3b91a9d47
|
@ -0,0 +1,2 @@
|
||||||
|
scratch/
|
||||||
|
node_modules/
|
|
@ -0,0 +1,13 @@
|
||||||
|
.PHONY: all clean veryclean test
|
||||||
|
|
||||||
|
all:
|
||||||
|
npm install .
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f dist/*.js
|
||||||
|
|
||||||
|
veryclean: clean
|
||||||
|
rm -rf node_modules/
|
||||||
|
|
||||||
|
test:
|
||||||
|
npm test
|
|
@ -0,0 +1,166 @@
|
||||||
|
{
|
||||||
|
"name": "syndicate-ijs",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"commander": {
|
||||||
|
"version": "2.3.0",
|
||||||
|
"resolved": "http://registry.npmjs.org/commander/-/commander-2.3.0.tgz",
|
||||||
|
"integrity": "sha1-/UMOiJgy7DU7ms0d4hfBHLPu+HM=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-2.2.0.tgz",
|
||||||
|
"integrity": "sha1-+HBX6ZWxofauaklgZkE3vFbwOdo=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"ms": "0.7.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"diff": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/diff/-/diff-1.4.0.tgz",
|
||||||
|
"integrity": "sha1-fyjS657nsVqX79ic5j3P2qPMur8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"escape-string-regexp": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-Tbwv5nTnGUnK8/smlc5/LcHZqNE=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"expect.js": {
|
||||||
|
"version": "0.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/expect.js/-/expect.js-0.3.1.tgz",
|
||||||
|
"integrity": "sha1-sKWaDS7/VDdUTr8M6qYBWEHQm1s=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"glob": {
|
||||||
|
"version": "3.2.11",
|
||||||
|
"resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz",
|
||||||
|
"integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"inherits": "2",
|
||||||
|
"minimatch": "0.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"growl": {
|
||||||
|
"version": "1.9.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/growl/-/growl-1.9.2.tgz",
|
||||||
|
"integrity": "sha1-Dqd0NxXbjY3ixe3hd14bRayFwC8=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"immutable": {
|
||||||
|
"version": "3.8.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/immutable/-/immutable-3.8.2.tgz",
|
||||||
|
"integrity": "sha1-wkOZUUVbs5kT2vKBN28VMOEErfM=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
|
||||||
|
"integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"jade": {
|
||||||
|
"version": "0.26.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/jade/-/jade-0.26.3.tgz",
|
||||||
|
"integrity": "sha1-jxDXl32NefL2/4YqgbBRPMslaGw=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"commander": "0.6.1",
|
||||||
|
"mkdirp": "0.3.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"commander": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "http://registry.npmjs.org/commander/-/commander-0.6.1.tgz",
|
||||||
|
"integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.3.0.tgz",
|
||||||
|
"integrity": "sha1-G79asbqCevI1dRQ0kEJkVfSB/h4=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lru-cache": {
|
||||||
|
"version": "2.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz",
|
||||||
|
"integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"minimatch": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz",
|
||||||
|
"integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"lru-cache": "2",
|
||||||
|
"sigmund": "~1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minimist": {
|
||||||
|
"version": "0.0.8",
|
||||||
|
"resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
|
||||||
|
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"mkdirp": {
|
||||||
|
"version": "0.5.1",
|
||||||
|
"resolved": "http://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
|
||||||
|
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"minimist": "0.0.8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"mocha": {
|
||||||
|
"version": "2.5.3",
|
||||||
|
"resolved": "http://registry.npmjs.org/mocha/-/mocha-2.5.3.tgz",
|
||||||
|
"integrity": "sha1-FhvlvetJZ3HrmzV0UFC2IrWu/Fg=",
|
||||||
|
"dev": true,
|
||||||
|
"requires": {
|
||||||
|
"commander": "2.3.0",
|
||||||
|
"debug": "2.2.0",
|
||||||
|
"diff": "1.4.0",
|
||||||
|
"escape-string-regexp": "1.0.2",
|
||||||
|
"glob": "3.2.11",
|
||||||
|
"growl": "1.9.2",
|
||||||
|
"jade": "0.26.3",
|
||||||
|
"mkdirp": "0.5.1",
|
||||||
|
"supports-color": "1.2.0",
|
||||||
|
"to-iso-string": "0.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "0.7.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-0.7.1.tgz",
|
||||||
|
"integrity": "sha1-nNE8A62/8ltl7/3nzoZO6VIBcJg=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"sigmund": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"supports-color": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-1.2.0.tgz",
|
||||||
|
"integrity": "sha1-/x7R5hFp0Gs88tWI4YixjYhH4X4=",
|
||||||
|
"dev": true
|
||||||
|
},
|
||||||
|
"to-iso-string": {
|
||||||
|
"version": "0.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/to-iso-string/-/to-iso-string-0.0.2.tgz",
|
||||||
|
"integrity": "sha1-TcGeZk38y+Jb2NtQiwDG2hWCVdE=",
|
||||||
|
"dev": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"name": "syndicate-ijs",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"description": "Imperative Syndicate in the browser",
|
||||||
|
"homepage": "https://github.com/tonyg/syndicate",
|
||||||
|
"main": "src/main.js",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git://github.com/tonyg/syndicate"
|
||||||
|
},
|
||||||
|
"directories": {
|
||||||
|
"bin": "./bin"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"clean": "rm -f dist/*",
|
||||||
|
"test": "mocha"
|
||||||
|
},
|
||||||
|
"author": "Tony Garnock-Jones <tonyg@ccs.neu.edu>",
|
||||||
|
"devDependencies": {
|
||||||
|
"expect.js": "^0.3.1",
|
||||||
|
"immutable": "^3.8.2",
|
||||||
|
"mocha": "^2.5.3"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,101 @@
|
||||||
|
"use strict";
|
||||||
|
// Bags and Deltas (which are Bags where item-counts can be negative).
|
||||||
|
|
||||||
|
const Immutable = require("immutable");
|
||||||
|
|
||||||
|
const PRESENT_TO_ABSENT = -1;
|
||||||
|
const ABSENT_TO_ABSENT = 0;
|
||||||
|
const ABSENT_TO_PRESENT = 1;
|
||||||
|
const PRESENT_TO_PRESENT = 2;
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
function MutableBag(s) {
|
||||||
|
this._items = s ? fromSet(s) : Immutable.Map();
|
||||||
|
}
|
||||||
|
|
||||||
|
MutableBag.prototype.change = function (key, delta) {
|
||||||
|
var net;
|
||||||
|
({bag: this._items, net: net} = change(this._items, key, delta));
|
||||||
|
return net;
|
||||||
|
};
|
||||||
|
|
||||||
|
MutableBag.prototype.get = function (key) {
|
||||||
|
return get(this._items, key);
|
||||||
|
};
|
||||||
|
|
||||||
|
MutableBag.prototype.clear = function () {
|
||||||
|
this._items = Immutable.Map();
|
||||||
|
};
|
||||||
|
|
||||||
|
MutableBag.prototype.includes = function (key) {
|
||||||
|
return includes(this._items, key);
|
||||||
|
};
|
||||||
|
|
||||||
|
MutableBag.prototype.isEmpty = function () {
|
||||||
|
return this._items.isEmpty();
|
||||||
|
};
|
||||||
|
|
||||||
|
MutableBag.prototype.count = function () {
|
||||||
|
return this._items.count();
|
||||||
|
};
|
||||||
|
|
||||||
|
MutableBag.prototype.keys = function () {
|
||||||
|
return this._items.keys();
|
||||||
|
};
|
||||||
|
|
||||||
|
MutableBag.prototype.entries = function () {
|
||||||
|
return this._items.entries();
|
||||||
|
};
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
const Bag = Immutable.Map;
|
||||||
|
|
||||||
|
function fromSet(s) {
|
||||||
|
return Bag().withMutations(function (b) {
|
||||||
|
for (let v of Immutable.Set(s)) {
|
||||||
|
b = b.set(v, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function change(bag, key, delta, clamp) {
|
||||||
|
let oldCount = get(bag, key);
|
||||||
|
let newCount = oldCount + delta;
|
||||||
|
if (clamp) {
|
||||||
|
newCount = Math.max(0, newCount);
|
||||||
|
}
|
||||||
|
if (newCount === 0) {
|
||||||
|
return {
|
||||||
|
bag: bag.remove(key),
|
||||||
|
net: (oldCount === 0) ? ABSENT_TO_ABSENT : PRESENT_TO_ABSENT
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
bag: bag.set(key, newCount),
|
||||||
|
net: (oldCount === 0) ? ABSENT_TO_PRESENT : PRESENT_TO_PRESENT
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function get(bag, key) {
|
||||||
|
return bag.get(key, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function includes(bag, key) {
|
||||||
|
return get(bag, key) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
module.exports.PRESENT_TO_ABSENT = PRESENT_TO_ABSENT;
|
||||||
|
module.exports.ABSENT_TO_ABSENT = ABSENT_TO_ABSENT;
|
||||||
|
module.exports.ABSENT_TO_PRESENT = ABSENT_TO_PRESENT;
|
||||||
|
module.exports.PRESENT_TO_PRESENT = PRESENT_TO_PRESENT;
|
||||||
|
module.exports.MutableBag = MutableBag;
|
||||||
|
module.exports.Bag = Bag;
|
||||||
|
module.exports.fromSet = fromSet;
|
||||||
|
module.exports.change = change;
|
||||||
|
module.exports.get = get;
|
||||||
|
module.exports.includes = includes;
|
|
@ -0,0 +1,4 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
module.exports.Bag = require("./bag.js");
|
||||||
|
// module.exports.Skeleton = require("./skeleton.js");
|
|
@ -0,0 +1,114 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var expect = require('expect.js');
|
||||||
|
var Immutable = require('immutable');
|
||||||
|
var Bag = require('../src/bag.js');
|
||||||
|
|
||||||
|
describe('immutable bag', function () {
|
||||||
|
it('should be initializable from a set', function () {
|
||||||
|
var b = Bag.fromSet(Immutable.Set(['a', 'b', 'c']));
|
||||||
|
expect(b.count()).to.equal(3);
|
||||||
|
expect(Bag.get(b, 'a')).to.equal(1);
|
||||||
|
expect(Bag.get(b, 'z')).to.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be initializable from an array', function () {
|
||||||
|
var b = Bag.fromSet(['a', 'b', 'c', 'a']);
|
||||||
|
expect(b.count()).to.equal(3);
|
||||||
|
expect(Bag.get(b, 'a')).to.equal(1);
|
||||||
|
expect(Bag.get(b, 'z')).to.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be immutable', function () {
|
||||||
|
var b = Bag.Bag();
|
||||||
|
Bag.change(b, 'a', 1);
|
||||||
|
Bag.change(b, 'a', 1);
|
||||||
|
expect(b).to.equal(Bag.Bag());
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should count up', function () {
|
||||||
|
var b = Bag.Bag();
|
||||||
|
var change1, change2;
|
||||||
|
({bag: b, net: change1} = Bag.change(b, 'a', 1));
|
||||||
|
({bag: b, net: change2} = Bag.change(b, 'a', 1));
|
||||||
|
expect(change1).to.equal(Bag.ABSENT_TO_PRESENT);
|
||||||
|
expect(change2).to.equal(Bag.PRESENT_TO_PRESENT);
|
||||||
|
expect(Bag.get(b, 'a')).to.equal(2);
|
||||||
|
expect(Bag.get(b, 'z')).to.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should count down', function () {
|
||||||
|
var b = Bag.fromSet(['a']);
|
||||||
|
var c1, c2, c3, c4;
|
||||||
|
({bag: b, net: c1} = Bag.change(b, 'a', 1));
|
||||||
|
({bag: b, net: c2} = Bag.change(b, 'a', -1));
|
||||||
|
expect(b.count()).to.equal(1);
|
||||||
|
expect(c1).to.equal(Bag.PRESENT_TO_PRESENT);
|
||||||
|
expect(c2).to.equal(Bag.PRESENT_TO_PRESENT);
|
||||||
|
({bag: b, net: c3} = Bag.change(b, 'a', -1));
|
||||||
|
expect(b.count()).to.equal(0);
|
||||||
|
expect(c3).to.equal(Bag.PRESENT_TO_ABSENT);
|
||||||
|
expect(Bag.get(b, 'a')).to.equal(0);
|
||||||
|
expect(Bag.get(b, 'z')).to.equal(0);
|
||||||
|
({bag: b, net: c4} = Bag.change(b, 'a', -1));
|
||||||
|
expect(b.count()).to.equal(1);
|
||||||
|
expect(c4).to.equal(Bag.ABSENT_TO_PRESENT);
|
||||||
|
expect(Bag.get(b, 'a')).to.equal(-1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be clamped', function() {
|
||||||
|
var b = Bag.fromSet(['a']);
|
||||||
|
({bag: b} = Bag.change(b, 'a', -1, true));
|
||||||
|
({bag: b} = Bag.change(b, 'a', -1, true));
|
||||||
|
({bag: b} = Bag.change(b, 'a', -1, true));
|
||||||
|
({bag: b} = Bag.change(b, 'a', -1, true));
|
||||||
|
expect(b.count()).to.equal(0);
|
||||||
|
expect(Bag.get(b, 'a')).to.equal(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('mutable bag', function () {
|
||||||
|
it('should be initializable from a set', function () {
|
||||||
|
var b = new Bag.MutableBag(Immutable.Set(['a', 'b', 'c']));
|
||||||
|
expect(b.count()).to.equal(3);
|
||||||
|
expect(b.get('a')).to.equal(1);
|
||||||
|
expect(b.get('z')).to.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be initializable from an array', function () {
|
||||||
|
var b = new Bag.MutableBag(['a', 'b', 'c', 'a']);
|
||||||
|
expect(b.count()).to.equal(3);
|
||||||
|
expect(b.get('a')).to.equal(1);
|
||||||
|
expect(b.get('z')).to.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be mutable', function () {
|
||||||
|
var b = new Bag.MutableBag();
|
||||||
|
b.change('a', 1);
|
||||||
|
b.change('a', 1);
|
||||||
|
expect(b.get('a')).to.equal(2);
|
||||||
|
expect(b.get('z')).to.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should count up', function () {
|
||||||
|
var b = new Bag.MutableBag();
|
||||||
|
expect(b.change('a', 1)).to.equal(Bag.ABSENT_TO_PRESENT);
|
||||||
|
expect(b.change('a', 1)).to.equal(Bag.PRESENT_TO_PRESENT);
|
||||||
|
expect(b.get('a')).to.equal(2);
|
||||||
|
expect(b.get('z')).to.equal(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should count down', function () {
|
||||||
|
var b = new Bag.MutableBag(['a']);
|
||||||
|
expect(b.change('a', 1)).to.equal(Bag.PRESENT_TO_PRESENT);
|
||||||
|
expect(b.change('a', -1)).to.equal(Bag.PRESENT_TO_PRESENT);
|
||||||
|
expect(b.count()).to.equal(1);
|
||||||
|
expect(b.change('a', -1)).to.equal(Bag.PRESENT_TO_ABSENT);
|
||||||
|
expect(b.count()).to.equal(0);
|
||||||
|
expect(b.get('a')).to.equal(0);
|
||||||
|
expect(b.get('z')).to.equal(0);
|
||||||
|
expect(b.change('a', -1)).to.equal(Bag.ABSENT_TO_PRESENT);
|
||||||
|
expect(b.count()).to.equal(1);
|
||||||
|
expect(b.get('a')).to.equal(-1);
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue