composr-core
Version:
CompoSR core functions
343 lines (277 loc) • 8.8 kB
JavaScript
'use strict'
var currifiedToArrayPromise = require('../utils/currifiedToArrayPromise')
/**
BaseManager:
Class that creates a flow of execution and some base methods
This class provides the following interface:
- register
- _register
- unregister
- _unregister
- compile
- _compile
- validate
- _extractDomainFromId
- _addToStore
The mandatory methods for the child classes to implement are:
_compile
_addToStore
_unregister
*/
function BaseManager (options) {
this.itemName = options.itemName
this.store = options.store
this.validator = options.validator
this.model = options.model
this.dao = options.dao
}
// Reset the stack
BaseManager.prototype.resetItems = function resetItems () {
this.store.reset()
}
// Entry point for registering or unregistering items
BaseManager.prototype.register = function register (domain, itemOrItems) {
var module = this
if (!domain) {
return Promise.reject('missing domain')
}
var itemsPromisesGenerator = currifiedToArrayPromise(itemOrItems)
return itemsPromisesGenerator(function (item) {
return module._register(domain, item)
},
function (result, item) {
var response = {
registered: false,
id: item.id,
model: null,
error: null
}
if (result instanceof module.model) {
response.registered = true
response.id = result.getId()
response.model = result
} else {
response.error = result
}
return response
})
}
// Register an item on the stack
BaseManager.prototype._register = function _register (domain, item) {
var module = this
return this.validate(item)
.then(function () {
// Returns the MODEL
var modelInstance = module.compile(domain, item)
if (modelInstance) {
module.__preAdd(domain, modelInstance)
var added = module._addToStore(domain, modelInstance)
module.events.emit('debug', module.itemName + ':registered', added, modelInstance.getId())
if (added) {
// Corbel-composr listens to this event for registering routes. And uses item.id
module.events.emit(module.itemName + ':registered', modelInstance)
module.__postAdd(domain, modelInstance)
}
return modelInstance
} else {
module.events.emit('warn', module.itemName + ':not:registered', item.id, item.url)
throw new Error('not:registered')
}
})
.catch(function (err) {
module.events.emit('warn', module.itemName + ':not:registered', module.itemName + ':not:valid', item.id, item.url, err)
return err
})
}
// Registers phrases, extracting domain from id (TODO: Test)
BaseManager.prototype.registerWithoutDomain = function registerWithoutDomain (items) {
var module = this
var promises = []
var itemsHash = {}
items.forEach(function (item) {
var domain = module._extractDomainFromId(item.id)
if (!itemsHash[domain]) {
itemsHash[domain] = []
}
itemsHash[domain].push(item)
})
Object.keys(itemsHash).forEach(function (key) {
promises.push(module.register(key, itemsHash[key]))
})
return Promise.all(promises)
}
// Verifies that a JSON for a item is well formed
BaseManager.prototype.validate = function validate (item) {
return this.validator(item)
.then(function () {
return {
valid: true
}
})
.catch(function (errors) {
var result = {
valid: false,
errors: errors
}
throw result
})
}
// Iterates over the items to compile
BaseManager.prototype.compile = function compile (domain, itemOrItems) {
var module = this
var isArray = Array.isArray(itemOrItems)
if (isArray === false) {
itemOrItems = [itemOrItems]
}
var compiledResults = itemOrItems.map(function (item) {
return module._compile(domain, item)
})
return isArray ? compiledResults : compiledResults[0]
}
// Iterates over the items to unregister
BaseManager.prototype.unregister = function unregister (domain, itemOrItemIds) {
var module = this
var isArray = Array.isArray(itemOrItemIds)
if (isArray === false) {
itemOrItemIds = [itemOrItemIds]
}
var results = itemOrItemIds.map(function (id) {
module.events.emit('debug', module.itemName + ':unregister:' + id)
return module._unregister(domain, id)
})
if (isArray === false) {
return results[0]
} else {
return results
}
}
// Extracts the domain from a json item
BaseManager.prototype._extractDomainFromId = function _extractDomainFromId (id) {
return id.split('!')[0]
}
// Extracts the virtual domain from a jsonItem
BaseManager.prototype._extractVirtualDomainFromId = function _extractVirtualDomainFromId (id) {
return id.split('!')[1]
}
BaseManager.prototype._addToStore = function _addToStore (domain, modelInstance) {
if (!domain || !modelInstance) {
return false
}
if (!modelInstance.getId()) {
return false
}
this.store.add(domain, modelInstance)
return true
}
BaseManager.prototype.getById = function getById (id) {
var domain = this._extractDomainFromId(id)
return this.store.get(domain, id)
}
BaseManager.prototype.getByVirtualDomain = function getByVirtualDomain () {
// TODO: implement
// call this.store.getByVirtualDomain
}
BaseManager.prototype.getByDomain = function getByDomain (domain) {
return this.store.getAsList(domain)
}
/* *******************************
Mandatory implementations
********************************/
BaseManager.prototype._compile = function _compile (domain, item) {
var BaseModel = this.model
var model = new BaseModel(item, domain)
return model
}
// Removes item from memory
BaseManager.prototype._unregister = function _unregister (domain, id) {
if (!domain) {
this.events.emit('warn', this.itemName + ':unregister:missing:parameters', 'domain')
return false
}
if (!id) {
this.events.emit('warn', this.itemName + ':unregister:missing:parameters', 'id')
return false
}
if (this.store.exists(domain, id)) {
this.store.remove(domain, id)
this.events.emit('debug', this.itemName + ':unregistered', domain)
return true
} else {
this.events.emit('warn', this.itemName + ':unregister:not:found', domain)
return false
}
}
/* **************************************************
CRUD interface
****************************************************/
/**
* Loads and register a item if an id is provided, all items if the id is undefined
* @param string id Item id
* @return promise load and register promise
*/
BaseManager.prototype.load = function load (id) {
var module = this
if (id) {
var domain = this._extractDomainFromId(id)
return this.dao.load(id)
.then(function (item) {
return module.register(domain, item)
})
} else {
return this.dao.loadAll()
.then(function (items) {
return module.registerWithoutDomain(items)
})
}
}
/**
* Conditional save.
* - Checks if the json has to be saved
* - Checks if the json is valid
* - If it has to ve saved, calls __save
* - If not, resolves
* - If it is invalid, rejects
*/
BaseManager.prototype.save = function save (domain, json, optionalDriver) {
var BaseModel = this.model
var item = new BaseModel(json, domain) // Constructs the id
var shouldBeSaved = this.__shouldSave(item)
var module = this
return new Promise(function (resolve, reject) {
if (shouldBeSaved) {
module.__save(item.getRawModel(), optionalDriver) // For the json with the ID
.then(function (result) {
module.events.emit('saved:' + module.itemName, result)
resolve(item.getRawModel())
})
.catch(function (aComposrError) {
module.events.emit('error', module.itemName, 'not:saved', item.getId())
reject(aComposrError)
})
} else {
resolve(item.getRawModel())
}
})
}
BaseManager.prototype.__shouldSave = function __shouldSave (item) {
return this.getById(item.getId()) && this.getById(item.getId()).getMD5() !== item.getMD5()
}
BaseManager.prototype.__save = function __save (json, optionalDriver) {
return this.dao.save(json, optionalDriver)
}
/* Delete interface */
BaseManager.prototype.delete = function _delete (id, optionalDriver) {
this.events.emit('debug', 'delete:' + this.itemName, id)
return this.dao.delete(id, optionalDriver)
}
/* *******************************
Optional methods
********************************/
// Pre add call
BaseManager.prototype.__preAdd = function __preAdd () {
this.events.emit('trace', '__preAdd not implemented')
}
BaseManager.prototype.__postAdd = function __postAdd () {
this.events.emit('trace', '__postAdd not implemented')
}
module.exports = BaseManager