Skip to content

Commit

Permalink
feat: add async tests, rename api
Browse files Browse the repository at this point in the history
  • Loading branch information
Prozi committed Jan 9, 2021
1 parent 00a6e71 commit a6a1036
Show file tree
Hide file tree
Showing 4 changed files with 229 additions and 71 deletions.
124 changes: 105 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,49 +5,135 @@
# Usage

```javascript
const { ObjectPool } = require("@jacekpietal/object-pool");

const pool = new ObjectPool(factory);
const ObjectPool = require("@jacekpietal/object-pool");
```

// you should provide this function
function factory() {
// that returns a new object instance
}
```javascript
const pool = new ObjectPool(
// you should provide this function
function factory() {
// that returns a new object instance
// can be async
}
);

// will return value from pool
// if pool empty uses factory function
const nextValue = pool.next();
const object = pool.get();

// returns value to the pool
pool.back(nextValue);
pool.put(object);

// prints 1
console.log(pool.size);

// removes value from pool
pool.delete(object);

// prints 0
console.log(pool.size);

// pre-fills pool up to size
// useful I think only for async functions
pool.size = 100;

// empties pool
pool.size = 0;

// size manipulations do trigger as required
// `put` and `delete` events for each change
```

## With Promises / async functions

If you want to use async functions be sure to put the same thing you get from the pool.

```javascript
const pool = new ObjectPool(async () => "value");

async function test() {
const once = pool.get();
const value1 = await once;

// put back the promise reference
pool.put(once);

const again = pool.get();
const value2 = await again;

// prints:
// {
// once: Promise { 'value' },
// again: Promise { 'value' },
// value1: 'value',
// value2: 'value'
// }
console.log({ once, again, value1, value2 });
}

test();
```

# Events

```javascript
pool.events.on("next", (value) => {
console.log({ next: value });
// all actions are accompanied by EventEmitter events

pool.events.on("get", (value) => {
console.log({ get: value });
});

pool.events.on("back", (value) => {
console.log({ back: value });
pool.events.on("put", (value) => {
console.log({ put: value });
});

pool.events.on("remove", (value) => {
console.log({ remove: value });
pool.events.on("delete", (value) => {
console.log({ delete: value });
});
```

# API

```javascript
pool.next(); // returns next value from pool
pool.get(); // returns get value from pool

pool.back(object); // puts back value to end of pool
pool.put(object); // puts put value to end of pool

pool.empty(); // empties the whole pool
pool.delete(object); // deletes object from pool manually
```

pool.remove(object); // removes object from pool manually
# Tests

Please check tests for more complicated cases:

```bash
$ yarn test
yarn run v1.22.5
$ jest
PASS ./index.spec.js
GIVEN ObjectPool instance
✓ THEN It should create (2 ms)
WHEN pool.get is called
✓ THEN even if pool.size is 0 (1 ms)
✓ THEN It should return new instance of factory call
✓ THEN events.get should be emitted (1 ms)
WHEN pool.put is called
✓ THEN pool.size should be greater than 0 afterwards
✓ THEN events.put should be emitted (1 ms)
WHEN promise is `put` and then `get` again
✓ THEN it still works as expected (1502 ms)
WHEN pool.size is set to 0 on non-empty pool
✓ THEN events.delete should be emitted for each item (1 ms)
WHEN pool.size is set
✓ THEN pool size adjusts and is being filled accordingly (2 ms)
✓ THEN pool size adjusts and is being trimmed accordingly (1 ms)

Test Suites: 1 passed, 1 total
Tests: 10 passed, 10 total
Snapshots: 0 total
Time: 2.668 s
Ran all test suites.
Done in 3.41s.
```
# License
Expand Down
66 changes: 42 additions & 24 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,53 +3,71 @@
const EventEmitter = require("events");

