Compose Classes! There are many like it, but this one is for you.
Example with simplified Mongo collections
Consider a few basic interfaces into persistent storage:
class Users {
static get collection() { return db.collection('users') }
static get schema() {
return Joi.Object({
name: Joi.string().required()
})
}
static async findByName(name) { }
}
class Devices {
static get collection() { return db.collection('devices') }
static get schema() {
return Joi.Object({
started: Joi.bool().default(false)
})
}
static async startAll() { }
}
Ok. Nice and lean.
We can keep them that way by encapsulating some common behavior:
class CommonOperations {
static assertSchema(data) {
return Joi.attempt(data, this.schema)
}
static async create(data) {
const newThing = CommonOperations.assertSchema(data)
return this.collection.insertOne(newThing)
}
static async findById(id) {
return this.collection.findOne({ id })
}
static async armageddon() {
console.warn(`Removing all documents from ${this.collection}!`)
return this.collection.remove({})
}
}
Time to beef these up!:
const BeefyUsers = compose([ CommonOperations ])(Users)
const BeefyDevices = compose([ CommonOperations ])(Devices)
const favoriteUser = await Users.findById(1)
const favoriteDevice = await Device.findById(12)
const maru = { name: 'maru' }
const stemCellReactor = { started: false }
await Users.create(maru)
await Devices.create(stemCellReactor)
Less creative examples:
class Target {
static get targetGetter() { return '0' }
static originalMethod() { return 0 }
}
class FirstSource {
static get firstSourceGetter() { return '1' }
static get overwritableGetter() { return 'first compose\'s getter' }
static firstSourceMethod() { return 1234 }
static overwritableMethod() { return 'first compose\'s method' }
}
class SecondSource {
static get secondSourceGetter() { return '2' }
static get overwritableGetter() { return 'second compose\'s getter' }
static overwritableMethod() { return 'second compose\'s method' }
static secondSourceMethod() { return 5678 }
}
const ComposedClass = compose([ FirstSource, SecondSource ])(Target)
Target.targetGetter
Target.firstSourceGetter
Target.secondSourceGetter
Target.originalMethod()
Target.firstSourceMethod()
Target.secondSourceMethod()
Target.overwritableMethod()
FirstSource.overwritableGetter
FirstSource.overwritableMethod()
SecondSource.overwritableGetter
SecondSource.overwritableMethod()