Skip to content

Commit

Permalink
Merge pull request #10 from ncthbrt/fix/child-api
Browse files Browse the repository at this point in the history
Removed ability to get references to children from outside the actor
ncthbrt authored Oct 23, 2017
2 parents c518f27 + 686b4f2 commit 92e265e
Showing 6 changed files with 69 additions and 49 deletions.
5 changes: 4 additions & 1 deletion lib/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
const { spawn, spawnStateless } = require('./actor');
const { spawnPersistent, configurePersistence } = require('./extensions/persistence');

module.exports = {
...require('./system'),
spawn,
spawnStateless
spawnStateless,
spawnPersistent,
configurePersistence
};
17 changes: 0 additions & 17 deletions lib/references.js
Original file line number Diff line number Diff line change
@@ -12,7 +12,6 @@ const applyOrThrowIfStopped = (reference, f) => {
const fallback = {
dispatch: () => { },
query: () => Promise.reject(new Error('Actor stopped. Query can never resolve')),
stopped: true,
stop: () => {}
};

@@ -42,14 +41,6 @@ class ActorReference {
privateProperties.set(this, { system });
}

isStopped () {
return actorOrFallback(this).stopped;
}

children () {
return new Map(actorOrFallback(this).childReferences);
}

dispatch (message, sender) {
return actorOrFallback(this).dispatch(message, sender);
}
@@ -70,14 +61,6 @@ class ActorSystemReference {
freeze(this);
}

isStopped () {
return privateProperties.get(this).system.stopped;
}

children () {
return new Map(privateProperties.get(this).system.childReferences);
}

stop () {
return privateProperties.get(this).system.stop();
}
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "nact",
"version": "2.0.2",
"version": "2.0.3",
"description": "Mulithreaded implementation of the actor model for node",
"main": "lib/index.js",
"scripts": {
71 changes: 44 additions & 27 deletions test/actor.js
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ const { start, spawn, spawnStateless } = require('../lib');
const { Promise } = require('bluebird');
const { LocalPath } = require('../lib/paths');
const delay = Promise.delay;
const { applyOrThrowIfStopped } = require('../lib/references');

const spawnChildrenEchoer = (parent, name) =>
spawnStateless(
@@ -16,6 +17,22 @@ const spawnChildrenEchoer = (parent, name) =>
name
);

const isStopped = (reference) => {
try {
return applyOrThrowIfStopped(reference, (ref) => ref.stopped);
} catch (e) {
return true;
}
};

const children = (reference) => {
try {
return applyOrThrowIfStopped(reference, ref => new Map(ref.childReferences));
} catch (e) {
return new Map();
}
};

const ignore = () => { };

const retry = async (assertion, remainingAttempts, retryInterval = 0) => {
@@ -114,14 +131,14 @@ describe('Actor', function () {
console.error = ignore;
let child = spawnStateless(system, (msg) => { throw new Error('testError'); });
child.dispatch();
await retry(() => child.isStopped().should.be.true, 12, 10);
await retry(() => isStopped(child).should.be.true, 12, 10);
});

it('should automatically stop if rejected promise is thrown', async function () {
console.error = ignore;
let child = spawnStateless(system, (msg) => Promise.reject(new Error('testError')));
child.dispatch();
await retry(() => child.isStopped().should.be.true, 12, 10);
await retry(() => isStopped(child).should.be.true, 12, 10);
});
});

@@ -149,35 +166,35 @@ describe('Actor', function () {
let grandchild2 = spawnStateless(child1, ignore, 'grandchild2');

child1.stop();
child1.isStopped().should.be.true;
grandchild1.isStopped().should.be.true;
grandchild2.isStopped().should.be.true;
isStopped(child1).should.be.true;
isStopped(grandchild1).should.be.true;
isStopped(grandchild2).should.be.true;

system.stop();
system.children().should.be.empty;
actor.isStopped().should.be.true;
child2.isStopped().should.be.true;
children(system).should.be.empty;
isStopped(actor).should.be.true;
isStopped(child2).should.be.true;
});

it('is invoked automatically when the next state is not returned', async function () {
let child = spawn(system, ignore, 'testActor');
child.dispatch();
await retry(() => child.isStopped().should.be.true, 12, 10);
system.children().should.not.include('testActor');
await retry(() => isStopped(child).should.be.true, 12, 10);
children(system).should.not.include('testActor');
});

it('should be able to be invoked multiple times', async function () {
let child = spawn(system, ignore);
child.stop();
await retry(() => child.isStopped().should.be.true, 12, 10);
await retry(() => isStopped(child).should.be.true, 12, 10);
child.stop();
child.isStopped().should.be.true;
isStopped(child).should.be.true;
});

it('should ignore subsequent dispatchs', async function () {
let child = spawnStateless(system, () => { throw new Error('Should not be triggered'); });
child.stop();
await retry(() => child.isStopped().should.be.true, 12, 10);
await retry(() => isStopped(child).should.be.true, 12, 10);
child.dispatch('test');
});
});
@@ -191,9 +208,9 @@ describe('Actor', function () {
delete console.error;
});

