Finish actor.js
This commit is contained in:
parent
9ffbec107f
commit
95a4bc3c93
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
161
src/actor.js
161
src/actor.js
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue