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
157
src/actor.js
157
src/actor.js
|
@ -1,4 +1,7 @@
|
||||||
var Reflect = require("./reflect.js");
|
var Reflect = require("./reflect.js");
|
||||||
|
var Minimart = require("./minimart.js");
|
||||||
|
var World = Minimart.World;
|
||||||
|
var Route = Minimart.Route;
|
||||||
|
|
||||||
Actor._chunks = null;
|
Actor._chunks = null;
|
||||||
|
|
||||||
|
@ -21,9 +24,13 @@ function checkChunks(type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function extractChunk(type, defaultOptions, args) {
|
function extractChunk(type, defaultOptions, args) {
|
||||||
|
var rawProjectionFn = args[0]
|
||||||
var options = null;
|
var options = null;
|
||||||
var handler = 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 (typeof args[i] === 'function') {
|
||||||
if (handler !== null) { throw new Error("Too many handler functions in Actor."+type); }
|
if (handler !== null) { throw new Error("Too many handler functions in Actor."+type); }
|
||||||
handler = args[i];
|
handler = args[i];
|
||||||
|
@ -47,16 +54,20 @@ function extractChunk(type, defaultOptions, args) {
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
type: type,
|
type: type,
|
||||||
rawProjection: args[0],
|
rawProjectionFn: rawProjectionFn,
|
||||||
options: options,
|
options: options,
|
||||||
handler: handler
|
handler: handler
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function recordChunk(chunk) {
|
||||||
|
Actor._chunks.push(chunk);
|
||||||
|
}
|
||||||
|
|
||||||
function chunkExtractor(type, defaultOptions) {
|
function chunkExtractor(type, defaultOptions) {
|
||||||
return function (/* ... */) {
|
return function (/* ... */) {
|
||||||
checkChunks(type);
|
checkChunks(type);
|
||||||
Actor._chunks.push(extractChunk(type,
|
recordChunk(extractChunk(type,
|
||||||
defaultOptions,
|
defaultOptions,
|
||||||
Array.prototype.slice.call(arguments)));
|
Array.prototype.slice.call(arguments)));
|
||||||
};
|
};
|
||||||
|
@ -86,15 +97,149 @@ Actor.observeSubscribers = chunkExtractor('observeSubscribers', observerDefaults
|
||||||
|
|
||||||
Actor.observeGestalt = function (gestaltFn, eventHandlerFn) {
|
Actor.observeGestalt = function (gestaltFn, eventHandlerFn) {
|
||||||
checkChunks('observeGestalt');
|
checkChunks('observeGestalt');
|
||||||
Actor._chunks.push({
|
recordChunk({
|
||||||
type: 'observeGestalt',
|
type: 'observeGestalt',
|
||||||
gestaltFn: gestaltFn,
|
gestaltFn: gestaltFn,
|
||||||
|
options: {
|
||||||
|
when: function () { return true; }
|
||||||
|
},
|
||||||
eventHandlerFn: eventHandlerFn
|
eventHandlerFn: eventHandlerFn
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
function finalizeActor(behavior, chunks) {
|
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) {
|
function kwApply(f, thisArg, args) {
|
||||||
|
@ -103,7 +248,7 @@ function kwApply(f, thisArg, args) {
|
||||||
for (var i = 0; i < formals.length; i++) {
|
for (var i = 0; i < formals.length; i++) {
|
||||||
var formal = formals[i];
|
var formal = formals[i];
|
||||||
if (!(formal in args)) {
|
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]);
|
actuals.push(args[formal]);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,8 @@ function formalParameters(fn) {
|
||||||
var argDecl = fnText.match(FN_ARGS);
|
var argDecl = fnText.match(FN_ARGS);
|
||||||
var args = argDecl[1].split(FN_ARG_SPLIT);
|
var args = argDecl[1].split(FN_ARG_SPLIT);
|
||||||
for (var i = 0; i < args.length; i++) {
|
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;
|
return result;
|
||||||
|
|
Loading…
Reference in New Issue