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