class ObjectPool {
constructor(factory) {
this.factory = factory;
constructor(factoryFunction) {
this.factory = factoryFunction;

this.objects = new Set();

this.events = new EventEmitter();
}

empty() {
if (this.objects.size === 0) {
return;
get size() {
return this.objects.size;
}

set size(size = 0) {
if (typeof size !== "number") {
throw new Error("Parameter is not a number: " + typeof size);
}

const [result] = this.objects.values();
let current = this.objects.size;

this.remove(result);
while (current < size) {
const object = this.factory();

this.empty();
}
this.put(object);

current++;
}

next() {
if (this.objects.size === 0) {
const result = this.factory();
while (current > size) {
const [object] = this.objects.values();

this.events.emit("next", result);
this.delete(object);

return result;
current--;
}
}

const [result] = this.objects.values();
get() {
if (this.size === 0) {
const object = this.factory();

this.objects.delete(result);
this.events.emit("get", object);

this.events.emit("next", result);
return object;
}

return result;
}
const [object] = this.objects.values();

remove(object) {
this.objects.delete(object);

this.events.emit("remove", object);
this.events.emit("get", object);

return object;
}

back(object) {
put(object) {
this.objects.add(object);

this.events.emit("back", object);
this.events.emit("put", object);
}

delete(object) {
this.objects.delete(object);

this.events.emit("delete", object);
}
}

module.exports.ObjectPool = ObjectPool;
module.exports = ObjectPool;
106 changes: 80 additions & 26 deletions index.spec.js
Original file line number Diff line number Diff line change
@@ -1,64 +1,118 @@
const { ObjectPool } = require(".");
"use strict";

const ObjectPool = require(".");

describe("GIVEN ObjectPool instance", () => {
let pool;

const resolveArgument = "[Promise resolved]";

function factory() {
return new Promise((resolve) => {
setTimeout(() => resolve(resolveArgument), 500);
});
}

beforeEach(() => {
pool = new ObjectPool(() => true);
pool = new ObjectPool(factory);
});

it("THEN It should create", () => {
expect(pool).toBeTruthy();
});

describe("WHEN pool.next is called", () => {
it("THEN even if objects size is 0", () => {
expect(pool.objects.size).toBe(0);
describe("WHEN pool.get is called", () => {
it("THEN even if pool.size is 0", () => {
expect(pool.size).toBe(0);
});

it("THEN It should return new instance of object", () => {
expect(pool.next()).toBe(true);
it("THEN It should return new instance of factory call", () => {
expect(typeof pool.get()).toBe(typeof factory());
});

it("THEN events.next should be emitted", async (done) => {
pool.events.on("next", () => done());
it("THEN events.get should be emitted", async (done) => {
pool.events.on("get", () => done());

pool.next();
pool.get();
});
});

describe("WHEN pool.back is called", () => {
it("THEN objects size should be greater than 0 afterwards", () => {
pool.back(false);
describe("WHEN pool.put is called", () => {
it("THEN pool.size should be greater than 0 afterwards", () => {
pool.put(false);

expect(pool.size).toBeGreaterThan(0);
});

it("THEN events.put should be emitted", async (done) => {
pool.events.on("put", () => done());

expect(pool.objects.size).toBeGreaterThan(0);
pool.put(null);
});
});

describe("WHEN promise is `put` and then `get` again", () => {
it("THEN it still works as expected", async (done) => {
const once = pool.get();
const value = await once;

setTimeout(() => {
pool.put(once);

const again = pool.get();

expect(typeof again).toBe(typeof once);
expect(typeof value).toBe(typeof resolveArgument);

expect(value).toBe(resolveArgument);

it("THEN events.back should be emitted", async (done) => {
pool.events.on("back", () => done());
expect(Promise.resolve(once)).toBe(once);
expect(Promise.resolve(again)).toBe(again);

pool.back(null);
done();
}, 1000);
});
});

describe("WHEN pool.empty is called on non-empty pool", () => {
describe("WHEN pool.size is set to 0 on non-empty pool", () => {
// prepare with data
beforeEach(() => {
pool.back(null);
pool.back(null);
pool.back(null);
pool.put(null);
pool.put(null);
pool.put(null);
});

it("THEN events.remove should be emitted for each item", async (done) => {
let count = pool.objects.size;
it("THEN events.delete should be emitted for each item", async (done) => {
let count = pool.size;

pool.events.on("remove", () => {
pool.events.on("delete", () => {
if (!--count) {
done()
done();
}
});

pool.empty();
pool.size = 0;
});
});

describe("WHEN pool.size is set", () => {
const initialSize = 5;

// prepare with data
beforeEach(() => {
pool.size = initialSize;
});

it("THEN pool size adjusts and is being filled accordingly", () => {
pool.size = initialSize * 10;

expect(pool.size).toBe(initialSize * 10);
});

it("THEN pool size adjusts and is being trimmed accordingly", () => {
pool.size = 1;

expect(pool.size).toBe(1);
});
});
});
Loading

0 comments on commit a6a1036

Please sign in to comment.