Skip to content

Commit

Permalink
Merge branch 'pr/20'
Browse files Browse the repository at this point in the history
  • Loading branch information
notheotherben committed Nov 17, 2015
2 parents 0a8ee18 + bd0858e commit fcddc72
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 22 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Iridium
**A High Performance, IDE Friendly ODM for MongoDB**

[![NPM Module](https://badge.fury.io/js/iridium.png)](https://npmjs.org/package/iridium)
[![Build Status](https://travis-ci.org/SierraSoftworks/Iridium.png?branch=release)](https://travis-ci.org/SierraSoftworks/Iridium)
[![NPM Module](https://badge.fury.io/js/iridium.svg)](https://npmjs.org/package/iridium)
[![Build Status](https://travis-ci.org/SierraSoftworks/Iridium.svg?branch=release)](https://travis-ci.org/SierraSoftworks/Iridium)
[![Coverage Status](https://coveralls.io/repos/SierraSoftworks/Iridium/badge.svg?branch=release)](https://coveralls.io/r/SierraSoftworks/Iridium?branch=release)
[![Code Climate](https://codeclimate.com/github/SierraSoftworks/Iridium/badges/gpa.svg)](https://codeclimate.com/github/SierraSoftworks/Iridium)
[![Test Coverage](https://codeclimate.com/github/SierraSoftworks/Iridium/badges/coverage.svg)](https://codeclimate.com/github/SierraSoftworks/Iridium)
Expand Down Expand Up @@ -267,4 +267,4 @@ You'll also find `first()` and `select()` which allow you to select the first, o
maps to the instance itself within the predicate - helping to make comparisons somewhat easier within JavaScript ES5.

### Custom Instances
If you decide to implement your own instance constructor then this is the part you'll be interested in.
If you decide to implement your own instance constructor then this is the part you'll be interested in.
9 changes: 5 additions & 4 deletions lib/Hooks.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import instance = require('./Instance');
import Bluebird = require('bluebird');

export interface Hooks<TDocument, TInstance> {
onCreating? (document: TDocument): void;
onRetrieved? (document: TDocument): void;
onReady? (instance: TInstance): void;
onSaving? (instance: TInstance, changes: any): void;
onCreating? (document: TDocument): Promise.Thenable<void> | void;
onRetrieved? (document: TDocument): Promise.Thenable<void> | void;
onReady? (instance: TInstance): Promise.Thenable<void> | void;
onSaving? (instance: TInstance, changes: any): Promise.Thenable<void> | void;
}
8 changes: 4 additions & 4 deletions lib/Instance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,20 +68,20 @@ export class Instance<TDocument extends { _id?: any }, TInstance> {
* A function which is called whenever a new document is in the process of being inserted into the database.
* @param document The document which will be inserted into the database.
*/
static onCreating: (document: { _id?: any }) => void;
static onCreating: (document: { _id?: any }) => Promise.Thenable<void> | void;

/**
* A function which is called whenever a document of this type is received from the database, prior to it being
* wrapped by an Instance object.
* @param document The document that was retrieved from the database.
*/
static onRetrieved: (document: { _id?: any }) => void;
static onRetrieved: (document: { _id?: any }) => Promise.Thenable<void> | void;

/**
* A function which is called whenever a new instance has been created to wrap a document.
* @param instance The instance which has been created.
*/
static onReady: (instance: Instance<{ _id?: any }, Instance<{ _id?: any }, any>>) => void;
static onReady: (instance: Instance<{ _id?: any }, Instance<{ _id?: any }, any>>) => Promise.Thenable<void> | void;

/**
* A function which is called whenever an instance's save() method is called to allow you to interrogate and/or manipulate
Expand All @@ -90,7 +90,7 @@ export class Instance<TDocument extends { _id?: any }, TInstance> {
* @param instance The instance to which the changes are being made
* @param changes The MongoDB change object describing the changes being made to the document.
*/
static onSaving: (instance: Instance<{ _id?: any }, Instance<{ _id?: any }, any>>, changes: any) => void;
static onSaving: (instance: Instance<{ _id?: any }, Instance<{ _id?: any }, any>>, changes: any) => Promise.Thenable<void> | void;

/**
* The name of the collection into which documents of this type are stored.
Expand Down
8 changes: 4 additions & 4 deletions lib/InstanceInterface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,26 +53,26 @@ export interface InstanceImplementation<TDocument extends { _id ?: any }, TInsta
* An optional method which will be called whenever a document is about to be inserted into the database,
* allowing you to set default values and do any preprocessing you wish prior to the document being inserted.
*/
onCreating? (document: TDocument): void;
onCreating? (document: TDocument): Promise.Thenable<void> | void;

/**
* An optional method which is called whenever a new document is received from the model's collection and
* prior to the document being wrapped, can be used to perform preprocessing if necessary - however we recommend
* you rather make use of transforms for that task.
*/
onRetrieved? (document: TDocument): void;
onRetrieved? (document: TDocument): Promise.Thenable<void> | void;

/**
* An optional method which is called whenever a new document for this model has been wrapped in an instance.
*/
onReady? (instance: TInstance): void;
onReady? (instance: TInstance): Promise.Thenable<void> | void;

/**
* An optional method which is called prior to saving an instance, it is provided with the instance itself as
* well as the proposed changes to the instance. This allows you to make additional changes, such as updating
* a lastChanged property on the document, or abort changes by throwing an error.
*/
onSaving? (instance: TInstance, changes: any): void;
onSaving? (instance: TInstance, changes: any): Promise.Thenable<void> | void;

/**
* The cache controller used to determine whether a document may be cached, as well as deriving a unique cache
Expand Down
17 changes: 10 additions & 7 deletions lib/ModelHandlers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,22 @@ export class ModelHandlers<TDocument extends { _id?: any }, TInstance> {
partial: false
});

let wrapped: TResult;
return Bluebird.resolve(result).then((target: any) => {
return <Bluebird<TResult>>Bluebird.resolve().then(() => {

// Cache the document if caching is enabled
if (this.model.core.cache && options.cache && !options.fields) {
this.model.cache.set(target); // Does not block execution pipeline - fire and forget
}

// Trigger the received hook
if (this.model.hooks.onRetrieved) this.model.hooks.onRetrieved(target);

if (this.model.hooks.onRetrieved) return this.model.hooks.onRetrieved(target);
}).then(() => {
// Wrap the document and trigger the ready hook
let wrapped: TResult = wrapper(target, false, !!options.fields);
wrapped = wrapper(target, false, !!options.fields);

if (this.model.hooks.onReady && wrapped instanceof this.model.Instance) this.model.hooks.onReady(<TInstance><any>wrapped);
if (this.model.hooks.onReady && wrapped instanceof this.model.Instance) return this.model.hooks.onReady(<TInstance><any>wrapped);
}).then(() => {
return wrapped;
});
});
Expand All @@ -54,7 +55,8 @@ export class ModelHandlers<TDocument extends { _id?: any }, TInstance> {
creatingDocuments(documents: TDocument[]): Bluebird<any[]> {
return Bluebird.all(documents.map((document: any) => {
return Bluebird.resolve().then(() => {
if (this.model.hooks.onCreating) this.model.hooks.onCreating(document);
if (this.model.hooks.onCreating) return this.model.hooks.onCreating(document);
}).then(() => {
document = this.model.helpers.convertToDB(document);
let validation: Skmatc.Result = this.model.helpers.validate(document);
if (validation.failed) return Bluebird.reject(validation.error);
Expand All @@ -66,7 +68,8 @@ export class ModelHandlers<TDocument extends { _id?: any }, TInstance> {

savingDocument(instance: TInstance, changes: any): Bluebird<TInstance> {
return Bluebird.resolve().then(() => {
if (this.model.hooks.onSaving) this.model.hooks.onSaving(instance, changes);
if (this.model.hooks.onSaving) return this.model.hooks.onSaving(instance, changes);
}).then(() => {
return instance;
});
}
Expand Down
29 changes: 29 additions & 0 deletions test/Hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ interface TestDocument {

let hookEmitter = new Events.EventEmitter();

let shouldReject = 0;
class Test extends Iridium.Instance<TestDocument, Test> {
static collection = 'test';
static schema: Iridium.Schema = {
Expand All @@ -21,18 +22,22 @@ class Test extends Iridium.Instance<TestDocument, Test> {
answer: number;

static onCreating(document: TestDocument) {
if (shouldReject === 1) return Promise.reject('test rejection');
hookEmitter.emit('creating', document);
}

static onReady(instance: Test) {
if (shouldReject === 2) return Promise.reject('test rejection');
hookEmitter.emit('ready', instance);
}

static onRetrieved(document: TestDocument) {
if (shouldReject === 3) return Promise.reject('test rejection');
hookEmitter.emit('retrieved', document);
}

static onSaving(instance: Test, changes: any) {
if (shouldReject === 4) return Promise.reject('test rejection');
hookEmitter.emit('saving', instance, changes);
}
}
Expand All @@ -43,6 +48,7 @@ describe("Hooks", function () {
let core = new Iridium.Core({ database: 'test' });
let model = new Iridium.Model<TestDocument, Test>(core, Test);

beforeEach(() => shouldReject = 0);
beforeEach(() => core.connect().then(() => model.remove()).then(() => model.insert({ answer: 10 })));
afterEach(() => model.remove());
after(() => core.close());
Expand All @@ -64,6 +70,11 @@ describe("Hooks", function () {

return model.insert({ answer: 11 }).then(() => chai.expect(result).to.exist).then(() => result);
});

it("should accept promises",(done) => {
shouldReject = 1;
model.insert({ answer: 11 }).then(null, (err) => chai.expect(err).to.equal('test rejection') && done());
});
});

describe("ready",() => {
Expand All @@ -88,6 +99,11 @@ describe("Hooks", function () {

return model.get().then(() => chai.expect(result).to.exist).then(() => result);
});

it("should accept promises",(done) => {
shouldReject = 2;
model.get().then(null, (err) => chai.expect(err).to.equal('test rejection') && done());
});
});

describe("retreived",() => {
Expand All @@ -112,6 +128,11 @@ describe("Hooks", function () {

return model.get().then(() => chai.expect(result).to.exist).then(() => result);
});

it("should accept promises",(done) => {
shouldReject = 3;
model.get().then(null, (err) => chai.expect(err).to.equal('test rejection') && done());
});
});

describe("saving",() => {
Expand Down Expand Up @@ -159,5 +180,13 @@ describe("Hooks", function () {
return instance.save();
}).then(() => chai.expect(result).to.exist).then(() => result);
});

it("should accept promises",(done) => {
shouldReject = 4;
model.get().then((instance) => {
instance.answer++;
return instance.save();
}).then(null, (err) => chai.expect(err).to.equal('test rejection') && done());
});
});
});

0 comments on commit fcddc72

Please sign in to comment.