Skip to content

Instantly share code, notes, and snippets.

@caseyWebb
Created May 7, 2017 00:17
Show Gist options
  • Save caseyWebb/daf35c7ae617555bf8335f6608465007 to your computer and use it in GitHub Desktop.
Save caseyWebb/daf35c7ae617555bf8335f6608465007 to your computer and use it in GitHub Desktop.
Simple MongoDB Persistence
import * as Bluebird from 'bluebird'
import * as db from './db'
import Persistable from './_persistable'
beforeAll(() => db.connect())
afterEach(() => db.drop())
afterAll(() => db.disconnect())
test('persistable abstract class adds `create', async () => {
const name = 'foos'
type SerializedFoo = {
foo: string
}
class Foo extends Persistable<SerializedFoo> {
static $collectionName = name
data: SerializedFoo
}
const foo = await Foo.create({
foo: 'foo'
})
expect(foo._static.collection).toEqual(db.collection(name))
expect(foo.data.foo).toBe('foo')
})
test('persistable abstract class adds `fetch`', async () => {
const name = 'foos'
type SerializedFoo = {
foo: string
}
class Foo extends Persistable<SerializedFoo> {
static $collectionName = name
data: SerializedFoo
}
const { _id } = await Foo.create({
foo: 'foo'
})
const foo = await Foo.fetch(_id)
expect(foo.data.foo).toEqual('foo')
})
test('persistable abstract class adds `save', async () => {
const name = 'foos'
type SerializedFoo = {
foo: string
}
class Foo extends Persistable<SerializedFoo> {
static $collectionName = name
data: SerializedFoo
}
const foo = await Foo.create({
foo: 'foo'
})
foo.data.foo = 'bar'
await foo.save()
const bar = await Foo.fetch(foo._id)
expect(bar.data.foo).toBe('bar')
})
import * as Bluebird from 'bluebird'
import * as MongoDb from 'mongodb'
import { collection } from './db'
interface IPersistStatic<T, S> {
new (data: S, _id: MongoDb.ObjectID): T
collection: MongoDb.Collection
}
abstract class Persistable<S> {
abstract data: S
static $collectionName: string
_id: MongoDb.ObjectID
_static: typeof Persistable
constructor(data: S, _id: MongoDb.ObjectID) {
this.data = data
this._id = _id
this._static = this.constructor as typeof Persistable
}
save<S, T extends Persistable<S>>(
this: T
): Bluebird<void> {
return new Bluebird<void>((resolve, reject) => {
this._static.collection.updateOne(
{ _id: this._id },
this.data,
(err, res) => {
if (err) {
return reject(err)
}
resolve()
}
)
})
}
static _collection: MongoDb.Collection
static get collection(): MongoDb.Collection {
if (!this._collection) {
this._collection = collection(this.$collectionName)
}
return this._collection
}
static create<S, T extends Persistable<S>>(
this: IPersistStatic<T, S>,
data: S
): Bluebird<T> {
return new Bluebird<T>((resolve, reject) => {
this.collection.insertOne(data, (err, { insertedId: _id }) => {
if (err) {
return reject(err)
}
resolve(Reflect.construct(this, [data, _id]))
})
})
}
static fetch<S, T extends Persistable<S>>(
this: IPersistStatic<T, S>,
_id: MongoDb.ObjectID
): Bluebird<T> {
return new Bluebird<T>((resolve, reject) => {
this.collection.findOne({ _id }, (err, data) => {
if (err) {
return reject(err)
}
resolve(Reflect.construct(this, [data, data._id]))
})
})
}
}
export default Persistable
import * as Bluebird from 'bluebird'
import { Collection, Db, MongoClient } from 'mongodb'
import config from './config'
let db: Db
let dbPromise: Bluebird<Db>
export function connect(): Bluebird<void> {
return Bluebird.coroutine(function* () {
if (db) {
return db
}
if (!dbPromise) {
dbPromise = Bluebird.promisify(MongoClient.connect)(config.MONGO_URL) as Bluebird<Db>
}
db = yield dbPromise
})()
}
export function disconnect(): Bluebird<void> {
return new Bluebird<void>((resolve, reject) => {
db.close((err, res) => {
if (err) {
return reject(err)
}
resolve(res)
})
})
}
export function collection(c: string): Collection {
return db.collection(c)
}
export function drop(): Bluebird<void> {
return new Bluebird<void>((resolve, reject) => {
db.dropDatabase((err, res) => {
if (err) {
return reject(err)
}
resolve(res)
})
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment