Skip to content

Instantly share code, notes, and snippets.

@mlrawlings
Created September 12, 2014 18:08
Show Gist options
  • Save mlrawlings/49146926a8bd32bd8204 to your computer and use it in GitHub Desktop.
Save mlrawlings/49146926a8bd32bd8204 to your computer and use it in GitHub Desktop.
var mongoose = require('mongoose')
, Schema = mongoose.Schema
, ObjectId = mongoose.SchemaTypes.ObjectId
module.exports = function(schema, options) {
var options = options || {}
, historySchemaDefinition = {}
, historySchema
, HistoryArchive
options.auditRefs = options.auditRefs || []
schema.add({ created: Date, revised: Date, history: [{ type:ObjectId, ref:options.collection+'HistoryArchive' }] })
schema.eachPath(function(path, value) {
if(path != '_id' && path != '_v' && path != 'history') {
var pathParts = path.split('.')
var currentPath = historySchemaDefinition
var paramOptions = {}
for(var i = 0; i < pathParts.length-1; i++) {
var key = pathParts[i]
if(!historySchemaDefinition[key]) {
historySchemaDefinition[key] = {}
}
currentPath = historySchemaDefinition[key]
}
for(key in value.options) {
if(key == 'type' && value.options.type[0]) {
paramOptions.type = [{}]
for(key in value.options.type[0]) {
if(key == 'ref' && options.auditRefs.indexOf(value.options.type[0].ref) > -1) {
paramOptions.type[0].ref = value.options.type[0].ref + 'HistoryArchive'
} else {
paramOptions.type[0][key] = value.options.type[0][key]
}
}
} else if(key == 'ref' && options.auditRefs.indexOf(value.options.ref) > -1) {
paramOptions.ref = value.options.ref + 'HistoryArchive'
} else {
paramOptions[key] = value.options[key]
}
}
currentPath[pathParts[i]] = paramOptions
}
})
historySchemaDefinition.current_entity = { type:ObjectId, ref:options.collection }
historySchema = new Schema(historySchemaDefinition)
for(path in schema.virtuals) {
if(path != 'id') {
var virtuals = schema.virtuals[path]
for(var i = 0; i < virtuals.getters.length; i++) {
historySchema.virtual(path).get(virtuals.getters[i])
}
}
}
historySchema.methods.getPrevious = function(current, fn) {
var previousId, self = this
current.history.forEach(function(item, i) {
if(item._id.toString() == self._id.toString()) {
previousId = i == 0 ? '' : current.history[i-1]
}
})
if(!previousId) return fn(null, null)
HistoryArchive.findById(previousId, fn)
}
HistoryArchive = mongoose.model(options.collection+'HistoryArchive', historySchema)
schema.methods.saveAs = function(user, fn) {
this.revisedBy = user
this.save(fn)
}
schema.pre('save', function(next) {
if (!this.created) {
this.created = this.revised || new Date
this.revised = this.created
} else {
this.revised = Date.now()
}
next()
})
schema.pre('save', function(next) {
var entity = this
, archive = new HistoryArchive()
, waiting = 0
schema.eachPath(function(key, value) {
var ref = value.options.ref
, content = entity.get(key)
if (key != 'history' && key != '_v' && content) {
if(key == '_id') {
archive.set('current_entity', content)
} else if(value.options.type && value.options.type[0]) {
ref = value.options.type[0].ref
for(var i = 0; i < content.length; i++) {
if(ref && options.auditRefs.indexOf(ref) > -1) {
switchToArchive(content[i], key+'.'+i, ref)
} else {
archive.set(key+'.'+i, content[i])
}
}
} else if(ref && options.auditRefs.indexOf(ref) > -1) {
switchToArchive(content, key, ref)
} else {
archive.set(key, content)
}
}
})
if(!waiting) done()
function switchToArchive(content, key, ref) {
waiting++
mongoose.model(ref).getCurrentHistoryItem(content._id || content, function(err, historyItem) {
waiting--
archive.set(key, historyItem._id || historyItem)
if(!waiting) done()
})
}
function done() {
archive.save(function() {
entity.history.push(archive._id)
next()
})
}
})
schema.statics.getCurrentHistoryItem = function(id, fn) {
this.findById(id, function(err, entity) {
HistoryArchive.findOne({ _id:{ $in:entity.history } }).sort({ revised:-1 }).exec(fn)
})
}
schema.statics.findHistory = function() {
return HistoryArchive.find.apply(HistoryArchive, arguments)
}
schema.statics.findHistoryById = function() {
return HistoryArchive.findById.apply(HistoryArchive, arguments)
}
schema.methods.getCurrentHistoryItemId = function() {
var historyItem = this.history[this.history.length-1]
return historyItem._id ? historyItem._id : historyItem
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment