Some modularity; general cleanup of codebase

This commit is contained in:
Tony Garnock-Jones 2014-07-23 17:21:51 -07:00
parent 4239c1ea33
commit 7bab9be492
27 changed files with 4390 additions and 1732 deletions

3
.gitignore vendored
View File

@ -1,6 +1,5 @@
private-key.pem private-key.pem
server-cert.pem server-cert.pem
MarketplaceChat.app.zip
MarketplaceChat.app/
scratch/ scratch/
_site/ _site/
node_modules/

View File

@ -1,21 +1,8 @@
APP_NAME=MarketplaceChat.app all:
LIB_SOURCES=\ npm run build
route.js \
marketplace.js \
spy.js \
dom-driver.js \
jquery-driver.js \
routing-table-widget.js \
routing-table-widget.css \
wake-detector.js \
websocket-driver.js
APP_SOURCES=\
examples/chat/index.html \
examples/chat/index.js \
examples/chat/style.css
RESOURCES=$(wildcard examples/chat/app-resources/*)
all: $(APP_NAME).zip deps:
npm install .
keys: private-key.pem server-cert.pem keys: private-key.pem server-cert.pem
@ -31,23 +18,3 @@ server-cert.pem: private-key.pem
clean-keys: clean-keys:
rm -f private-key.pem server-cert.pem rm -f private-key.pem server-cert.pem
$(APP_NAME).zip: $(APP_NAME)
zip -r $@ $<
$(APP_NAME): $(APP_SOURCES) $(LIB_SOURCES)
echo RESOURCES $(RESOURCES)
rm -rf $@
mkdir -p $@/Contents/MacOS
mkdir -p $@/Contents/Resources
cp examples/chat/app-resources/Info.plist $@/Contents
cp examples/chat/app-resources/boot.sh $@/Contents/MacOS
cp examples/chat/app-resources/app.icns $@/Contents/Resources
cp -r third-party $@/Contents/Resources
cp $(LIB_SOURCES) $@/Contents/Resources
mkdir -p $@/Contents/Resources/examples/chat
cp $(APP_SOURCES) $@/Contents/Resources/examples/chat
chmod a+x $@/Contents/MacOS/boot.sh
clean:
rm -rf $(APP_NAME) $(APP_NAME).zip

View File

@ -14,7 +14,7 @@ To install the Racket server:
To run the Racket server: To run the Racket server:
- `racket server.rkt` from the base directory of this repository. racket -l minimart/examples/broker
The Racket server listens for tunnelled Network Calculus events via The Racket server listens for tunnelled Network Calculus events via
websocket on ports 8000 (HTTP) and 8443 (HTTPS, if you have a websocket on ports 8000 (HTTP) and 8443 (HTTPS, if you have a

1
dist/README.md vendored Normal file
View File

@ -0,0 +1 @@
Directory for build products, checked in to the repo for ease-of-use.

2650
dist/minimart.js vendored Normal file

File diff suppressed because one or more lines are too long

2
dist/minimart.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -3,33 +3,15 @@
<head> <head>
<title>JS Marketplace: Chat Example</title> <title>JS Marketplace: Chat Example</title>
<meta charset="utf-8"> <meta charset="utf-8">
<link href="../../third-party/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <link href="../../third-party/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="../../third-party/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet"> <link href="../../third-party/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet">
<link href="style.css" rel="stylesheet"> <link href="style.css" rel="stylesheet">
<!-- <script src="../../third-party/bootstrap/js/bootstrap.min.js"></script> -->
<script src="../../third-party/jquery-2.0.3.min.js"></script> <script src="../../third-party/jquery-2.0.3.min.js"></script>
<script src="../../dist/minimart.js"></script>
<script src="../../route.js"></script>
<script src="../../marketplace.js"></script>
<script src="../../spy.js"></script>
<script src="../../dom-driver.js"></script>
<script src="../../routing-table-widget.js"></script>
<link href="../../routing-table-widget.css" rel="stylesheet">
<script src="../../jquery-driver.js"></script>
<script src="../../wake-detector.js"></script>
<script src="../../websocket-driver.js"></script>
<script src="index.js"></script> <script src="index.js"></script>
</head> </head>
<body> <body>
<div class="container-fluid"> <div class="container-fluid">
<!-- <div class="row-fluid"> -->
<!-- <div class="span12"> -->
<!-- <h1>JS Marketplace</h1> -->
<!-- </div> -->
<!-- </div> -->
<div class="row-fluid"> <div class="row-fluid">
<div class="span12"> <div class="span12">
<form class="form-horizontal" name="nym_form"> <form class="form-horizontal" name="nym_form">

View File

@ -1,3 +1,10 @@
var Route = Minimart.Route;
var World = Minimart.World;
var sub = Minimart.sub;
var pub = Minimart.pub;
var __ = Minimart.__;
var _$ = Minimart._$;
function chatEvent(nym, status, utterance, stamp) { function chatEvent(nym, status, utterance, stamp) {
return ["chatEvent", nym, status, utterance, stamp || +(new Date())]; return ["chatEvent", nym, status, utterance, stamp || +(new Date())];
} }
@ -18,7 +25,7 @@ function outputItem(item) {
function updateNymList(g) { function updateNymList(g) {
var statuses = {}; var statuses = {};
var nymProj = ["broker", 0, ["chatEvent", _$, _$, __, __]]; var nymProj = ["broker", 0, ["chatEvent", _$, _$, __, __]];
var matchedNyms = route.matcherKeys(g.project(route.compileProjection(nymProj), true, 0, 0)); var matchedNyms = Route.matcherKeys(g.project(Route.compileProjection(nymProj), true, 0, 0));
for (var i = 0; i < matchedNyms.length; i++) { for (var i = 0; i < matchedNyms.length; i++) {
statuses[matchedNyms[i][0]] = matchedNyms[i][1]; statuses[matchedNyms[i][0]] = matchedNyms[i][1];
} }
@ -55,15 +62,15 @@ $(document).ready(function () {
$("#nym_form").submit(function (e) { e.preventDefault(); return false; }); $("#nym_form").submit(function (e) { e.preventDefault(); return false; });
if (!($("#nym").val())) { $("#nym").val("nym" + Math.floor(Math.random() * 65536)); } if (!($("#nym").val())) { $("#nym").val("nym" + Math.floor(Math.random() * 65536)); }
G = new Ground(function () { G = new Minimart.Ground(function () {
console.log('starting ground boot'); console.log('starting ground boot');
// World.spawn(new Spy()); // World.spawn(new Spy());
spawnJQueryDriver(); Minimart.JQuery.spawnJQueryDriver();
spawnDOMDriver(); Minimart.DOM.spawnDOMDriver();
spawnRoutingTableWidget("#spy-holder", "spy"); Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy");
World.spawn(new WakeDetector()); World.spawn(new Minimart.WakeDetector());
var wsconn = new WebSocketConnection("broker", $("#wsurl").val(), true); var wsconn = new Minimart.WebSocket.WebSocketConnection("broker", $("#wsurl").val(), true);
World.spawn(wsconn); World.spawn(wsconn);
World.spawn({ World.spawn({
// Monitor connection, notifying connectivity changes // Monitor connection, notifying connectivity changes
@ -74,7 +81,7 @@ $(document).ready(function () {
handleEvent: function (e) { handleEvent: function (e) {
if (e.type === "routes") { if (e.type === "routes") {
var states = var states =
route.matcherKeys(e.gestalt.project(route.compileProjection([__, _$]), Route.matcherKeys(e.gestalt.project(Route.compileProjection([__, _$]),
true, 0, 0)); true, 0, 0));
var newState = states.length > 0 ? states[0][0] : "crashed"; var newState = states.length > 0 ? states[0][0] : "crashed";
if (this.state != newState) { if (this.state != newState) {

View File

@ -3,24 +3,11 @@
<head> <head>
<title>JS Marketplace: DOM Example</title> <title>JS Marketplace: DOM Example</title>
<meta charset="utf-8"> <meta charset="utf-8">
<link href="../../third-party/bootstrap/css/bootstrap.min.css" rel="stylesheet"> <link href="../../third-party/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<link href="../../third-party/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet"> <link href="../../third-party/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet">
<link href="style.css" rel="stylesheet"> <link href="style.css" rel="stylesheet">
<!-- <script src="../../third-party/bootstrap/js/bootstrap.min.js"></script> -->
<script src="../../third-party/jquery-2.0.3.min.js"></script> <script src="../../third-party/jquery-2.0.3.min.js"></script>
<script src="../../dist/minimart.js"></script>
<script src="../../route.js"></script>
<script src="../../marketplace.js"></script>
<script src="../../spy.js"></script>
<script src="../../dom-driver.js"></script>
<script src="../../routing-table-widget.js"></script>
<link href="../../routing-table-widget.css" rel="stylesheet">
<script src="../../jquery-driver.js"></script>
<script src="../../wake-detector.js"></script>
<script src="../../websocket-driver.js"></script>
<script src="index.js"></script> <script src="index.js"></script>
</head> </head>
<body> <body>

View File

@ -1,10 +1,16 @@
var G; var G;
$(document).ready(function () { $(document).ready(function () {
G = new Ground(function () { var World = Minimart.World;
var sub = Minimart.sub;
var pub = Minimart.pub;
var __ = Minimart.__;
var _$ = Minimart._$;
G = new Minimart.Ground(function () {
console.log('starting ground boot'); console.log('starting ground boot');
// World.spawn(new Spy("GROUND", true)); // World.spawn(new Spy("GROUND", true));
spawnDOMDriver(); Minimart.DOM.spawnDOMDriver();
spawnRoutingTableWidget("#spy-holder", "spy"); Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy");
World.spawn({ World.spawn({
handleEvent: function (e) { handleEvent: function (e) {

View File

@ -4,9 +4,7 @@
<title>JS Marketplace: Smoketest</title> <title>JS Marketplace: Smoketest</title>
<meta charset="utf-8"> <meta charset="utf-8">
<script src="../../third-party/jquery-2.0.3.min.js"></script> <script src="../../third-party/jquery-2.0.3.min.js"></script>
<script src="../../route.js"></script> <script src="../../dist/minimart.js"></script>
<script src="../../marketplace.js"></script>
<script src="../../spy.js"></script>
<script src="index.js"></script> <script src="index.js"></script>
</head> </head>
<body> <body>

View File

@ -1,8 +1,14 @@
var G; var G;
$(document).ready(function () { $(document).ready(function () {
G = new Ground(function () { var World = Minimart.World;
var sub = Minimart.sub;
var pub = Minimart.pub;
var __ = Minimart.__;
var _$ = Minimart._$;
G = new Minimart.Ground(function () {
console.log('starting ground boot'); console.log('starting ground boot');
World.spawn(new Spy("GROUND", true)); World.spawn(new Minimart.Spy("GROUND", true));
World.spawn({ World.spawn({
counter: 0, counter: 0,
handleEvent: function (e) {}, handleEvent: function (e) {},

View File

@ -9,16 +9,7 @@
<link href="style.css" rel="stylesheet"> <link href="style.css" rel="stylesheet">
<script src="../../third-party/jquery-2.0.3.min.js"></script> <script src="../../third-party/jquery-2.0.3.min.js"></script>
<script src="../../route.js"></script> <script src="../../dist/minimart.js"></script>
<script src="../../marketplace.js"></script>
<script src="../../spy.js"></script>
<script src="../../dom-driver.js"></script>
<script src="../../routing-table-widget.js"></script>
<link href="../../routing-table-widget.css" rel="stylesheet">
<script src="../../jquery-driver.js"></script>
<script src="../../wake-detector.js"></script>
<script src="index.js"></script> <script src="index.js"></script>
</head> </head>
<body> <body>

View File

@ -1,6 +1,13 @@
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// GUI // GUI
var Route = Minimart.Route;
var World = Minimart.World;
var sub = Minimart.sub;
var pub = Minimart.pub;
var __ = Minimart.__;
var _$ = Minimart._$;
function piece(text, pos, lo, hi, cls) { function piece(text, pos, lo, hi, cls) {
return "<span class='"+cls+"'>"+ return "<span class='"+cls+"'>"+
((pos >= lo && pos < hi) ((pos >= lo && pos < hi)
@ -16,19 +23,19 @@ function spawnGui() {
sub(["fieldContents", __, __], 0, 1), sub(["fieldContents", __, __], 0, 1),
sub(["highlight", __], 0, 1)]); sub(["highlight", __], 0, 1)]);
}, },
fieldContentsSpec: route.compileProjection(["fieldContents", _$, _$]), fieldContentsSpec: Route.compileProjection(["fieldContents", _$, _$]),
highlightSpec: route.compileProjection(["highlight", _$]), highlightSpec: Route.compileProjection(["highlight", _$]),
handleEvent: function (e) { handleEvent: function (e) {
switch (e.type) { switch (e.type) {
case "routes": case "routes":
var text = "", pos = 0, highlight = false; var text = "", pos = 0, highlight = false;
// BUG: escape text! // BUG: escape text!
var fc = route.matcherKeys(e.gestalt.project(this.fieldContentsSpec, true)); var fc = Route.matcherKeys(e.gestalt.project(this.fieldContentsSpec, true));
if (fc.length > 0) { if (fc.length > 0) {
text = fc[0][0]; text = fc[0][0];
pos = fc[0][1]; pos = fc[0][1];
} }
var hl = route.matcherKeys(e.gestalt.project(this.highlightSpec, true)); var hl = Route.matcherKeys(e.gestalt.project(this.highlightSpec, true));
if (hl.length > 0) { if (hl.length > 0) {
highlight = hl[0][0]; highlight = hl[0][0];
} }
@ -112,7 +119,7 @@ function spawnSearch() {
World.spawn({ World.spawn({
fieldContents: "", fieldContents: "",
highlight: false, highlight: false,
fieldContentsSpec: route.compileProjection(["fieldContents", _$, _$]), fieldContentsSpec: Route.compileProjection(["fieldContents", _$, _$]),
boot: function () { boot: function () {
World.updateRoutes(this.subscriptions()); World.updateRoutes(this.subscriptions());
}, },
@ -137,7 +144,7 @@ function spawnSearch() {
handleEvent: function (e) { handleEvent: function (e) {
switch (e.type) { switch (e.type) {
case "routes": case "routes":
var fc = route.matcherKeys(e.gestalt.project(this.fieldContentsSpec, true)); var fc = Route.matcherKeys(e.gestalt.project(this.fieldContentsSpec, true));
if (fc.length > 0) { if (fc.length > 0) {
this.fieldContents = fc[0][0]; this.fieldContents = fc[0][0];
} }
@ -157,10 +164,10 @@ function spawnSearch() {
var G; var G;
$(document).ready(function () { $(document).ready(function () {
G = new Ground(function () { G = new Minimart.Ground(function () {
spawnJQueryDriver(); Minimart.JQuery.spawnJQueryDriver();
spawnDOMDriver(); Minimart.DOM.spawnDOMDriver();
spawnRoutingTableWidget("#spy-holder", "spy"); Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy");
spawnGui(); spawnGui();
spawnModel(); spawnModel();

22
package.json Normal file
View File

@ -0,0 +1,22 @@
{
"name": "js-marketplace",
"version": "0.0.0",
"description": "Network Calculus in the browser",
"homepage": "https://github.com/tonyg/js-marketplace",
"main": "src/main.js",
"scripts": {
"clean": "rm -f dist/*",
"build-debug": "browserify src/main.js -d -s Minimart -o dist/minimart.js",
"build-min": "browserify src/main.js -s Minimart -o dist/_minimart.js && uglifyjs dist/_minimart.js -o dist/minimart.min.js && rm dist/_minimart.js",
"build": "npm run build-debug && npm run build-min",
"watch": "watchify src/main.js -d -s Minimart -o dist/minimart.js",
"prepublish": "npm run build"
},
"author": "Tony Garnock-Jones <tonyg@ccs.neu.edu>",
"devDependencies": {
"watchify": "^0.6.1",
"uglify-js": "^2.4.12",
"browserify": "^3.30.4",
"mocha": "^1.17.1"
}
}

1531
route.js

File diff suppressed because it is too large Load Diff

View File

@ -1,49 +0,0 @@
.routing-table li { list-style-type: none; }
.routing-table .sub .pattern { background-color: lightblue; }
.routing-table .sub .level { background-color: lightblue; }
.routing-table .pub .pattern { background-color: lightgreen; }
.routing-table .pub .level { background-color: lightgreen; }
.routing-table .route {
display: inline-block;
height: 2em;
}
.routing-table .route .level { font-style: italic; line-height: 1em; padding: 0 0.5em; }
.routing-table .route .polarity { display: none; }
.routing-table .route .pattern { padding-right: 0.5em; }
.routing-table .route:before {
content: " ";
float: left;
}
.routing-table .pub:before {
border-right: 0.6em solid lightgreen;
border-top: 0.75em solid transparent;
border-bottom: 0.75em solid transparent;
}
.routing-table .sub:before {
border-left: 0.6em solid transparent;
border-top: 0.75em solid lightblue;
border-bottom: 0.75em solid lightblue;
}
.routing-table .route:after {
content: " ";
float: right;
}
.routing-table .pub:after {
border-left: 0.6em solid lightgreen;
border-top: 0.75em solid transparent;
border-bottom: 0.75em solid transparent;
}
.routing-table .sub:after {
border-right: 0.6em solid transparent;
border-top: 0.75em solid lightblue;
border-bottom: 0.75em solid lightblue;
}

View File

@ -1,7 +1,13 @@
// DOM fragment display driver // DOM fragment display driver
var Minimart = require("./minimart.js");
var World = Minimart.World;
var sub = Minimart.sub;
var pub = Minimart.pub;
var __ = Minimart.__;
var _$ = Minimart._$;
function spawnDOMDriver() { function spawnDOMDriver() {
var d = new DemandMatcher(["DOM", _$, _$, _$]); var d = new Minimart.DemandMatcher(["DOM", _$, _$, _$]);
d.onDemandIncrease = function (captures) { d.onDemandIncrease = function (captures) {
var selector = captures[0]; var selector = captures[0];
var fragmentClass = captures[1]; var fragmentClass = captures[1];
@ -24,7 +30,7 @@ DOMFragment.prototype.boot = function () {
var self = this; var self = this;
var monitoring = sub(["DOM", self.selector, self.fragmentClass, self.fragmentSpec], 1, 2); var monitoring = sub(["DOM", self.selector, self.fragmentClass, self.fragmentSpec], 1, 2);
World.spawn(new World(function () { World.spawn(new World(function () {
spawnJQueryDriver(self.selector+" > ."+self.fragmentClass, 1); Minimart.JQuery.spawnJQueryDriver(self.selector+" > ."+self.fragmentClass, 1);
World.spawn({ World.spawn({
handleEvent: function (e) { handleEvent: function (e) {
if (e.type === "routes") { if (e.type === "routes") {
@ -86,3 +92,7 @@ DOMFragment.prototype.buildNodes = function () {
}); });
return nodes; return nodes;
}; };
///////////////////////////////////////////////////////////////////////////
module.exports.spawnDOMDriver = spawnDOMDriver;

View File

@ -1,8 +1,15 @@
// JQuery event driver // JQuery event driver
var Minimart = require("./minimart.js");
var World = Minimart.World;
var sub = Minimart.sub;
var pub = Minimart.pub;
var __ = Minimart.__;
var _$ = Minimart._$;
function spawnJQueryDriver(baseSelector, metaLevel) { function spawnJQueryDriver(baseSelector, metaLevel) {
metaLevel = metaLevel || 0; metaLevel = metaLevel || 0;
var d = new DemandMatcher(["jQuery", _$, _$, __], metaLevel, {demandSideIsSubscription: true}); var d = new Minimart.DemandMatcher(["jQuery", _$, _$, __], metaLevel,
{demandSideIsSubscription: true});
d.onDemandIncrease = function (captures) { d.onDemandIncrease = function (captures) {
var selector = captures[0]; var selector = captures[0];
var eventName = captures[1]; var eventName = captures[1];
@ -44,3 +51,7 @@ JQueryEventRouter.prototype.computeNodes = function () {
return $(this.selector); return $(this.selector);
} }
}; };
///////////////////////////////////////////////////////////////////////////
module.exports.spawnJQueryDriver = spawnJQueryDriver;

9
src/main.js Normal file
View File

@ -0,0 +1,9 @@
module.exports = require("./minimart.js");
module.exports.DOM = require("./dom-driver.js");
module.exports.JQuery = require("./jquery-driver.js");
module.exports.RoutingTableWidget = require("./routing-table-widget.js");
module.exports.WebSocket = require("./websocket-driver.js");
module.exports.Spy = require("./spy.js").Spy;
module.exports.WakeDetector = require("./wake-detector.js").WakeDetector;

View File

@ -1,27 +1,31 @@
var Route = require("./route.js");
///////////////////////////////////////////////////////////////////////////
// TODO: trigger-guards as per minimart // TODO: trigger-guards as per minimart
/*---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------*/
/* Events and Actions */ /* Events and Actions */
var __ = route.__; var __ = Route.__;
var _$ = route._$; var _$ = Route._$;
function sub(pattern, metaLevel, level) { function sub(pattern, metaLevel, level) {
return route.simpleGestalt(false, pattern, metaLevel, level); return Route.simpleGestalt(false, pattern, metaLevel, level);
} }
function pub(pattern, metaLevel, level) { function pub(pattern, metaLevel, level) {
return route.simpleGestalt(true, pattern, metaLevel, level); return Route.simpleGestalt(true, pattern, metaLevel, level);
} }
function spawn(behavior, initialGestalts) { function spawn(behavior, initialGestalts) {
return { type: "spawn", return { type: "spawn",
behavior: behavior, behavior: behavior,
initialGestalt: route.gestaltUnion(initialGestalts || []) }; initialGestalt: Route.gestaltUnion(initialGestalts || []) };
} }
function updateRoutes(gestalts) { function updateRoutes(gestalts) {
return { type: "routes", gestalt: route.gestaltUnion(gestalts) }; return { type: "routes", gestalt: Route.gestaltUnion(gestalts) };
} }
function pendingRoutingUpdate(aggregate, affectedSubgestalt, knownTarget) { function pendingRoutingUpdate(aggregate, affectedSubgestalt, knownTarget) {
@ -49,11 +53,11 @@ function World(bootFn) {
this.alive = true; this.alive = true;
this.eventQueue = []; this.eventQueue = [];
this.runnablePids = {}; this.runnablePids = {};
this.partialGestalt = route.emptyGestalt; // Only gestalt from local processes this.partialGestalt = Route.emptyGestalt; // Only gestalt from local processes
this.fullGestalt = route.emptyGestalt ;; // partialGestalt unioned with downwardGestalt this.fullGestalt = Route.emptyGestalt ;; // partialGestalt unioned with downwardGestalt
this.processTable = {}; this.processTable = {};
this.tombstones = {}; this.tombstones = {};
this.downwardGestalt = route.emptyGestalt; this.downwardGestalt = Route.emptyGestalt;
this.processActions = []; this.processActions = [];
this.asChild(-1, bootFn, true); this.asChild(-1, bootFn, true);
} }
@ -134,7 +138,7 @@ World.prototype.enqueueAction = function (pid, action) {
World.prototype.isInert = function () { World.prototype.isInert = function () {
return this.eventQueue.length === 0 return this.eventQueue.length === 0
&& this.processActions.length === 0 && this.processActions.length === 0
&& route.is_emptySet(this.runnablePids); && Route.is_emptySet(this.runnablePids);
}; };
World.prototype.markPidRunnable = function (pid) { World.prototype.markPidRunnable = function (pid) {
@ -183,7 +187,7 @@ World.prototype.kill = function (pid, exn) {
p.exitReason = exn; p.exitReason = exn;
this.tombstones[pid] = p; this.tombstones[pid] = p;
} }
this.applyAndIssueRoutingUpdate(p.gestalt, route.emptyGestalt); this.applyAndIssueRoutingUpdate(p.gestalt, Route.emptyGestalt);
} }
}; };
@ -227,7 +231,7 @@ World.prototype.performAction = function (pid, action) {
this.asChild(pid, function () { action.behavior.boot() }); this.asChild(pid, function () { action.behavior.boot() });
this.markPidRunnable(pid); this.markPidRunnable(pid);
} }
this.applyAndIssueRoutingUpdate(route.emptyGestalt, newGestalt, pid); this.applyAndIssueRoutingUpdate(Route.emptyGestalt, newGestalt, pid);
break; break;
case "routes": case "routes":
if (pid in this.processTable) { if (pid in this.processTable) {
@ -391,8 +395,8 @@ function DemandMatcher(projection, metaLevel, options) {
supplyLevel: 0, supplyLevel: 0,
demandSideIsSubscription: false demandSideIsSubscription: false
}, options); }, options);
this.pattern = route.projectionToPattern(projection); this.pattern = Route.projectionToPattern(projection);
this.projectionSpec = route.compileProjection(projection); this.projectionSpec = Route.compileProjection(projection);
this.metaLevel = metaLevel | 0; this.metaLevel = metaLevel | 0;
this.demandLevel = options.demandLevel; this.demandLevel = options.demandLevel;
this.supplyLevel = options.supplyLevel; this.supplyLevel = options.supplyLevel;
@ -428,12 +432,12 @@ DemandMatcher.prototype.handleGestalt = function (gestalt) {
this.demandSideIsSubscription, this.demandSideIsSubscription,
this.metaLevel, this.metaLevel,
this.supplyLevel); this.supplyLevel);
var newDemand = route.arrayToSet(route.matcherKeys(newDemandMatcher)); var newDemand = Route.arrayToSet(Route.matcherKeys(newDemandMatcher));
var newSupply = route.arrayToSet(route.matcherKeys(newSupplyMatcher)); var newSupply = Route.arrayToSet(Route.matcherKeys(newSupplyMatcher));
var demandDelta = route.setSubtract(newDemand, this.currentDemand); var demandDelta = Route.setSubtract(newDemand, this.currentDemand);
var supplyDelta = route.setSubtract(this.currentSupply, newSupply); var supplyDelta = Route.setSubtract(this.currentSupply, newSupply);
var demandIncr = route.setSubtract(demandDelta, newSupply); var demandIncr = Route.setSubtract(demandDelta, newSupply);
var supplyDecr = route.setIntersect(supplyDelta, newDemand); var supplyDecr = Route.setIntersect(supplyDelta, newDemand);
this.currentDemand = newDemand; this.currentDemand = newDemand;
this.currentSupply = newSupply; this.currentSupply = newSupply;
for (var k in demandIncr) this.onDemandIncrease(demandIncr[k]); for (var k in demandIncr) this.onDemandIncrease(demandIncr[k]);
@ -533,3 +537,21 @@ Ground.prototype.enqueueAction = function (pid, action) {
console.error("You have sent a message into the outer void.", action); console.error("You have sent a message into the outer void.", action);
} }
}; };
///////////////////////////////////////////////////////////////////////////
module.exports.__ = __;
module.exports._$ = _$;
module.exports.sub = sub;
module.exports.pub = pub;
module.exports.spawn = spawn;
module.exports.updateRoutes = updateRoutes;
module.exports.sendMessage = sendMessage;
module.exports.shutdownWorld = shutdownWorld;
module.exports.World = World;
module.exports.DemandMatcher = DemandMatcher;
module.exports.Deduplicator = Deduplicator;
module.exports.Ground = Ground;
module.exports.Route = Route;

