Finish actor.js

This commit is contained in:
Tony Garnock-Jones 2014-07-24 16:22:22 -07:00
parent 9ffbec107f
commit 95a4bc3c93
4 changed files with 596 additions and 56 deletions

484
dist/minimart.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,4 +1,7 @@
var Reflect = require("./reflect.js");
var Minimart = require("./minimart.js");
var World = Minimart.World;
var Route = Minimart.Route;
Actor._chunks = null;
@ -21,9 +24,13 @@ function checkChunks(type) {
}
function extractChunk(type, defaultOptions, args) {
var rawProjectionFn = args[0]
var options = null;
var handler = null;
for (var i = 1; i < args.length; i++) { // NB: skip the first arg - it's rawProjection
if (typeof rawProjectionFn !== 'function') {
throw new Error("Actor."+type+" expects a function producing a pattern as first argument");
}
for (var i = 1; i < args.length; i++) { // NB: skip the first arg - it's rawProjectionFn
if (typeof args[i] === 'function') {
if (handler !== null) { throw new Error("Too many handler functions in Actor."+type); }
handler = args[i];
@ -47,18 +54,22 @@ function extractChunk(type, defaultOptions, args) {
}
return {
type: type,
rawProjection: args[0],
rawProjectionFn: rawProjectionFn,
options: options,
handler: handler
};
}
function recordChunk(chunk) {
Actor._chunks.push(chunk);
}
function chunkExtractor(type, defaultOptions) {
return function (/* ... */) {
checkChunks(type);
Actor._chunks.push(extractChunk(type,
defaultOptions,
Array.prototype.slice.call(arguments)));
recordChunk(extractChunk(type,
defaultOptions,
Array.prototype.slice.call(arguments)));
};
}
@ -86,15 +97,149 @@ Actor.observeSubscribers = chunkExtractor('observeSubscribers', observerDefaults
Actor.observeGestalt = function (gestaltFn, eventHandlerFn) {
checkChunks('observeGestalt');
Actor._chunks.push({
recordChunk({
type: 'observeGestalt',
gestaltFn: gestaltFn,
options: {
when: function () { return true; }
},
eventHandlerFn: eventHandlerFn
});
};
function finalizeActor(behavior, chunks) {
throw new Error("notYetImplemented"); // HERE
var oldBoot = behavior.boot;
var oldHandleEvent = behavior.handleEvent;
var projections = {};
var compiledProjections = {};
var previousObjs = {};
behavior.boot = function () {
if (oldBoot) { oldBoot.call(this); }
this.updateRoutes();
};
behavior.updateRoutes = function () {
var newRoutes = Route.emptyGestalt;
for (var i = 0; i < chunks.length; i++) {
var chunk = chunks[i];
if (chunk.options.when.call(this)) {
switch (chunk.type) {
case 'observeGestalt':
newRoutes = newRoutes.union(chunk.gestaltFn.call(this));
break;
case 'advertise': // fall through
case 'subscribe':
var proj = chunk.rawProjectionFn.call(this);
projections[i] = proj;
var g = Route.simpleGestalt(chunk.type === 'advertise',
Route.projectionToPattern(proj),
chunk.options.metaLevel,
0);
newRoutes = newRoutes.union(g);
break;
case 'observeSubscribers': // fall through
case 'observeAdvertisers':
var proj = chunk.rawProjectionFn.call(this);
projections[i] = proj;
compiledProjections[i] = Route.compileProjection(proj);
var g = Route.simpleGestalt(chunk.type === 'observeSubscribers',
Route.projectionToPattern(proj),
chunk.options.metaLevel,
chunk.options.level + 1);
newRoutes = newRoutes.union(g);
if (chunk.options.added || chunk.options.removed) {
previousObjs[i] = Route.arrayToSet([]);
}
break;
default:
throw new Error("Unsupported chunk type: "+chunk.type);
}
}
}
World.updateRoutes([newRoutes]);
};
behavior.handleEvent = function (e) {
if (oldHandleEvent) { oldHandleEvent.call(this, e); }
for (var i = 0; i < chunks.length; i++) {
var chunk = chunks[i];
switch (chunk.type) {
case 'observeGestalt':
chunk.eventHandlerFn.call(this, e);
break;
case 'advertise': // fall through
case 'subscribe':
if (chunk.handler
&& (e.type === 'message')
&& (e.isFeedback === (chunk.type === 'advertise')))
{
var matchResult = Route.matchPattern(e.message, projections[i]);
if (matchResult) {
kwApply(chunk.handler, this, matchResult);
}
}
break;
case 'observeSubscribers': // fall through
case 'observeAdvertisers':
if (e.type === 'routes') {
var projectionResult = e.gestalt.project(compiledProjections[i],
chunk.type !== 'observeSubscribers',
chunk.options.metaLevel,
chunk.options.level);
var isPresent = !Route.is_emptyMatcher(projectionResult);
if (chunk.options.presence) {
this[chunk.options.presence] = isPresent;
}
var objs = [];
if (isPresent) {
var keys = Route.matcherKeys(projectionResult);
if (keys === null) {
console.warn("Wildcard detected while projecting ("
+JSON.stringify(chunk.options)+")");
} else {
objs = Route.matcherKeysToObjects(keys, compiledProjections[i]);
if (chunk.options.set) {
for (var j = 0; j < objs.length; j++) {
objs[j] = chunk.options.set.call(this, objs[j]);
}
}
}
}
if (chunk.options.name) {
this[chunk.options.name] = objs;
}
if (chunk.options.added || chunk.options.removed) {
var objSet = Route.arrayToSet(objs);
if (chunk.options.added) {
this[chunk.options.added] =
Route.setToArray(Route.setSubtract(objSet, previousObjs[i]));
}
if (chunk.options.removed) {
this[chunk.options.removed] =
Route.setToArray(Route.setSubtract(previousObjs[i], objSet));
}
previousObjs[i] = objSet;
}
if (chunk.handler) {
chunk.handler.call(this);
}
}
break;
default:
throw new Error("Unsupported chunk type: "+chunk.type);
}
}
};
return behavior;
}
function kwApply(f, thisArg, args) {
@ -103,7 +248,7 @@ function kwApply(f, thisArg, args) {
for (var i = 0; i < formals.length; i++) {
var formal = formals[i];
if (!(formal in args)) {
throw new Error("Function parameter "+formal+" not present in args");
throw new Error("Function parameter '"+formal+"' not present in args");
}
actuals.push(args[formal]);
}

View File

@ -16,7 +16,8 @@ function formalParameters(fn) {
var argDecl = fnText.match(FN_ARGS);
var args = argDecl[1].split(FN_ARG_SPLIT);
for (var i = 0; i < args.length; i++) {
result.push(args[i].trim());
var trimmed = args[i].trim();
if (trimmed) { result.push(trimmed); }
}
return result;