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

View File

@ -13,15 +13,18 @@ $(document).ready(function () {
Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy");
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) {
if (e.type === "message" && e.message[0] === "jQuery") {
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({
counter: 0,

View File

@ -29,6 +29,13 @@ $(document).ready(function () {
Minimart.RoutingTableWidget.spawnRoutingTableWidget("#spy-holder", "spy"); // local
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) {
console.log(JSON.stringify(e));
if (e.type === "message"
@ -40,11 +47,7 @@ $(document).ready(function () {
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({
counter: 0,

View File

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

View File

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

View File

@ -17,9 +17,7 @@ function spawnDOMDriver(domWrapFunction, jQueryWrapFunction) {
fragmentClass,
fragmentSpec,
domWrapFunction,
jQueryWrapFunction),
[sub(domWrapFunction(selector, fragmentClass, fragmentSpec)),
sub(domWrapFunction(selector, fragmentClass, fragmentSpec), 0, 1)]);
jQueryWrapFunction));
};
World.spawn(d);
}
@ -40,12 +38,14 @@ function DOMFragment(selector, fragmentClass, fragmentSpec, domWrapFunction, jQu
DOMFragment.prototype.boot = function () {
var self = this;
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 () {
Minimart.JQuery.spawnJQueryDriver(self.selector+" > ."+self.fragmentClass,
1,
self.jQueryWrapFunction);
World.spawn({
boot: function () { return [monitoring] },
handleEvent: function (e) {
if (e.type === "routes") {
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) {

View File

@ -18,9 +18,7 @@ function spawnJQueryDriver(baseSelector, metaLevel, wrapFunction) {
selector,
eventName,
metaLevel,
wrapFunction),
[pub(wrapFunction(selector, eventName, __), metaLevel),
pub(wrapFunction(selector, eventName, __), metaLevel, 1)]);
wrapFunction));
};
World.spawn(d);
}
@ -47,6 +45,11 @@ function JQueryEventRouter(baseSelector, selector, eventName, metaLevel, wrapFun
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) {
if (e.type === "routes" && e.gestalt.isEmpty()) {
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);
}
function spawn(behavior, initialGestalts) {
return { type: "spawn",
behavior: behavior,
initialGestalt: Route.gestaltUnion(initialGestalts || []) };
function spawn(behavior) {
return { type: "spawn", behavior: behavior };
}
function updateRoutes(gestalts) {
@ -85,8 +83,8 @@ World.updateRoutes = function (gestalts) {
World.current().enqueueAction(World.activePid(), updateRoutes(gestalts));
};
World.spawn = function (behavior, initialGestalts) {
World.current().enqueueAction(World.activePid(), spawn(behavior, initialGestalts));
World.spawn = function (behavior) {
World.current().enqueueAction(World.activePid(), spawn(behavior));
};
World.exit = function (exn) {
@ -226,13 +224,18 @@ World.prototype.performAction = function (pid, action) {
switch (action.type) {
case "spawn":
var pid = World.nextPid++;
var newGestalt = action.initialGestalt.label(pid);
this.processTable[pid] = { gestalt: newGestalt, behavior: action.behavior };
if (action.behavior.boot) {
this.asChild(pid, function () { action.behavior.boot() });
var entry = { gestalt: Route.emptyGestalt, behavior: action.behavior };
this.processTable[pid] = entry;
if (entry.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.applyAndIssueRoutingUpdate(Route.emptyGestalt, newGestalt, pid);
if (!Route.emptyGestalt.equals(entry.gestalt)) {
this.applyAndIssueRoutingUpdate(Route.emptyGestalt, entry.gestalt, pid);
}
break;
case "routes":
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) {
switch (e.type) {
case "routes":
@ -436,8 +444,8 @@ DemandMatcher.prototype.debugState = function () {
DemandMatcher.prototype.boot = function () {
var observerLevel = 1 + Math.max(this.demandLevel, this.supplyLevel);
World.updateRoutes([sub(this.demandPattern, this.metaLevel, observerLevel),
pub(this.supplyPattern, this.metaLevel, observerLevel)]);
return [sub(this.demandPattern, this.metaLevel, observerLevel),
pub(this.supplyPattern, this.metaLevel, observerLevel)];
};
DemandMatcher.prototype.handleEvent = function (e) {

View File

@ -12,7 +12,7 @@ function Spy(label, useJson, observationLevel) {
}
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) {

View File

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

View File

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

View File

@ -297,6 +297,13 @@ describe("matcher 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 () {
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))))