Make boot() return (optional) initialGestalts instead of having a separate argument to spawn(). Fixes failing test case for initial actor route signalling.

This commit is contained in:
Tony Garnock-Jones 2014-08-30 14:51:42 -07:00
parent 85c6c228a3
commit 5621685052
12 changed files with 99 additions and 53 deletions

View File

@ -76,7 +76,7 @@ $(document).ready(function () {
// Monitor connection, notifying connectivity changes // Monitor connection, notifying connectivity changes
state: "crashed", // start with this to avoid spurious initial message print state: "crashed", // start with this to avoid spurious initial message print
boot: function () { boot: function () {
World.updateRoutes([sub(["broker_state", __], 0, 1)]); return [sub(["broker_state", __], 0, 1)];
}, },
handleEvent: function (e) { handleEvent: function (e) {
if (e.type === "routes") { if (e.type === "routes") {
@ -94,7 +94,7 @@ $(document).ready(function () {
World.spawn({ World.spawn({
// Actual chat functionality // Actual chat functionality
boot: function () { boot: function () {
World.updateRoutes(this.subscriptions()); return this.subscriptions();
}, },
nym: function () { return $("#nym").val(); }, nym: function () { return $("#nym").val(); },
currentStatus: function () { return $("#status").val(); }, currentStatus: function () { return $("#status").val(); },

View File

@ -13,15 +13,18 @@ $(document).ready(function () {
Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy"); Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy");
World.spawn({ World.spawn({
boot: function () {
return [pub(["DOM", "#clicker-holder", "clicker",
["button", ["span", [["style", "font-style: italic"]], "Click me!"]]]),
pub("bump_count"),
sub(["jQuery", "button.clicker", "click", __])];
},
handleEvent: function (e) { handleEvent: function (e) {
if (e.type === "message" && e.message[0] === "jQuery") { if (e.type === "message" && e.message[0] === "jQuery") {
World.send("bump_count"); World.send("bump_count");
} }
} }
}, [pub(["DOM", "#clicker-holder", "clicker", });
["button", ["span", [["style", "font-style: italic"]], "Click me!"]]]),
pub("bump_count"),
sub(["jQuery", "button.clicker", "click", __])]);
World.spawn({ World.spawn({
counter: 0, counter: 0,

View File

@ -29,6 +29,13 @@ $(document).ready(function () {
Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy"); // local Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy"); // local
World.spawn({ World.spawn({
boot: function () {
return [pub(domWrap("#clicker-holder", localId + "-clicker",
["button", ["span", [["style", "font-style: italic"]],
"Click me! (" + localId + ")"]])),
pub("bump_count"),
sub(jQueryWrap("button."+localId+"-clicker", "click", __))];
},
handleEvent: function (e) { handleEvent: function (e) {
console.log(JSON.stringify(e)); console.log(JSON.stringify(e));
if (e.type === "message" if (e.type === "message"
@ -40,11 +47,7 @@ $(document).ready(function () {
World.send("bump_count"); World.send("bump_count");
} }
} }
}, [pub(domWrap("#clicker-holder", localId + "-clicker", });
["button", ["span", [["style", "font-style: italic"]],
"Click me! (" + localId + ")"]])),
pub("bump_count"),
sub(jQueryWrap("button."+localId+"-clicker", "click", __))]);
World.spawn({ World.spawn({
counter: 0, counter: 0,

View File

@ -12,6 +12,9 @@ $(document).ready(function () {
World.spawn({ World.spawn({
name: 'GestaltDisplay', name: 'GestaltDisplay',
boot: function () {
return [sub(__, 0, 10), pub(__, 0, 10)];
},
handleEvent: function (e) { handleEvent: function (e) {
if (e.type === "routes") { if (e.type === "routes") {
var gd = document.getElementById('gestalt-display'); var gd = document.getElementById('gestalt-display');
@ -21,7 +24,7 @@ $(document).ready(function () {
gd.appendChild(t); gd.appendChild(t);
} }
} }
}, [sub(__, 0, 10), pub(__, 0, 10)]); });
World.spawn(new Actor(function () { World.spawn(new Actor(function () {
this.counter = 0; this.counter = 0;

View File

@ -13,8 +13,9 @@ function Actor(bootfn) {
try { try {
Actor._chunks = []; Actor._chunks = [];
bootfn.call(this); bootfn.call(this);
finalizeActor(this, Actor._chunks); var initialGestalt = finalizeActor(this, Actor._chunks);
Actor._chunks = oldChunks; Actor._chunks = oldChunks;
return [initialGestalt];
} catch (e) { } catch (e) {
Actor._chunks = oldChunks; Actor._chunks = oldChunks;
throw e; throw e;
@ -123,7 +124,7 @@ function finalizeActor(behavior, chunks) {
var compiledProjections = {}; var compiledProjections = {};
var previousObjs = {}; var previousObjs = {};
behavior.updateRoutes = function () { behavior._computeRoutes = function () {
var newRoutes = Route.emptyGestalt; var newRoutes = Route.emptyGestalt;
for (var i = 0; i < chunks.length; i++) { for (var i = 0; i < chunks.length; i++) {
var chunk = chunks[i]; var chunk = chunks[i];
@ -159,7 +160,11 @@ function finalizeActor(behavior, chunks) {
} }
} }
} }
World.updateRoutes([newRoutes]); return newRoutes;
};
behavior.updateRoutes = function () {
World.updateRoutes([this._computeRoutes()]);
}; };
behavior.handleEvent = function (e) { behavior.handleEvent = function (e) {
@ -254,7 +259,8 @@ function finalizeActor(behavior, chunks) {
if (chunk.options.removed) { behavior[chunk.options.removed] = []; } if (chunk.options.removed) { behavior[chunk.options.removed] = []; }
} }
} }
behavior.updateRoutes();
return behavior._computeRoutes();
} }
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////

View File

@ -17,9 +17,7 @@ function spawnDOMDriver(domWrapFunction, jQueryWrapFunction) {
fragmentClass, fragmentClass,
fragmentSpec, fragmentSpec,
domWrapFunction, domWrapFunction,
jQueryWrapFunction), jQueryWrapFunction));
[sub(domWrapFunction(selector, fragmentClass, fragmentSpec)),
sub(domWrapFunction(selector, fragmentClass, fragmentSpec), 0, 1)]);
}; };
World.spawn(d); World.spawn(d);
} }
@ -40,12 +38,14 @@ function DOMFragment(selector, fragmentClass, fragmentSpec, domWrapFunction, jQu
DOMFragment.prototype.boot = function () { DOMFragment.prototype.boot = function () {
var self = this; var self = this;
var monitoring = var monitoring =
sub(this.domWrapFunction(self.selector, self.fragmentClass, self.fragmentSpec), 1, 2); sub(self.domWrapFunction(self.selector, self.fragmentClass, self.fragmentSpec), 1, 2);
World.spawn(new World(function () { World.spawn(new World(function () {
Minimart.JQuery.spawnJQueryDriver(self.selector+" > ."+self.fragmentClass, Minimart.JQuery.spawnJQueryDriver(self.selector+" > ."+self.fragmentClass,
1, 1,
self.jQueryWrapFunction); self.jQueryWrapFunction);
World.spawn({ World.spawn({
boot: function () { return [monitoring] },
handleEvent: function (e) { handleEvent: function (e) {
if (e.type === "routes") { if (e.type === "routes") {
var level = e.gestalt.getLevel(1, 0); // find participant peers var level = e.gestalt.getLevel(1, 0); // find participant peers
@ -54,8 +54,11 @@ DOMFragment.prototype.boot = function () {
} }
} }
} }
}, [monitoring]); });
})); }));
return [sub(self.domWrapFunction(self.selector, self.fragmentClass, self.fragmentSpec)),
sub(self.domWrapFunction(self.selector, self.fragmentClass, self.fragmentSpec), 0, 1)]
}; };
DOMFragment.prototype.handleEvent = function (e) { DOMFragment.prototype.handleEvent = function (e) {

View File

@ -18,9 +18,7 @@ function spawnJQueryDriver(baseSelector, metaLevel, wrapFunction) {
selector, selector,
eventName, eventName,
metaLevel, metaLevel,
wrapFunction), wrapFunction));
[pub(wrapFunction(selector, eventName, __), metaLevel),
pub(wrapFunction(selector, eventName, __), metaLevel, 1)]);
}; };
World.spawn(d); World.spawn(d);
} }
@ -47,6 +45,11 @@ function JQueryEventRouter(baseSelector, selector, eventName, metaLevel, wrapFun
this.handler); this.handler);
} }
JQueryEventRouter.prototype.boot = function () {
return [pub(this.wrapFunction(this.selector, this.eventName, __), this.metaLevel),
pub(this.wrapFunction(this.selector, this.eventName, __), this.metaLevel, 1)];
};
JQueryEventRouter.prototype.handleEvent = function (e) { JQueryEventRouter.prototype.handleEvent = function (e) {
if (e.type === "routes" && e.gestalt.isEmpty()) { if (e.type === "routes" && e.gestalt.isEmpty()) {
this.computeNodes().off(this.eventName, this.handler); this.computeNodes().off(this.eventName, this.handler);

View File

@ -19,10 +19,8 @@ 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) {
return { type: "spawn", return { type: "spawn", behavior: behavior };
behavior: behavior,
initialGestalt: Route.gestaltUnion(initialGestalts || []) };
} }
function updateRoutes(gestalts) { function updateRoutes(gestalts) {
@ -85,8 +83,8 @@ World.updateRoutes = function (gestalts) {
World.current().enqueueAction(World.activePid(), updateRoutes(gestalts)); World.current().enqueueAction(World.activePid(), updateRoutes(gestalts));
}; };
World.spawn = function (behavior, initialGestalts) { World.spawn = function (behavior) {
World.current().enqueueAction(World.activePid(), spawn(behavior, initialGestalts)); World.current().enqueueAction(World.activePid(), spawn(behavior));
}; };
World.exit = function (exn) { World.exit = function (exn) {
@ -226,13 +224,18 @@ World.prototype.performAction = function (pid, action) {
switch (action.type) { switch (action.type) {
case "spawn": case "spawn":
var pid = World.nextPid++; var pid = World.nextPid++;
var newGestalt = action.initialGestalt.label(pid); var entry = { gestalt: Route.emptyGestalt, behavior: action.behavior };
this.processTable[pid] = { gestalt: newGestalt, behavior: action.behavior }; this.processTable[pid] = entry;
if (action.behavior.boot) { if (entry.behavior.boot) {
this.asChild(pid, function () { action.behavior.boot() }); var initialGestalts = this.asChild(pid, function () { return entry.behavior.boot() });
if (initialGestalts) {
entry.gestalt = Route.gestaltUnion(initialGestalts).label(pid);
}
this.markPidRunnable(pid); this.markPidRunnable(pid);
} }
this.applyAndIssueRoutingUpdate(Route.emptyGestalt, newGestalt, pid); if (!Route.emptyGestalt.equals(entry.gestalt)) {
this.applyAndIssueRoutingUpdate(Route.emptyGestalt, entry.gestalt, pid);
}
break; break;
case "routes": case "routes":
if (pid in this.processTable) { if (pid in this.processTable) {
@ -315,6 +318,11 @@ World.prototype.dispatchEvent = function (e) {
} }
}; };
World.prototype.boot = function () {
// Needed in order for the new World to be marked as "runnable", so
// its initial actions get performed.
};
World.prototype.handleEvent = function (e) { World.prototype.handleEvent = function (e) {
switch (e.type) { switch (e.type) {
case "routes": case "routes":
@ -436,8 +444,8 @@ DemandMatcher.prototype.debugState = function () {
DemandMatcher.prototype.boot = function () { DemandMatcher.prototype.boot = function () {
var observerLevel = 1 + Math.max(this.demandLevel, this.supplyLevel); var observerLevel = 1 + Math.max(this.demandLevel, this.supplyLevel);
World.updateRoutes([sub(this.demandPattern, this.metaLevel, observerLevel), return [sub(this.demandPattern, this.metaLevel, observerLevel),
pub(this.supplyPattern, this.metaLevel, observerLevel)]); pub(this.supplyPattern, this.metaLevel, observerLevel)];
}; };
DemandMatcher.prototype.handleEvent = function (e) { DemandMatcher.prototype.handleEvent = function (e) {

View File

@ -12,7 +12,7 @@ function Spy(label, useJson, observationLevel) {
} }
Spy.prototype.boot = function () { Spy.prototype.boot = function () {
World.updateRoutes([sub(__, 0, this.observationLevel), pub(__, 0, this.observationLevel)]); return [sub(__, 0, this.observationLevel), pub(__, 0, this.observationLevel)];
}; };
Spy.prototype.handleEvent = function (e) { Spy.prototype.handleEvent = function (e) {

View File

@ -17,8 +17,8 @@ function WakeDetector(period) {
WakeDetector.prototype.boot = function () { WakeDetector.prototype.boot = function () {
var self = this; var self = this;
World.updateRoutes([pub(this.message)]);
this.timerId = setInterval(World.wrap(function () { self.trigger(); }), this.period); this.timerId = setInterval(World.wrap(function () { self.trigger(); }), this.period);
return [pub(this.message)];
}; };
WakeDetector.prototype.handleEvent = function (e) {}; WakeDetector.prototype.handleEvent = function (e) {};

View File

@ -47,10 +47,11 @@ describe("configurationTrace", function() {
it("should yield an appropriate trace", function () { it("should yield an appropriate trace", function () {
checkTrace(function (trace) { checkTrace(function (trace) {
World.spawn({ World.spawn({
boot: function () { return [sub(__)] },
handleEvent: function (e) { handleEvent: function (e) {
trace(e); trace(e);
} }
}, [sub(__)]); });
World.send(123); World.send(123);
World.send(234); World.send(234);
}, [Minimart.updateRoutes([]), }, [Minimart.updateRoutes([]),
@ -64,9 +65,15 @@ describe("nonempty initial routes", function () {
it("should be immediately signalled to the process", function () { it("should be immediately signalled to the process", function () {
// Specifically, no Minimart.updateRoutes([]) first. // Specifically, no Minimart.updateRoutes([]) first.
checkTrace(function (trace) { checkTrace(function (trace) {
World.spawn({ handleEvent: function (e) { World.spawn({
World.spawn({ handleEvent: trace }, [sub(["A", __], 0, 1)]) boot: function () { return [pub(["A", __])] },
}}, [pub(["A", __])]); handleEvent: function (e) {
World.spawn({
boot: function () { return [sub(["A", __], 0, 1)] },
handleEvent: trace
});
}
});
}, [Minimart.updateRoutes([pub(["A", __]).label(1)])]); }, [Minimart.updateRoutes([pub(["A", __]).label(1)])]);
}); });
}); });
@ -74,16 +81,19 @@ describe("nonempty initial routes", function () {
describe("actor with nonempty initial routes", function () { describe("actor with nonempty initial routes", function () {
it("shouldn't see initial empty conversational context", function () { it("shouldn't see initial empty conversational context", function () {
checkTrace(function (trace) { checkTrace(function (trace) {
World.spawn({ handleEvent: function (e) { World.spawn({
World.spawn(new Actor(function () { boot: function () { return [pub(["A", __])] },
Actor.observeAdvertisers( handleEvent: function (e) {
function () { return ["A", __] }, World.spawn(new Actor(function () {
{ presence: "isPresent" }, Actor.observeAdvertisers(
function () { function () { return ["A", __] },
trace(["isPresent", this.isPresent]); { presence: "isPresent" },
}); function () {
})); trace(["isPresent", this.isPresent]);
}}, [pub(["A", __])]); });
}));
}
});
}, [["isPresent", true]]); }, [["isPresent", true]]);
}); });
}); });

View File

@ -297,6 +297,13 @@ describe("matcher equality", function () {
}); });
describe("gestalt equality", function () { describe("gestalt equality", function () {
it("should distinguish emptyGestalt reliably", function () {
expect(r.simpleGestalt(false, r.__, 0, 10)
.union(r.simpleGestalt(true, r.__, 0, 10))
.equals(r.emptyGestalt))
.to.be(false);
});
it("should not rely on object identity", function () { it("should not rely on object identity", function () {
expect(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0)) expect(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0))
.equals(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0)))) .equals(r.simpleGestalt(false, "A", 0, 0).union(r.simpleGestalt(true, "B", 0, 0))))