1519
src/route.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,11 @@
var Minimart = require("./minimart.js");
var Route = Minimart.Route;
var World = Minimart.World;
var sub = Minimart.sub;
var pub = Minimart.pub;
var __ = Minimart.__;
var _$ = Minimart._$;
function spawnRoutingTableWidget(selector, fragmentClass, observationLevel) { function spawnRoutingTableWidget(selector, fragmentClass, observationLevel) {
observationLevel = observationLevel || 10; observationLevel = observationLevel || 10;
// ^ arbitrary: should be Infinity, when route.js supports it. TODO // ^ arbitrary: should be Infinity, when route.js supports it. TODO
@ -5,8 +13,8 @@ function spawnRoutingTableWidget(selector, fragmentClass, observationLevel) {
World.spawn({ World.spawn({
boot: function () { this.updateState(); }, boot: function () { this.updateState(); },
state: route.emptyGestalt.serialize(), state: Route.emptyGestalt.serialize(),
nextState: route.emptyGestalt.serialize(), nextState: Route.emptyGestalt.serialize(),
timer: false, timer: false,
localGestalt: (sub( ["DOM", selector, fragmentClass, __], 0, 2) localGestalt: (sub( ["DOM", selector, fragmentClass, __], 0, 2)
@ -18,8 +26,7 @@ function spawnRoutingTableWidget(selector, fragmentClass, observationLevel) {
}, },
updateState: function () { updateState: function () {
var elts = ["ul", {"class": "routing-table"}, var elts = ["pre", Route.deserializeGestalt(this.state).pretty()];
["li", ["pre", route.deserializeGestalt(this.state).pretty()]]];
World.updateRoutes([sub(__, 0, observationLevel), World.updateRoutes([sub(__, 0, observationLevel),
pub(__, 0, observationLevel), pub(__, 0, observationLevel),
pub(["DOM", selector, fragmentClass, elts])]); pub(["DOM", selector, fragmentClass, elts])]);
@ -45,3 +52,5 @@ function spawnRoutingTableWidget(selector, fragmentClass, observationLevel) {
}); });
} }
module.exports.spawnRoutingTableWidget = spawnRoutingTableWidget;

View File

@ -1,4 +1,10 @@
// Generic Spy // Generic Spy
var Minimart = require("./minimart.js");
var World = Minimart.World;
var sub = Minimart.sub;
var pub = Minimart.pub;
var __ = Minimart.__;
var _$ = Minimart._$;
function Spy(label, useJson, observationLevel) { function Spy(label, useJson, observationLevel) {
this.label = label || "SPY"; this.label = label || "SPY";
@ -29,3 +35,5 @@ Spy.prototype.handleEvent = function (e) {
break; break;
} }
}; };
module.exports.Spy = Spy;

View File

@ -2,6 +2,12 @@
// suspension/sleeping!) has caused periodic activities to be // suspension/sleeping!) has caused periodic activities to be
// interrupted, and warns others about it // interrupted, and warns others about it
// Inspired by http://blog.alexmaccaw.com/javascript-wake-event // Inspired by http://blog.alexmaccaw.com/javascript-wake-event
var Minimart = require("./minimart.js");
var World = Minimart.World;
var sub = Minimart.sub;
var pub = Minimart.pub;
var __ = Minimart.__;
var _$ = Minimart._$;
function WakeDetector(period) { function WakeDetector(period) {
this.message = "wake"; this.message = "wake";
@ -25,3 +31,5 @@ WakeDetector.prototype.trigger = function () {
} }
this.mostRecentTrigger = now; this.mostRecentTrigger = now;
}; };
module.exports.WakeDetector = WakeDetector;

View File

@ -1,3 +1,11 @@
var Minimart = require("./minimart.js");
var Route = Minimart.Route;
var World = Minimart.World;
var sub = Minimart.sub;
var pub = Minimart.pub;
var __ = Minimart.__;
var _$ = Minimart._$;
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// WebSocket client driver // WebSocket client driver
@ -15,11 +23,11 @@ function WebSocketConnection(label, wsurl, shouldReconnect) {
this.wsurl = wsurl; this.wsurl = wsurl;
this.shouldReconnect = shouldReconnect ? true : false; this.shouldReconnect = shouldReconnect ? true : false;
this.reconnectDelay = DEFAULT_RECONNECT_DELAY; this.reconnectDelay = DEFAULT_RECONNECT_DELAY;
this.localGestalt = route.emptyGestalt; this.localGestalt = Route.emptyGestalt;
this.peerGestalt = route.emptyGestalt; this.peerGestalt = Route.emptyGestalt;
this.prevLocalRoutesMessage = null; this.prevLocalRoutesMessage = null;
this.prevPeerRoutesMessage = null; this.prevPeerRoutesMessage = null;
this.deduplicator = new Deduplicator(); this.deduplicator = new Minimart.Deduplicator();
this.connectionCount = 0; this.connectionCount = 0;
this.activityTimestamp = 0; this.activityTimestamp = 0;
@ -58,8 +66,8 @@ WebSocketConnection.prototype.relayGestalt = function () {
WebSocketConnection.prototype.aggregateGestalt = function () { WebSocketConnection.prototype.aggregateGestalt = function () {
var self = this; var self = this;
return this.peerGestalt.transform(function (m, metaLevel) { return this.peerGestalt.transform(function (m, metaLevel) {
return route.compilePattern(true, return Route.compilePattern(true,
[self.label, metaLevel, route.embeddedMatcher(m)]); [self.label, metaLevel, Route.embeddedMatcher(m)]);
}).union(this.relayGestalt()); }).union(this.relayGestalt());
}; };
@ -88,7 +96,8 @@ WebSocketConnection.prototype.safeSend = function (m) {
}; };
WebSocketConnection.prototype.sendLocalRoutes = function () { WebSocketConnection.prototype.sendLocalRoutes = function () {
var newLocalRoutesMessage = JSON.stringify(encodeEvent(updateRoutes([this.localGestalt]))); var newLocalRoutesMessage =
JSON.stringify(encodeEvent(Minimart.updateRoutes([this.localGestalt])));
if (this.prevLocalRoutesMessage !== newLocalRoutesMessage) { if (this.prevLocalRoutesMessage !== newLocalRoutesMessage) {
this.prevLocalRoutesMessage = newLocalRoutesMessage; this.prevLocalRoutesMessage = newLocalRoutesMessage;
this.safeSend(newLocalRoutesMessage); this.safeSend(newLocalRoutesMessage);
@ -96,14 +105,14 @@ WebSocketConnection.prototype.sendLocalRoutes = function () {
}; };
WebSocketConnection.prototype.collectMatchers = function (getAdvertisements, level, g) { WebSocketConnection.prototype.collectMatchers = function (getAdvertisements, level, g) {
var extractMetaLevels = route.compileProjection([this.label, _$, __]); var extractMetaLevels = Route.compileProjection([this.label, _$, __]);
var mls = route.matcherKeys(g.project(extractMetaLevels, getAdvertisements, 0, level)); var mls = Route.matcherKeys(g.project(extractMetaLevels, getAdvertisements, 0, level));
for (var i = 0; i < mls.length; i++) { for (var i = 0; i < mls.length; i++) {
var metaLevel = mls[i][0]; // only one capture in the projection var metaLevel = mls[i][0]; // only one capture in the projection
var extractMatchers = route.compileProjection([this.label, metaLevel, _$]); var extractMatchers = Route.compileProjection([this.label, metaLevel, _$]);
var m = g.project(extractMatchers, getAdvertisements, 0, level); var m = g.project(extractMatchers, getAdvertisements, 0, level);
this.localGestalt = this.localGestalt.union(route.simpleGestalt(getAdvertisements, this.localGestalt = this.localGestalt.union(Route.simpleGestalt(getAdvertisements,
route.embeddedMatcher(m), Route.embeddedMatcher(m),
metaLevel, metaLevel,
level)); level));
} }
@ -115,9 +124,9 @@ WebSocketConnection.prototype.handleEvent = function (e) {
case "routes": case "routes":
// TODO: GROSS - erasing by pid! // TODO: GROSS - erasing by pid!
var nLevels = e.gestalt.levelCount(0); var nLevels = e.gestalt.levelCount(0);
var relayGestalt = route.fullGestalt(1, nLevels).label(World.activePid()); var relayGestalt = Route.fullGestalt(1, nLevels).label(World.activePid());
var g = e.gestalt.erasePath(relayGestalt); var g = e.gestalt.erasePath(relayGestalt);
this.localGestalt = route.emptyGestalt; this.localGestalt = Route.emptyGestalt;
for (var level = 0; level < nLevels; level++) { for (var level = 0; level < nLevels; level++) {
this.collectMatchers(false, level, g); this.collectMatchers(false, level, g);
this.collectMatchers(true, level, g); this.collectMatchers(true, level, g);
@ -129,7 +138,8 @@ WebSocketConnection.prototype.handleEvent = function (e) {
var m = e.message; var m = e.message;
if (m.length && m.length === 3 && m[0] === this.label) if (m.length && m.length === 3 && m[0] === this.label)
{ {
var encoded = JSON.stringify(encodeEvent(sendMessage(m[2], m[1], e.isFeedback))); var encoded = JSON.stringify(encodeEvent(
Minimart.sendMessage(m[2], m[1], e.isFeedback)));
if (this.deduplicator.accept(encoded)) { if (this.deduplicator.accept(encoded)) {
this.safeSend(encoded); this.safeSend(encoded);
} }
@ -232,10 +242,17 @@ function encodeEvent(e) {
function decodeAction(j) { function decodeAction(j) {
switch (j[0]) { switch (j[0]) {
case "routes": case "routes":
return updateRoutes([route.deserializeGestalt(j[1], function (v) { return true; })]); return Minimart.updateRoutes([
Route.deserializeGestalt(j[1], function (v) { return true; })]);
case "message": case "message":
return sendMessage(j[1], j[2], j[3]); return Minimart.sendMessage(j[1], j[2], j[3]);
default: default:
throw { message: "Invalid JSON-encoded action: " + JSON.stringify(j) }; throw { message: "Invalid JSON-encoded action: " + JSON.stringify(j) };
} }
} }
///////////////////////////////////////////////////////////////////////////
module.exports.WebSocketConnection = WebSocketConnection;
module.exports.encodeEvent = encodeEvent;
module.exports.decodeAction = decodeAction;

View File