Merge pull request #621 from strongloop/feature/operation-hooks-overview
Create a script to describe operation hooks
This commit is contained in:
commit
2dddc78b08
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* Describe context objects of operation hooks in comprehensive HTML table.
|
||||
* Usage:
|
||||
* $ node support/describe-operation-hooks.js > hooks.html
|
||||
* $ open hooks.hml
|
||||
*
|
||||
*/
|
||||
var Promise = global.Promise = require('bluebird');
|
||||
var DataSource = require('../').DataSource;
|
||||
var Memory = require('../lib/connectors/memory').Memory;
|
||||
|
||||
var HOOK_NAMES = [
|
||||
'access',
|
||||
'before save', 'persist', 'after save',
|
||||
'before delete', 'after delete'
|
||||
];
|
||||
|
||||
var dataSources = [
|
||||
createOptimizedDataSource(),
|
||||
createUnoptimizedDataSource()
|
||||
];
|
||||
|
||||
var observedContexts = [];
|
||||
var lastId = 0;
|
||||
|
||||
Promise.onPossiblyUnhandledRejection(function(err) {
|
||||
console.error('POSSIBLY UNHANDLED REJECTION', err.stack);
|
||||
});
|
||||
|
||||
var operations = [
|
||||
function find(ds) {
|
||||
return ds.TestModel.find({ where: { id: '1' } });
|
||||
},
|
||||
|
||||
function count(ds) {
|
||||
return ds.TestModel.count({ id: ds.existingInstance.id });
|
||||
},
|
||||
|
||||
function create(ds) {
|
||||
return ds.TestModel.create({ name: 'created' });
|
||||
},
|
||||
|
||||
function findOrCreate_found(ds) {
|
||||
return ds.TestModel.findOrCreate(
|
||||
{ where: { name: ds.existingInstance.name } },
|
||||
{ name: ds.existingInstance.name });
|
||||
},
|
||||
|
||||
function findOrCreate_create(ds) {
|
||||
return ds.TestModel.findOrCreate(
|
||||
{ where: { name: 'new-record' } },
|
||||
{ name: 'new-record' });
|
||||
},
|
||||
|
||||
function updateOrCreate_create(ds) {
|
||||
return ds.TestModel.updateOrCreate({ id: 'not-found', name: 'not found' });
|
||||
},
|
||||
|
||||
function updateOrCreate_update(ds) {
|
||||
return ds.TestModel.updateOrCreate(
|
||||
{ id: ds.existingInstance.id, name: 'new name' });
|
||||
},
|
||||
|
||||
function updateAll(ds) {
|
||||
return ds.TestModel.updateAll({ name: 'searched' }, { name: 'updated' });
|
||||
},
|
||||
|
||||
function prototypeSave(ds) {
|
||||
ds.existingInstance.name = 'changed';
|
||||
return ds.existingInstance.save();
|
||||
},
|
||||
|
||||
function prototypeUpdateAttributes(ds) {
|
||||
return ds.existingInstance.updateAttributes({ name: 'changed' });
|
||||
},
|
||||
|
||||
function prototypeDelete(ds) {
|
||||
return ds.existingInstance.delete();
|
||||
},
|
||||
|
||||
function deleteAll(ds) {
|
||||
return ds.TestModel.deleteAll({ name: ds.existingInstance.name });
|
||||
},
|
||||
];
|
||||
|
||||
var p = setupTestModels();
|
||||
operations.forEach(function(op) {
|
||||
p = p.then(runner(op));
|
||||
});
|
||||
|
||||
p.then(report, console.error);
|
||||
|
||||
|
||||
function createOptimizedDataSource() {
|
||||
var ds = new DataSource({ connector: Memory });
|
||||
ds.name = 'Optimized';
|
||||
|
||||
ds.connector.findOrCreate = function (model, query, data, callback) {
|
||||
this.all(model, query, {}, function (err, list) {
|
||||
if (err || (list && list[0])) return callback(err, list && list[0], false);
|
||||
this.create(model, data, {}, function (err) {
|
||||
callback(err, data, true);
|
||||
});
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
function createUnoptimizedDataSource() {
|
||||
var ds = new DataSource({ connector: Memory });
|
||||
ds.name = 'Unoptimized';
|
||||
|
||||
// disable optimized methods
|
||||
ds.connector.updateOrCreate = false;
|
||||
ds.connector.findOrCreate = false;
|
||||
|
||||
return ds;
|
||||
}
|
||||
|
||||
function setupTestModels() {
|
||||
dataSources.forEach(function setupOnDataSource(ds) {
|
||||
var TestModel = ds.TestModel = ds.createModel('TestModel', {
|
||||
id: { type: String, id: true, default: uid },
|
||||
name: { type: String, required: true },
|
||||
extra: { type: String, required: false }
|
||||
});
|
||||
});
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function uid() {
|
||||
lastId += 1;
|
||||
return '' + lastId;
|
||||
}
|
||||
|
||||
function runner(fn) {
|
||||
return function() {
|
||||
var res = Promise.resolve();
|
||||
dataSources.forEach(function(ds) {
|
||||
res = res.then(function() {
|
||||
return resetStorage(ds);
|
||||
}).then(function() {
|
||||
observedContexts.push({
|
||||
operation: fn.name,
|
||||
connector: ds.name,
|
||||
hooks: {}
|
||||
});
|
||||
return fn(ds);
|
||||
});
|
||||
});
|
||||
return res;
|
||||
};
|
||||
}
|
||||
|
||||
function resetStorage(ds) {
|
||||
var TestModel = ds.TestModel;
|
||||
HOOK_NAMES.forEach(function(hook) {
|
||||
TestModel.clearObservers(hook);
|
||||
});
|
||||
return TestModel.deleteAll()
|
||||
.then(function() {
|
||||
return TestModel.create({ name: 'first' })
|
||||
})
|
||||
.then(function(instance) {
|
||||
// Look it up from DB so that default values are retrieved
|
||||
return TestModel.findById(instance.id)
|
||||
})
|
||||
.then(function(instance) {
|
||||
ds.existingInstance = instance;
|
||||
return TestModel.create({ name: 'second' });
|
||||
})
|
||||
.then(function() {
|
||||
HOOK_NAMES.forEach(function(hook) {
|
||||
TestModel.observe(hook, function(ctx, next) {
|
||||
var row = observedContexts[observedContexts.length-1];
|
||||
row.hooks[hook] = Object.keys(ctx);
|
||||
next();
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function report() {
|
||||
console.log('<style>')
|
||||
console.log('td { font-family: "monospace": }');
|
||||
console.log('td, th {');
|
||||
console.log(' vertical-align: text-top;');
|
||||
console.log(' padding: 0.5em;');
|
||||
console.log(' border-bottom: 1px solid gray;');
|
||||
console.log('</style>');
|
||||
|
||||
// merge rows where Optimized and Unoptimized produce the same context
|
||||
observedContexts.forEach(function(row, ix) {
|
||||
if (!ix) return;
|
||||
var last = observedContexts[ix-1];
|
||||
if (row.operation != last.operation) return;
|
||||
if (JSON.stringify(row.hooks) !== JSON.stringify(last.hooks)) return;
|
||||
last.merge = true;
|
||||
row.skip = true;
|
||||
});
|
||||
|
||||
console.log('<!-- ==== BEGIN DATA ==== -->\n');
|
||||
console.log('<table><thead><tr>\n <th></th>');
|
||||
HOOK_NAMES.forEach(function(h) { console.log(' <th>' + h + '</th>'); });
|
||||
console.log('</tr></thead><tbody>');
|
||||
|
||||
observedContexts.forEach(function(row) {
|
||||
if (row.skip) return;
|
||||
var caption = row.operation;
|
||||
if (!row.merge) caption += ' (' + row.connector + ')';
|
||||
console.log('<tr><th>' + caption + '</th>');
|
||||
HOOK_NAMES.forEach(function(h) {
|
||||
var text = row.hooks[h] ? row.hooks[h].join('<br/>') : '';
|
||||
console.log(' <td>' + text + '</td>');
|
||||
});
|
||||
console.log('</tr>');
|
||||
});
|
||||
console.log('</table>');
|
||||
}
|
Loading…
Reference in New Issue