it('automatically names an actor if a name is not provided', function () {
it('automatically names an actor if a name is not provided', async function () {
let child = spawnStateless(system, (msg) => msg);
system.children().size.should.equal(1);
children(system).size.should.equal(1);
child.name.should.not.be.undefined;
});

@@ -205,19 +222,19 @@ describe('Actor', function () {

it('correctly registers children upon startup', async function () {
let child = spawnChildrenEchoer(system, 'testChildActor');
system.children().should.have.keys('testChildActor');
let children = await child.query();
children.should.be.empty;
children(system).should.have.keys('testChildActor');
let childReferences = await child.query();
childReferences.should.be.empty;

spawnStateless(child, ignore, 'testGrandchildActor');
child.children().should.have.keys('testGrandchildActor');
children = await child.query();
children.should.have.members(['testGrandchildActor']);
children(child).should.have.keys('testGrandchildActor');
childReferences = await child.query();
childReferences.should.have.members(['testGrandchildActor']);

spawnStateless(child, ignore, 'testGrandchildActor2');
children = await child.query();
child.children().should.have.keys('testGrandchildActor2', 'testGrandchildActor');
children.should.have.members(['testGrandchildActor2', 'testGrandchildActor']);
childReferences = await child.query();
children(child).should.have.keys('testGrandchildActor2', 'testGrandchildActor');
childReferences.should.have.members(['testGrandchildActor2', 'testGrandchildActor']);
});

it('can be invoked from within actor', async function () {
@@ -230,9 +247,9 @@ describe('Actor', function () {
}
}, 'test');
actor.dispatch('spawn');
let children = await actor.query('query');
children.should.have.members(['child1', 'child2']);
actor.children().should.have.keys('child1', 'child2');
let childrenMap = await actor.query('query');
childrenMap.should.have.members(['child1', 'child2']);
children(actor).should.have.keys('child1', 'child2');
});
});

12 changes: 10 additions & 2 deletions test/persistent-actor.js
Original file line number Diff line number Diff line change
@@ -11,7 +11,15 @@ const chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
const { Promise } = require('bluebird');
const delay = Promise.delay;
const { applyOrThrowIfStopped } = require('../lib/references');

const isStopped = (reference) => {
try {
return applyOrThrowIfStopped(reference).stopped;
} catch (e) {
return true;
}
};
// Begin helpers
const ignore = () => {};

@@ -123,7 +131,7 @@ describe('PersistentActor', () => {
concatenativeFunction(''),
'test'
);
await retry(() => actor.isStopped().should.be.true, 5, 10);
await retry(() => isStopped(actor).should.be.true, 5, 10);
});

it('should signal an error if restore stream fails midway through recovery', async () => {
@@ -137,7 +145,7 @@ describe('PersistentActor', () => {
concatenativeFunction(''),
'frog'
);
await retry(() => actor.isStopped().should.be.true, 5, 10);
await retry(() => isStopped(actor).should.be.true, 5, 10);
});

it('should be able to restore and then persist new events (with correct seqNumbers)', async () => {
11 changes: 10 additions & 1 deletion test/system.js
Original file line number Diff line number Diff line change
@@ -3,6 +3,15 @@
const chai = require('chai');
chai.should();
const { start, spawnStateless, spawn } = require('../lib');
const { applyOrThrowIfStopped } = require('../lib/references');

const isStopped = (reference) => {
try {
return applyOrThrowIfStopped(reference).stopped;
} catch (e) {
return true;
}
};

const ignore = () => {};

@@ -31,7 +40,7 @@ describe('System', function () {

it('should register as being stopped', function () {
system.stop();
system.isStopped().should.be.true;
isStopped(system).should.be.true;
});
});
});

0 comments on commit 92e265e

Please sign in to comment.