Skip to content

Commit

Permalink
feat(hooks): add restore hooks support
Browse files Browse the repository at this point in the history
add support for restore hooks, only when soft deletes are on
  • Loading branch information
thetutlage committed Mar 9, 2016
1 parent b2708be commit d9329d8
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 8 deletions.
30 changes: 30 additions & 0 deletions src/Lucid/Model/Mixins/Dates.js
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,21 @@ Dates.setDeleteTimestamp = function (values) {
return values
}

/**
* sets the delete timestamp to null
*
* @method setRestoreTimestamp
*
* @param {Object} values
* @return {Object}
*
* @public
*/
Dates.setRestoreTimestamp = function (values) {
values[this.constructor.deleteTimestamp] = null
return values
}

/**
* returns getter method names for a given timestamp
* field.
Expand All @@ -95,3 +110,18 @@ Dates.getTimestampKey = function (fieldName) {
return 'deleteTimestamp'
}
}

/**
* formats a given date with the defined dateFormat
* for a model instance.
*
* @method formatDate
*
* @param {String} date
* @return {String}
*
* @public
*/
Dates.formatDate = function (date) {
return moment(date, this.constructor.dateFormat).isValid() ? moment(date).format(this.constructor.dateFormat) : date
}
22 changes: 22 additions & 0 deletions src/Lucid/Model/Mixins/Hooks.js
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,25 @@ Hooks.executeDeleteHooks = function * (scope, deleteHandler) {
yield this.composeHooks(scope, hooksChain)
return handlerResult
}

/**
* executes restore hooks on a given model instance.
*
* @method executeRestoreHooks
*
* @param {Object} scope
* @param {Function} restoreHandler
* @return {Number}
*
* @public
*/
Hooks.executeRestoreHooks = function * (scope, restoreHandler) {
let handlerResult = null
const restoreHandlerWrapper = function * (next) {
handlerResult = yield restoreHandler.call(this)
yield next
}
const hooksChain = this.getHooks('Restore', restoreHandlerWrapper)
yield this.composeHooks(scope, hooksChain)
return handlerResult
}
22 changes: 22 additions & 0 deletions src/Lucid/Model/Mixins/Persistance.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,3 +96,25 @@ Peristance.delete = function * () {
}
return yield this.executeDeleteHooks(this, deleteHandler)
}

/**
* restores a soft deleted model instance
* @method *restore
*
* @return {Number} - Number of affected rows
*
* @public
*/
Peristance.restore = function * () {
const restoreHandler = function * () {
const query = this.constructor.query().where('id', this.$primaryKeyValue)
const values = {}
const affected = yield query.restoreAttributes(values)
if (affected > 0) {
this.unfreeze()
_.merge(this.attributes, values)
}
return affected
}
return yield this.executeRestoreHooks(this, restoreHandler)
}
62 changes: 57 additions & 5 deletions src/Lucid/Model/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ class Model {
this.attributes = {}
this.original = {}
this.relations = {}
this.frozen = false
this.eagerLoad = new EagerLoad()
if (values) {
this.setJSON(values)
Expand Down Expand Up @@ -128,6 +129,53 @@ class Model {
this.$modelHooks[type].push({handler, name})
}

/**
* removes an array of named hooks from registered
* hooks
*
* @param {Array} names
*
* @public
*/
static removeHooks () {
const names = _.isArray(arguments[0]) ? arguments[0] : _.toArray(arguments)
_.each(this.$modelHooks, (hooks, type) => {
this.$modelHooks[type] = _.filter(hooks, function (hook) {
return names.indexOf(hook.name) <= -1
})
})
}

/**
* alias of removeHooks
* @see removeHooks
*
* @param {String} name
*
* @public
*/
static removeHook () {
this.removeHooks.apply(this, arguments)
}

/**
* defines an array of hooks in one go.
*
* @param {String} type
* @param {Array} hooks
*
* @public
*/
static defineHooks () {
this.$modelHooks = {}
const args = _.toArray(arguments)
const type = args[0]
const hooks = _.tail(args)
_.each(hooks, (hook) => {
this.addHook(type, hook)
})
}

/**
* store state of model, whether it has been
* booted or not
Expand Down Expand Up @@ -365,7 +413,7 @@ class Model {
* @public
*/
getCreateTimestamp (date) {
return moment(date).format(this.constructor.dateFormat)
return this.formatDate(date)
}

/**
Expand All @@ -378,7 +426,7 @@ class Model {
* @public
*/
getUpdateTimestamp (date) {
return moment(date).format(this.constructor.dateFormat)
return this.formatDate(date)
}

/**
Expand All @@ -391,7 +439,7 @@ class Model {
* @public
*/
getDeleteTimestamp (date) {
return moment(date).format(this.constructor.dateFormat)
return this.formatDate(date)
}

/**
Expand Down Expand Up @@ -552,7 +600,11 @@ class Model {
* @public
*/
freeze () {
Object.freeze(this)
this.frozen = true
}

unfreeze () {
this.frozen = false
}

/**
Expand All @@ -563,7 +615,7 @@ class Model {
* @public
*/
isDeleted () {
return Object.isFrozen(this)
return this.frozen
}

/**
Expand Down
6 changes: 3 additions & 3 deletions src/Lucid/Model/proxyHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

const proxyHandler = exports = module.exports = {}
const NE = require('node-exceptions')
const targetProperties = ['$primaryKeyValue', 'original', 'attributes', 'relations', 'eagerLoad']
const targetProperties = ['$primaryKeyValue', 'original', 'attributes', 'relations', 'eagerLoad', 'frozen']

/**
* proxy handler for getting target properties.Here
Expand All @@ -30,7 +30,7 @@ proxyHandler.get = function (target, name) {
* if value exists on the model instance, we return
* it right away.
*/
if (target[name]) {
if (target[name] !== undefined) {
return target[name]
}

Expand Down Expand Up @@ -65,7 +65,7 @@ proxyHandler.get = function (target, name) {
* @private
*/
proxyHandler.set = function (target, name, value) {
if (target.isDeleted()) {
if (target.isDeleted() && name !== 'frozen') {
throw new NE.RuntimeException('Cannot edit a frozen model')
}
if (targetProperties.indexOf(name) > -1) {
Expand Down
22 changes: 22 additions & 0 deletions src/Lucid/QueryBuilder/methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,28 @@ methods.deleteAttributes = function (target) {
}
methods.delete = methods.deleteAttributes

/**
* restores a soft deleted row
*
* @method restoreAttributes
*
* @param {Object} target
* @return {Promise}
*
* @public
*/
methods.restoreAttributes = function (target) {
return function (values) {
if (!target.HostModel.deleteTimestamp) {
throw new Error('Restore can only be done when soft deletes are enabled.')
}
values = values || {}
values = target.HostModel.prototype.setRestoreTimestamp(values)
return this.updateAttributes(values)
}
}
methods.restore = methods.restoreAttributes

/**
* returns the first record from data collection
*
Expand Down
Loading

0 comments on commit d9329d8

Please sign in to comment.