Last active
January 26, 2016 15:52
-
-
Save francisbrito/3212d067e6ea1a33a6a9 to your computer and use it in GitHub Desktop.
WIP: Proof of Concept for a MongoDB data mapper.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Show hidden characters
{ | |
"presets": ['es2015-node4'] | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const mongodb = require('mongodb'); | |
const utilities = require('underscore'); | |
const DEFAULT_PAGINATION = {skip: 0, limit: 0}; | |
const DEFAULT_DESTROY_OPTIONS = { | |
forcefully: false, | |
}; | |
const MONGODB_DATA_MAPPER_PROTOTYPE = { | |
// Life-cycle methods. | |
*initialize(options = {}) { | |
if (propertiesAreMissing(['dbUri', 'factory', 'collectionName'], options)) { | |
throw new Error('Missing `dbUri`, `factory` or `collectionName`'); | |
} | |
const {dbUri, factory, collectionName} = options; | |
if (!isValidConnectionUri(dbUri)) throw new Error('`dbUri` is malformed.'); | |
if (!isValidFactoryFunction(factory)) throw new Error('`factory` is invalid.'); | |
if (!isValidCollectionName(collectionName)) throw new Error('`collectionName` is invalid.'); | |
const db = yield mongodb.MongoClient.connect(dbUri); | |
this.db = db; | |
this.collection = db.collection(collectionName); | |
this.factory = this.createEntity = factory; | |
}, | |
*destroy({forcefully} = DEFAULT_DESTROY_OPTIONS) { | |
yield this.db.close(forcefully); | |
}, | |
// CRUD methods. | |
*save(entity) { | |
if (!this.factory.isFactoryOf(entity)) | |
throw new Error(`Can only save instances of ${this.factory.name}`); | |
// Maps `this.factory`'s ID to MongoDB's ID. | |
const entityId = entity.id; | |
const entityWithoutId = utilities.omit(entity.toJSON(), 'id'); | |
const mongoDbEntity = Object.assign({}, entityWithoutId, {_id: entityId}); | |
yield this.collection.insert(mongoDbEntity); | |
}, | |
*find(query = {}, projection = {}, sorting = {}, pagination = DEFAULT_PAGINATION) {}, | |
*update(id, properties) {}, | |
*remove(id) {}, | |
}; | |
module.exports = MONGODB_DATA_MAPPER_PROTOTYPE; | |
/** | |
* Helpers | |
*/ | |
function propertiesAreMissing(properties, obj) { | |
return properties.map(p => !utilities.has(obj, p)).reduce((p, c) => p && c); | |
} | |
function isValidConnectionUri(uri) { | |
// NOTE: For now, a valid connection URI is a string starting with `mongodb://`. | |
return utilities.isString(uri) && uri.startsWith('mongodb://'); | |
} | |
function isValidFactoryFunction(factory) { | |
return utilities.isFunction(factory) && factory.isFactoryOf && utilities.isFunction(factory.isFactoryOf); | |
} | |
function isValidCollectionName(collectionName) { | |
return utilities.isString(collectionName); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const isFunction = require('underscore').isFunction; | |
const isValidUuid = require('validator').isUUID; | |
const generateUuid = require('uuid').v4; | |
const TODO_PROTOTYPE = { | |
toJSON() { | |
const {id, done, text} = this; | |
return { | |
id, | |
done, | |
text, | |
}; | |
}, | |
markAsDone() { | |
this.done = true; | |
}, | |
}; | |
function Todo(properties) { | |
// Allow calling this constructor without the `new` keyword. | |
if (!(this instanceof Todo)) return new Todo(properties); | |
const id = generateUuid(); | |
const {done, text} = properties; | |
this.id = id; | |
this.done = !!done; | |
this.text = text || ''; | |
} | |
Todo.isFactoryOf = (properties) => { | |
const {id} = properties; | |
if (!hasFields(['id', 'done', 'text'], properties)) return false; | |
if (!isValidUuid(id)) return false; | |
if (!hasValidToJsonMethod(properties)) return false; | |
return true | |
}; | |
Object.setPrototypeOf(Todo.prototype, TODO_PROTOTYPE); | |
exports.Todo = Todo; | |
/** | |
* Helpers | |
*/ | |
function hasFields(fields, obj) { | |
return fields.map(f => f in obj).reduce((p, c) => p && c); | |
} | |
function hasValidToJsonMethod(obj) { | |
return obj.toJSON && isFunction(obj.toJSON); /* && obj.toJSON === TODO_PROTOTYPE.toJSON // limits extensability (?) */ | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"name": "data-mapper", | |
"version": "1.0.0", | |
"description": "", | |
"main": "index.js", | |
"dependencies": { | |
"babel-preset-es2015-node4": "^2.0.3", | |
"bluebird": "^3.1.4", | |
"co": "^4.6.0", | |
"mongodb": "^2.1.4", | |
"underscore": "^1.8.3", | |
"validator": "^4.5.1", | |
"uuid": "^2.0.1" | |
}, | |
"devDependencies": {}, | |
"scripts": { | |
"test": "echo \"Error: no test specified\" && exit 1" | |
}, | |
"author": "", | |
"license": "ISC" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const Promise = require('bluebird'); | |
const toCoroutine = require('co'); | |
const Todo = require('./models').Todo; | |
const MongoDbDataMapper = require('./'); | |
Promise | |
.resolve(toCoroutine(function* () { | |
const todoDataMapper = Object.create(MongoDbDataMapper); | |
yield todoDataMapper.initialize({ | |
dbUri: 'mongodb://localhost/test', | |
factory: Todo, | |
collectionName: 'todos', | |
}); | |
const myNewTodo = new Todo({ | |
text: 'Test Data-Mapper concept.' | |
}); | |
yield todoDataMapper.save(myNewTodo); | |
yield todoDataMapper.destroy(); | |
})); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment