A Javascript library for carefully refactoring critical paths. Influenced heavily from github/scientist.
Scientist is built with and accepts Promises (or Functions that return Promises) in any experiment.
Here's a quick and dirty example of how to Science!
var Experiment = require('node-scientist').Experiment
var experiment = new Experiment('getData')
// use: the control. value will be returned by `.run`.
experiment.use(function () { return Promise.resolve().return(true) })
// try: the candidate. value will be reported in `.publish`.
experiment.try(function () { return Promise.resolve().delay(10).return(false) })
// run: run the experiment.
experiment.run()
.then(function (result) {
// result === true, from `.use`.
})
var Scientist = require('node-scientist')
function MyModel () {}
util.inherits(MyModel, Scientist)
MyModel.prototype.myMethod = function (cb) {
var experiment = this.science('myMethod', { Experiment: MyExperiment })
// use: the control. value will be returned by `.run`
experiment.use(function () { return Promise.resolve().delay(10).return(7) })
// try: the candidate. value will be reported in `.publish`.
experiment.try(function () { return Promise.resolve().delay(5).throw(new Error('foo')) })
// run: run the experiment.
// NOTE: bluebird allows `.asCallback`, which can help in models w/ callbacks.
experiment.run().asCallback(cb)
}
var m = new MyModel()
m.myMethod(function (err, value) {
// err === undefined, candidate error is not passed through.
// value === 7, from `.use`.
})
A very simple publisher that will print results to the screen.
var Experiment = require('node-scientist').Experiment
var find = require('101/find')
function MyExperiment (name) {
Experiment.call(this, name)
}
util.inherits(MyExperiment, Experiment)
/**
* Publisher function. Takes a result, must return a Promise.
* @param {Result} result Result object from Scientist.
* @return {Promise} Promise resolved when publishing is done.
*/
MyExperiment.prototype.publish = function (result) {
var control = find(result.observations, hasProperties({ name: 'control' }))
var candidate = find(result.observations, hasProperties({ name: 'candidate' }))
console.log('Results:')
console.log('Correctness (were the candidates correct?):', !result.mismatched() ? 'yes' : 'no')
console.log('Values (control, candidate):', control.value, candidate.value)
console.log('Candidate Time:', candidate.duration)
console.log('Control Time:', control.duration)
console.log('Improvement Time (+larger is better):', control.duration - candidate.duration)
return Promise.resolve(true)
}
This publisher is used like so:
// as the first case, with just an Experiment:
var experiment = new MyExperiment('foobar')
// if you want to include the Scientist in the model:
SomeModel.prototype.myMethod = function (cb) {
var experiment = this.science('myMethod', { Experiment: MyExperiment })
// and continue with the experiment...
}