Commit 3e152964 authored by Tristan Cavelier's avatar Tristan Cavelier

promy replaced by RSVP library

parent 78fe0cd7
...@@ -14,12 +14,6 @@ module.exports = function (grunt) { ...@@ -14,12 +14,6 @@ module.exports = function (grunt) {
errorsOnly: true errorsOnly: true
} }
}, },
promy: {
src: ['src/promy/**/*.js'],
options: {
errorsOnly: true
}
},
jio: { jio: {
src: ['src/jio/**/*.js'], src: ['src/jio/**/*.js'],
exclude: ['src/jio/intro.js', 'src/jio/outro.js'], exclude: ['src/jio/intro.js', 'src/jio/outro.js'],
...@@ -84,10 +78,6 @@ module.exports = function (grunt) { ...@@ -84,10 +78,6 @@ module.exports = function (grunt) {
banner: '/*! <%= pkg.name %> <%= pkg.version %> ' + banner: '/*! <%= pkg.name %> <%= pkg.version %> ' +
'<%= grunt.template.today("yyyy-mm-dd") %> */\n' '<%= grunt.template.today("yyyy-mm-dd") %> */\n'
}, },
promy: {
src: 'src/promy/promy.js',
dest: 'promy.min.js'
},
jio: { jio: {
src: 'jio.js', // '<%= pkg.name %>.js' src: 'jio.js', // '<%= pkg.name %>.js'
dest: 'jio.min.js' dest: 'jio.min.js'
......
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, regexp: true */
/*global constants, dictUpdate, deepClone */
function restCommandRejecter(param, args) {
// reject(status, reason, message, {"custom": "value"});
// reject(status, reason, {..});
// reject(status, {..});
var a = args[0], b = args[1], c = args[2], d = args[3], weak, strong;
weak = {"result": "error"};
strong = {};
weak.status = constants.http_status.unknown;
weak.statusText = constants.http_status_text.unknown;
weak.message = 'Command failed';
weak.reason = 'fail';
weak.method = param.method;
if (param.kwargs._id) {
weak.id = param.kwargs._id;
}
if (/Attachment$/.test(param.method)) {
weak.attachment = param.kwargs._attachment;
}
if (typeof a !== 'object' || Array.isArray(a)) {
strong.status = constants.http_status[a];
strong.statusText = constants.http_status_text[a];
if (strong.status === undefined ||
strong.statusText === undefined) {
return restCommandRejecter(param, [
// can create infernal loop if 'internal_storage_error' is not defined
// in the constants
'internal_storage_error',
'invalid response',
'Unknown status "' + a + '"'
]);
}
a = b;
b = c;
c = d;
}
if (typeof a !== 'object' || Array.isArray(a)) {
strong.reason = a;
a = b;
b = c;
}
if (typeof a !== 'object' || Array.isArray(a)) {
strong.message = a;
a = b;
}
if (typeof a === 'object' && !Array.isArray(a)) {
dictUpdate(weak, a);
}
dictUpdate(weak, strong);
strong = undefined;
if (weak.error === undefined) {
weak.error = weak.statusText.toLowerCase().replace(/ /g, '_').
replace(/[^_a-z]/g, '');
}
if (typeof weak.message !== 'string') {
weak.message = "";
}
if (typeof weak.reason !== 'string') {
weak.reason = "unknown";
}
return param.solver.reject(deepClone(weak));
}
/*jslint indent: 2, maxlen: 80, nomen: true, sloppy: true, regexp: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global Deferred, inherits, constants, dictUpdate, deepClone, Blob, /*global constants, methodType, dictUpdate, Blob, deepClone,
methodType */ restCommandRejecter */
function IODeferred(method, info) { function restCommandResolver(param, args) {
IODeferred.super_.call(this);
this._info = info || {};
this._method = method;
// this._options = options;
}
inherits(IODeferred, Deferred);
IODeferred.prototype.resolve = function (a, b) {
// resolve('ok', {"custom": "value"}); // resolve('ok', {"custom": "value"});
// resolve(200, {...}); // resolve(200, {...});
// resolve({...}); // resolve({...});
var weak = {"result": "success"}, strong = {}; var a = args[0], b = args[1], weak = {"result": "success"}, strong = {};
if (this._method === 'post') { if (param.method === 'post') {
weak.status = constants.http_status.created; weak.status = constants.http_status.created;
weak.statusText = constants.http_status_text.created; weak.statusText = constants.http_status_text.created;
} else if (methodType(this._method) === "writer" || } else if (methodType(param.method) === "writer" ||
this._method === "check") { param.method === "check") {
weak.status = constants.http_status.no_content; weak.status = constants.http_status.no_content;
weak.statusText = constants.http_status_text.no_content; weak.statusText = constants.http_status_text.no_content;
} else { } else {
weak.status = constants.http_status.ok; weak.status = constants.http_status.ok;
weak.statusText = constants.http_status_text.ok; weak.statusText = constants.http_status_text.ok;
} }
if (this._info._id) { if (param.kwargs._id) {
weak.id = this._info._id; weak.id = param.kwargs._id;
} }
if (/Attachment$/.test(this._method)) { if (/Attachment$/.test(param.method)) {
weak.attachment = this._info._attachment; weak.attachment = param.kwargs._attachment;
} }
weak.method = this._method; weak.method = param.method;
if (typeof a === 'string' || (typeof a === 'number' && isFinite(a))) { if (typeof a === 'string' || (typeof a === 'number' && isFinite(a))) {
strong.status = constants.http_status[a]; strong.status = constants.http_status[a];
strong.statusText = constants.http_status_text[a]; strong.statusText = constants.http_status_text[a];
if (strong.status === undefined || if (strong.status === undefined ||
strong.statusText === undefined) { strong.statusText === undefined) {
return this.reject( return restCommandRejecter(param, [
'internal_storage_error', 'internal_storage_error',
'invalid response', 'invalid response',
'Unknown status "' + a + '"' 'Unknown status "' + a + '"'
); ]);
} }
a = b; a = b;
} }
...@@ -52,14 +44,14 @@ IODeferred.prototype.resolve = function (a, b) { ...@@ -52,14 +44,14 @@ IODeferred.prototype.resolve = function (a, b) {
} }
dictUpdate(weak, strong); dictUpdate(weak, strong);
strong = undefined; // free memory strong = undefined; // free memory
if (this._method === 'post' && (typeof weak.id !== 'string' || !weak.id)) { if (param.method === 'post' && (typeof weak.id !== 'string' || !weak.id)) {
return this.reject( return restCommandRejecter(param, [
'internal_storage_error', 'internal_storage_error',
'invalid response', 'invalid response',
'New document id have to be specified' 'New document id have to be specified'
); ]);
} }
if (this._method === 'getAttachment') { if (param.method === 'getAttachment') {
if (typeof weak.data === 'string') { if (typeof weak.data === 'string') {
weak.data = new Blob([weak.data], { weak.data = new Blob([weak.data], {
"type": weak.content_type || weak.mimetype || "" "type": weak.content_type || weak.mimetype || ""
...@@ -68,111 +60,21 @@ IODeferred.prototype.resolve = function (a, b) { ...@@ -68,111 +60,21 @@ IODeferred.prototype.resolve = function (a, b) {
delete weak.mimetype; delete weak.mimetype;
} }
if (!(weak.data instanceof Blob)) { if (!(weak.data instanceof Blob)) {
return this.reject( return restCommandRejecter(param, [
'internal_storage_error', 'internal_storage_error',
'invalid response', 'invalid response',
'getAttachment method needs a Blob as returned "data".' 'getAttachment method needs a Blob as returned "data".'
); ]);
} }
} else if (methodType(this._method) === 'reader' && } else if (methodType(param.method) === 'reader' &&
this._method !== 'check' && param.method !== 'check' &&
(typeof weak.data !== 'object' || (typeof weak.data !== 'object' ||
Object.getPrototypeOf(weak.data) !== Object.prototype)) { Object.getPrototypeOf(weak.data) !== Object.prototype)) {
return this.reject( return restCommandRejecter(param, [
'internal_storage_error', 'internal_storage_error',
'invalid response', 'invalid response',
this._method + ' method needs a dict as returned "data".' param.method + ' method needs a dict as returned "data".'
); ]);
}
//return super_resolve(deepClone(weak));
return IODeferred.super_.prototype.resolve.call(this, deepClone(weak));
};
IODeferred.prototype.reject = function (a, b, c, d) {
// reject(status, reason, message, {"custom": "value"});
// reject(status, reason, {..});
// reject(status, {..});
var weak = {"result": "error"}, strong = {};
weak.status = constants.http_status.unknown;
weak.statusText = constants.http_status_text.unknown;
weak.message = 'Command failed';
weak.reason = 'fail';
weak.method = this._method;
if (this._info._id) {
weak.id = this._info._id;
}
if (/Attachment$/.test(this._method)) {
weak.attachment = this._info._attachment;
}
if (typeof a !== 'object' || Array.isArray(a)) {
strong.status = constants.http_status[a];
strong.statusText = constants.http_status_text[a];
if (strong.status === undefined ||
strong.statusText === undefined) {
return this.reject(
// can create infernal loop if 'internal_storage_error' is not defined
// in the constants
'internal_storage_error',
'invalid response',
'Unknown status "' + a + '"'
);
}
a = b;
b = c;
c = d;
}
if (typeof a !== 'object' || Array.isArray(a)) {
strong.reason = a;
a = b;
b = c;
}
if (typeof a !== 'object' || Array.isArray(a)) {
strong.message = a;
a = b;
}
if (typeof a === 'object' && !Array.isArray(a)) {
dictUpdate(weak, a);
} }
return param.solver.resolve(deepClone(weak));
dictUpdate(weak, strong); }
strong = undefined;
if (weak.error === undefined) {
weak.error = weak.statusText.toLowerCase().replace(/ /g, '_').
replace(/[^_a-z]/g, '');
}
if (typeof weak.message !== 'string') {
weak.message = "";
}
if (typeof weak.reason !== 'string') {
weak.reason = "unknown";
}
//return super_reject(deepClone(weak));
return IODeferred.super_.prototype.reject.call(this, deepClone(weak));
};
IODeferred.createFromDeferred = function (method, info, options, deferred) {
var iodeferred = new IODeferred(method, info, options);
// iodeferred.promise().done(deferred.resolve.bind(deferred)).
// fail(deferred.reject.bind(deferred)).
// progress(deferred.notify.bind(deferred));
// // phantomjs doesn't like 'bind'...
iodeferred.promise.then(
deferred.resolve.bind(deferred),
deferred.reject.bind(deferred),
deferred.notify.bind(deferred)
);
return iodeferred;
};
IODeferred.createFromParam = function (param) {
return IODeferred.createFromDeferred(
param.method,
param.kwargs,
param.options,
param.deferred
);
};
/*jslint indent: 2, maxlen: 80, nomen: true, sloppy: true */ /*jslint indent: 2, maxlen: 80, nomen: true, sloppy: true */
/*global exports, Blob, FileReader, Deferred, hex_sha256, XMLHttpRequest, /*global exports, Blob, FileReader, RSVP, hex_sha256, XMLHttpRequest,
constants */ constants */
/** /**
...@@ -310,32 +310,41 @@ function makeBinaryStringDigest(string) { ...@@ -310,32 +310,41 @@ function makeBinaryStringDigest(string) {
exports.util.makeBinaryStringDigest = makeBinaryStringDigest; exports.util.makeBinaryStringDigest = makeBinaryStringDigest;
function readBlobAsBinaryString(blob) { function readBlobAsBinaryString(blob) {
var deferred = new Deferred(), fr = new FileReader(); var fr = new FileReader();
fr.addEventListener("load", deferred.resolve.bind(deferred)); return new RSVP.Promise(function (resolve, reject, notify) {
fr.addEventListener("error", deferred.reject.bind(deferred)); fr.addEventListener("load", resolve);
fr.addEventListener("progress", deferred.notify.bind(deferred)); fr.addEventListener("error", reject);
fr.readAsBinaryString(blob); fr.addEventListener("progress", notify);
return deferred.promise; fr.readAsBinaryString(blob);
}, function () {
fr.abort();
});
} }
exports.util.readBlobAsBinaryString = readBlobAsBinaryString; exports.util.readBlobAsBinaryString = readBlobAsBinaryString;
function readBlobAsArrayBuffer(blob) { function readBlobAsArrayBuffer(blob) {
var deferred = new Deferred(), fr = new FileReader(); var fr = new FileReader();
fr.addEventListener("load", deferred.resolve.bind(deferred)); return new RSVP.Promise(function (resolve, reject, notify) {
fr.addEventListener("error", deferred.reject.bind(deferred)); fr.addEventListener("load", resolve);
fr.addEventListener("progress", deferred.notify.bind(deferred)); fr.addEventListener("error", reject);
fr.readAsArrayBuffer(blob); fr.addEventListener("progress", notify);
return deferred.promise; fr.readAsArrayBuffer(blob);
}, function () {
fr.abort();
});
} }
exports.util.readBlobAsArrayBuffer = readBlobAsArrayBuffer; exports.util.readBlobAsArrayBuffer = readBlobAsArrayBuffer;
function readBlobAsText(blob) { function readBlobAsText(blob) {
var deferred = new Deferred(), fr = new FileReader(); var fr = new FileReader();
fr.addEventListener("load", deferred.resolve.bind(deferred)); return new RSVP.Promise(function (resolve, reject, notify) {
fr.addEventListener("error", deferred.reject.bind(deferred)); fr.addEventListener("load", resolve);
fr.addEventListener("progress", deferred.notify.bind(deferred)); fr.addEventListener("error", reject);
fr.readAsText(blob); fr.addEventListener("progress", notify);
return deferred.promise; fr.readAsText(blob);
}, function () {
fr.abort();
});
} }
exports.util.readBlobAsText = readBlobAsText; exports.util.readBlobAsText = readBlobAsText;
...@@ -355,29 +364,33 @@ exports.util.readBlobAsText = readBlobAsText; ...@@ -355,29 +364,33 @@ exports.util.readBlobAsText = readBlobAsText;
* @return {Promise} The promise * @return {Promise} The promise
*/ */
function ajax(param) { function ajax(param) {
var k, xhr = new XMLHttpRequest(), deferred = new Deferred(); var xhr = new XMLHttpRequest();
xhr.open(param.type || "GET", param.url, true); return new RSVP.Promise(function (resolve, reject, notify) {
xhr.responseType = param.dataType || ""; var k;
if (typeof param.headers === 'object' && param.headers !== null) { xhr.open(param.type || "GET", param.url, true);
for (k in param.headers) { xhr.responseType = param.dataType || "";
if (param.headers.hasOwnProperty(k)) { if (typeof param.headers === 'object' && param.headers !== null) {
xhr.setRequestHeader(k, param.headers[k]); for (k in param.headers) {
if (param.headers.hasOwnProperty(k)) {
xhr.setRequestHeader(k, param.headers[k]);
}
} }
} }
} xhr.addEventListener("load", function (e) {
xhr.addEventListener("load", function (e) { if (e.target.status >= 400) {
if (e.target.status >= 400) { return reject(e);
return deferred.reject(e); }
resolve(e);
});
xhr.addEventListener("error", reject);
xhr.addEventListener("progress", notify);
if (typeof param.beforeSend === 'function') {
param.beforeSend(xhr);
} }
deferred.resolve(e); xhr.send(param.data);
}, function () {
xhr.abort();
}); });
xhr.addEventListener("error", deferred.reject.bind(deferred));
xhr.addEventListener("progress", deferred.notify.bind(deferred));
if (typeof param.beforeSend === 'function') {
param.beforeSend(xhr);
}
xhr.send(param.data);
return deferred.promise;
} }
exports.util.ajax = ajax; exports.util.ajax = ajax;
......
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global arrayInsert, indexOf, deepClone, defaults, IODeferred */ /*global arrayInsert, indexOf, deepClone, defaults, restCommandRejecter */
// creates // creates
// - some defaults job rule actions // - some defaults job rule actions
...@@ -8,11 +8,12 @@ function enableJobChecker(jio, shared, options) { ...@@ -8,11 +8,12 @@ function enableJobChecker(jio, shared, options) {
// dependencies // dependencies
// - shared.jobs Object Array // - shared.jobs Object Array
// - param.promise Object
// creates // creates
// - shared.job_rules Array // - shared.job_rules Array
// uses 'job' events // uses 'job' event
var i; var i;
...@@ -20,24 +21,25 @@ function enableJobChecker(jio, shared, options) { ...@@ -20,24 +21,25 @@ function enableJobChecker(jio, shared, options) {
shared.job_rule_actions = { shared.job_rule_actions = {
wait: function (original_job, new_job) { wait: function (original_job, new_job) {
original_job.deferred.promise.always(function () { original_job.promise.always(function () {
shared.emit('job', new_job); shared.emit('job', new_job);
}); });
new_job.state = 'waiting'; new_job.state = 'waiting';
new_job.modified = new Date(); new_job.modified = new Date();
}, },
update: function (original_job, new_job) { update: function (original_job, new_job) {
if (!new_job.deferred) { if (!new_job.solver) {
// promise associated to the job // promise associated to the job
new_job.state = 'done'; new_job.state = 'done';
shared.emit('jobDone', new_job); shared.emit('jobDone', new_job);
} else { } else {
if (!original_job.deferred) { if (!original_job.solver) {
original_job.deferred = new_job.deferred; original_job.solver = new_job.solver;
} else { } else {
original_job.deferred.promise. original_job.promise.then(
done(new_job.command.resolve). new_job.command.resolve,
fail(new_job.command.reject); new_job.command.reject
);
} }
} }
new_job.state = 'running'; new_job.state = 'running';
...@@ -46,11 +48,11 @@ function enableJobChecker(jio, shared, options) { ...@@ -46,11 +48,11 @@ function enableJobChecker(jio, shared, options) {
deny: function (original_job, new_job) { deny: function (original_job, new_job) {
new_job.state = 'fail'; new_job.state = 'fail';
new_job.modified = new Date(); new_job.modified = new Date();
IODeferred.createFromParam(new_job).reject( restCommandRejecter(new_job, [
'precondition_failed', 'precondition_failed',
'command denied', 'command denied',
'Command rejected by the job checker.' 'Command rejected by the job checker.'
); ]);
} }
}; };
......
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global setTimeout, Job, createStorage, deepClone, IODeferred, min */ /*global setTimeout, Job, createStorage, deepClone, min, restCommandResolver,
restCommandRejecter */
function enableJobExecuter(jio, shared) { // , options) { function enableJobExecuter(jio, shared) { // , options) {
...@@ -46,44 +47,30 @@ function enableJobExecuter(jio, shared) { // , options) { ...@@ -46,44 +47,30 @@ function enableJobExecuter(jio, shared) { // , options) {
}); });
shared.on('jobDone', function (param, args) { shared.on('jobDone', function (param, args) {
var d;
if (param.state === 'running') { if (param.state === 'running') {
param.state = 'done'; param.state = 'done';
param.modified = new Date(); param.modified = new Date();
shared.emit('jobEnd', param); shared.emit('jobEnd', param);
if (param.deferred) { if (param.solver) {
d = IODeferred.createFromDeferred( restCommandResolver(param, args);
param.method,
param.kwargs,
param.options,
param.deferred
);
d.resolve.apply(d, args);
} }
} }
}); });
shared.on('jobFail', function (param, args) { shared.on('jobFail', function (param, args) {
var d;
if (param.state === 'running') { if (param.state === 'running') {
param.state = 'fail'; param.state = 'fail';
param.modified = new Date(); param.modified = new Date();
shared.emit('jobEnd', param); shared.emit('jobEnd', param);
if (param.deferred) { if (param.solver) {
d = IODeferred.createFromDeferred( restCommandRejecter(param, args);
param.method,
param.kwargs,
param.options,
param.deferred
);
d.reject.apply(d, args);
} }
} }
}); });
shared.on('jobNotify', function (param, args) { shared.on('jobNotify', function (param, args) {
if (param.state === 'running' && param.deferred) { if (param.state === 'running' && param.solver) {
param.deferred.notify.apply(param.deferred, args); param.solver.notify(args[0]);
} }
}); });
} }
...@@ -58,7 +58,7 @@ function enableJobMaker(jio, shared, options) { ...@@ -58,7 +58,7 @@ function enableJobMaker(jio, shared, options) {
shared.rest_method_names.forEach(function (method) { shared.rest_method_names.forEach(function (method) {
shared.on(method, function (param) { shared.on(method, function (param) {
if (param.deferred) { if (param.solver) {
// params are good // params are good
shared.emit('job', param); shared.emit('job', param);
} }
......
/*jslint indent: 2, maxlen: 80, sloppy: true */ /*jslint indent: 2, maxlen: 80, sloppy: true */
/*global arrayValuesToTypeDict, dictClear, Deferred, deepClone */ /*global arrayValuesToTypeDict, dictClear, RSVP, deepClone */
// adds methods to JIO // adds methods to JIO
// - post // - post
...@@ -18,7 +18,12 @@ ...@@ -18,7 +18,12 @@
// - method string // - method string
// - kwargs object // - kwargs object
// - options object // - options object
// - command object // - solver object
// - solver.resolve function
// - solver.reject function
// - solver.notify function
// - cancellers object
// - promise object
function enableRestAPI(jio, shared) { // (jio, shared, options) function enableRestAPI(jio, shared) { // (jio, shared, options)
...@@ -36,7 +41,7 @@ function enableRestAPI(jio, shared) { // (jio, shared, options) ...@@ -36,7 +41,7 @@ function enableRestAPI(jio, shared) { // (jio, shared, options)
]; ];
function prepareParamAndEmit(method, storage_spec, args) { function prepareParamAndEmit(method, storage_spec, args) {
var promise, callback, type_dict, param = {}; var callback, type_dict, param = {};
type_dict = arrayValuesToTypeDict(Array.prototype.slice.call(args)); type_dict = arrayValuesToTypeDict(Array.prototype.slice.call(args));
type_dict.object = type_dict.object || []; type_dict.object = type_dict.object || [];
if (method !== 'allDocs') { if (method !== 'allDocs') {
...@@ -49,31 +54,38 @@ function enableRestAPI(jio, shared) { // (jio, shared, options) ...@@ -49,31 +54,38 @@ function enableRestAPI(jio, shared) { // (jio, shared, options)
} else { } else {
param.kwargs = {}; param.kwargs = {};
} }
param.solver = {};
param.options = deepClone(type_dict.object.shift()) || {}; param.options = deepClone(type_dict.object.shift()) || {};
//param.deferred = new IODeferred(method, param.kwargs, param.options); param.promise = new RSVP.Promise(function (resolve, reject, notify) {
param.deferred = new Deferred(); param.solver.resolve = resolve;
promise = param.deferred.promise; param.solver.reject = reject;
param.solver.notify = notify;
}, function () {
var k;
for (k in param.cancellers) {
if (param.cancellers.hasOwnProperty(k)) {
param.cancellers[k]();
}
}
});
type_dict['function'] = type_dict['function'] || []; type_dict['function'] = type_dict['function'] || [];
if (type_dict['function'].length === 1) { if (type_dict['function'].length === 1) {
callback = type_dict['function'].shift(); callback = type_dict['function'][0];
promise.done(function (answer) { param.promise.then(function (answer) {
callback(undefined, answer); callback(undefined, answer);
}); }, function (answer) {
promise.fail(function (answer) {
callback(answer, undefined); callback(answer, undefined);
}); });
} else if (type_dict['function'].length > 1) { } else if (type_dict['function'].length > 1) {
promise.done(type_dict['function'].shift()); param.promise.then(type_dict['function'][0],
promise.fail(type_dict['function'].shift()); type_dict['function'][1],
if (type_dict['function'].length === 1) { type_dict['function'][2]);
promise.always(type_dict['function'].shift());
}
} }
type_dict = dictClear(type_dict); type_dict = dictClear(type_dict);
param.storage_spec = storage_spec; param.storage_spec = storage_spec;
param.method = method; param.method = method;
shared.emit(method, param); shared.emit(method, param);
return promise; return param.promise;
} }
shared.createRestApi = function (storage_spec, that) { shared.createRestApi = function (storage_spec, that) {
......
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */ /*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true, unparam: true */
/*global Blob, IODeferred, Metadata */ /*global Blob, restCommandRejecter, Metadata */
function enableRestParamChecker(jio, shared) { function enableRestParamChecker(jio, shared) {
// dependencies // dependencies
// - param.deferred // - param.solver
// - param.kwargs // - param.kwargs
// checks the kwargs and convert value if necessary // checks the kwargs and convert value if necessary
...@@ -17,12 +17,12 @@ function enableRestParamChecker(jio, shared) { ...@@ -17,12 +17,12 @@ function enableRestParamChecker(jio, shared) {
function checkId(param) { function checkId(param) {
if (typeof param.kwargs._id !== 'string' || param.kwargs._id === '') { if (typeof param.kwargs._id !== 'string' || param.kwargs._id === '') {
IODeferred.createFromParam(param).reject( restCommandRejecter(param, [
'bad_request', 'bad_request',
'wrong document id', 'wrong document id',
'Document id must be a non empty string.' 'Document id must be a non empty string.'
); ]);
delete param.deferred; delete param.solver;
return false; return false;
} }
return true; return true;
...@@ -31,12 +31,12 @@ function enableRestParamChecker(jio, shared) { ...@@ -31,12 +31,12 @@ function enableRestParamChecker(jio, shared) {
function checkAttachmentId(param) { function checkAttachmentId(param) {
if (typeof param.kwargs._attachment !== 'string' || if (typeof param.kwargs._attachment !== 'string' ||
param.kwargs._attachment === '') { param.kwargs._attachment === '') {
IODeferred.createFromParam(param).reject( restCommandRejecter(param, [
'bad_request', 'bad_request',
'wrong attachment id', 'wrong attachment id',
'Attachment id must be a non empty string.' 'Attachment id must be a non empty string.'
); ]);
delete param.deferred; delete param.solver;
return false; return false;
} }
return true; return true;
...@@ -84,15 +84,15 @@ function enableRestParamChecker(jio, shared) { ...@@ -84,15 +84,15 @@ function enableRestParamChecker(jio, shared) {
delete param.kwargs._mimetype; delete param.kwargs._mimetype;
delete param.kwargs._content_type; delete param.kwargs._content_type;
} else { } else {
IODeferred.createFromParam(param).reject( restCommandRejecter(param, [
'bad_request', 'bad_request',
'wrong attachment', 'wrong attachment',
'Attachment information must be like {"_id": document id, ' + 'Attachment information must be like {"_id": document id, ' +
'"_attachment": attachment name, "_data": string, ["_mimetype": ' + '"_attachment": attachment name, "_data": string, ["_mimetype": ' +
'content type]} or {"_id": document id, "_attachment": ' + 'content type]} or {"_id": document id, "_attachment": ' +
'attachment name, "_blob": Blob}' 'attachment name, "_blob": Blob}'
); ]);
delete param.deferred; delete param.solver;
} }
}); });
......
...@@ -4,11 +4,11 @@ ...@@ -4,11 +4,11 @@
return define(dependencies, module); return define(dependencies, module);
} }
if (typeof exports === 'object') { if (typeof exports === 'object') {
return module(exports, require('promy'), require('sha256')); return module(exports, require('rsvp'), require('sha256'));
} }
window.jIO = {}; window.jIO = {};
module(window.jIO, promy, {hex_sha256: hex_sha256}); module(window.jIO, RSVP, {hex_sha256: hex_sha256});
}(['exports', 'promy', 'sha256'], function (exports, promy, sha256) { }(['exports', 'rsvp', 'sha256'], function (exports, RSVP, sha256) {
"use strict"; "use strict";
var hex_sha256 = sha256.hex_sha256, Deferred = promy.Deferred; var hex_sha256 = sha256.hex_sha256;
/*
* Promy: Promises library
* Copyright (C) 2013 Nexedi SA
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/*jslint indent: 2, maxlen: 80, sloppy: true, nomen: true */
/*global setInterval, setTimeout, clearInterval, clearTimeout */
(function (dependencies, module) {
"use strict";
/*global define, exports, window */
if (typeof define === 'function' && define.amd) {
return define(dependencies, module);
}
if (typeof exports === 'object') {
module(exports);
}
if (typeof window === 'object') {
window.promy = {};
module(window.promy);
}
}(['exports'], function (exports) {
"use strict";
var UNRESOLVED = 0, RESOLVED = 1, REJECTED = 2, CANCELLED = 3;
/**
* thenItem(item, [onSucess], [onError], [onProgress]): any
*
* Execute one of the given callback when the item is fulfilled. If the item
* is not a promise, then onSuccess is called with the item as first
* parameter.
*
* @param {Any} item A promise, deferred or a simple value
* @param {Function} [onSuccess] The callback to call on resolve
* @param {Function} [onError] The callback to call on reject
* @param {Function} [onProgress] The callback to call on notify
*/
function thenItem(item, onSuccess, onError, onProgress) {
if (typeof item === 'object' && item !== null) {
if (typeof item.promise === 'object' && item.promise !== null &&
typeof item.promise.then === 'function') {
// item seams to be a Deferred
return item.promise.then(
onSuccess,
onError,
onProgress
);
}
if (typeof item.then === 'function') {
// item seams to be a Promise
return item.then(
onSuccess,
onError,
onProgress
);
}
}
return onSuccess(item);
}
/**
* promiseResolve(promise, answers): any
*
* Resolve the promise with the given answers.
*
* @param {Promise} promise The promise to resolve
* @param {Array} answers The arguments to give
*/
function promiseResolve(promise, answers) {
var array;
if (promise._state === UNRESOLVED) {
promise._state = RESOLVED;
promise._answers = answers;
array = promise._onResolve.slice();
setTimeout(function () {
var i;
for (i = 0; i < array.length; i += 1) {
try {
array[i].apply(promise, promise._answers);
} catch (ignore) {} // errors will never be retrieved by global
}
});
// free the memory
promise._onResolve = undefined;
promise._onReject = undefined;
promise._onProgress = undefined;
}
}
/**
* promiseReject(promise, answers): any
*
* Reject the promise with the given answers.
*
* @param {Promise} promise The promise to reject
* @param {Array} answers The arguments to give
*/
function promiseReject(promise, answers) {
var array;
if (promise._state === UNRESOLVED) {
promise._state = REJECTED;
promise._answers = answers;
array = promise._onReject.slice();
setTimeout(function () {
var i;
for (i = 0; i < array.length; i += 1) {
try {
array[i].apply(promise, promise._answers);
} catch (ignore) {} // errors will never be retrieved by global
}
});
// free the memory
promise._onResolve = undefined;
promise._onReject = undefined;
promise._onProgress = undefined;
}
}
/**
* promiseNotify(promise, answers): any
*
* Notify the promise with the given answers.
*
* @param {Promise} promise The promise to notify
* @param {Array} answers The arguments to give
*/
function promiseNotify(promise, answers) {
var i;
if (promise._onProgress) {
for (i = 0; i < promise._onProgress.length; i += 1) {
try {
promise._onProgress[i].apply(promise, answers);
} catch (ignore) {} // errors will never be retrieved by global
}
}
}
/**
* Promise(cancel)
*
* @class Promise
* @constructor
*/
function Promise(cancel) {
this._onReject = [];
this._onResolve = [];
this._onProgress = [];
this._state = UNRESOLVED;
if (typeof cancel === 'function') {
this._cancel = cancel;
}
}
////////////////////////////////////////////////////////////
// http://wiki.commonjs.org/wiki/Promises/A
// then(fulfilledHandler, errorHandler, progressHandler)
/**
* then([onSuccess], [onError], [onProgress]): Promise
*
* Returns a new Promise with the return value of the `onSuccess` or `onError`
* callback as first parameter. If the pervious promise is resolved, the
* `onSuccess` callback is called. If rejected, the `onError` callback is
* called. If notified, `onProgress` is called.
*
* Deferred.when(1).
* then(function (one) { return one + 1; }).
* then(console.log); // shows 2
*
* @method then
* @param {Function} [onSuccess] The callback to call on resolve
* @param {Function} [onError] The callback to call on reject
* @param {Function} [onProgress] The callback to call on notify
* @return {Promise} The new promise
*/
Promise.prototype.then = function (onSuccess, onError, onProgress) {
/*global Deferred*/
var defer, next = new this.constructor(this._cancel), that = this;
defer = new Deferred();
defer.promise = next;
switch (this._state) {
case RESOLVED:
if (typeof onSuccess === 'function') {
setTimeout(function () {
try {
thenItem(
onSuccess.apply(that, that._answers),
defer.resolve.bind(defer),
defer.reject.bind(defer)
);
} catch (e) {
defer.reject(e);
}
});
} else {
setTimeout(function () {
defer.resolve.apply(defer, that._answers);
});
}
break;
case REJECTED:
if (typeof onError === 'function') {
setTimeout(function () {
var result;
try {
result = onError.apply(that, that._answers);
if (result === undefined) {
return defer.reject.apply(defer, that._answers);
}
thenItem(
result,
defer.reject.bind(defer),
defer.reject.bind(defer)
);
} catch (e) {
defer.reject(e);
}
});
} else {
setTimeout(function () {
defer.reject.apply(defer, that._answers);
});
}
break;
case UNRESOLVED:
if (typeof onSuccess === 'function') {
this._onResolve.push(function () {
try {
thenItem(
onSuccess.apply(that, arguments),
defer.resolve.bind(defer),
defer.reject.bind(defer),
defer.notify.bind(defer)
);
} catch (e) {
defer.reject(e);
}
});
} else {
this._onResolve.push(function () {
defer.resolve.apply(defer, arguments);
});
}
if (typeof onError === 'function') {
this._onReject.push(function () {
try {
thenItem(
onError.apply(that, that._answers),
defer.reject.bind(defer),
defer.reject.bind(defer)
);
} catch (e) {
defer.reject(e);
}
});
} else {
this._onReject.push(function () {
defer.reject.apply(defer, that._answers);
});
}
if (typeof onProgress === 'function') {
this._onProgress.push(function () {
var result;
try {
result = onProgress.apply(that, arguments);
if (result === undefined) {
defer.notify.apply(defer, arguments);
} else {
defer.notify(result);
}
} catch (e) {
defer.notify.apply(defer, arguments);
}
});
} else {
this._onProgress.push(function () {
defer.notify.apply(defer, arguments);
});
}
break;
default:
break;
}
return next;
};
// p.resolve() ?
// p.reject() ?
// p.notify() ?
/**
* p.cancel(): p
*
* Cancels the operation by calling promise._cancel().
*
* @method cancel
* @return {Promise} this
*/
Promise.prototype.cancel = function () {
if (this._state === UNRESOLVED) {
this._state = CANCELLED;
if (typeof this._cancel === 'function') {
this._cancel();
}
this._onResolve = undefined;
this._onReject = undefined;
this._onProgress = undefined;
}
return this;
};
/**
* p.timeout(delay): p
*
* Reject the promise with an Error("Timeout") and cancel the operation.
*
* @method timeout
* @param {Number} delay The delay before rejection
* @return {Promise} this
*/
Promise.prototype.timeout = function (delay) {
return exports.choose(this, exports.delay(delay).then(function () {
throw new Error("Timeout (" + delay + ")");
}));
};
////////////////////////////////////////////////////////////
// http://wiki.commonjs.org/wiki/Promises/A
// get(propertyName)
/**
* get(property): Promise
*
* Get the property of the promise response as first parameter of the new
* Promise.
*
* Deferred.when({'a': 'b'}).get('a').then(console.log); // shows 'b'
*
* @method get
* @param {String} property The object property name
* @return {Promise} The promise
*/
Promise.prototype.get = function (property) {
return this.then(function (dict) {
return dict[property];
});
};
////////////////////////////////////////////////////////////
// http://wiki.commonjs.org/wiki/Promises/A
// call(functionName, arg1, arg2, ...)
/**
* call(function_name, *args): Promise
*
* Deferred.when({'a': console.log}).call('a', 'b'); // shows 'b'
*
* @method call
* @param {String} function_name The function to call
* @param {Any} *[args] The function arguments
* @return {Promise} A new promise
*/
Promise.prototype.call = function (function_name) {
var args = Array.prototype.slice.call(arguments, 1);
return this.then(function (dict) {
return dict[function_name].apply(dict, args);
});
};
/**
* put(property, value): Promise
*
* Put a property value from a promise response and return the registered
* value as first parameter of the new Promise.
*
* Deferred.when({'a': 'b'}).put('a', 'c').then(console.log); // shows 'c'
*
* @method put
* @param {String} property The object property name
* @param {Any} value The value to put
* @return {Promise} A new promise
*/
Promise.prototype.put = function (property, value) {
return this.then(function (dict) {
dict[property] = value;
return dict[property];
});
};
/**
* del(property): Promise
*
* Delete a property value from a promise response and return the property
* value as first parameter of the new Promise.
*
* Deferred.when({'a': 'b'}).del('a').then(console.log);
* // shows undefined
*
* @method del
* @param {String} property The object property name
* @return {Promise} A new promise
*/
Promise.prototype.del = function (property) {
return this.then(function (dict) {
delete dict[property];
return dict[property];
});
};
/**
* p.done(callback): p
*
* Call the callback on resolve.
*
* Deferred.when(1).
* done(function (one) { return one + 1; }).
* done(console.log); // shows 1
*
* @method done
* @param {Function} callback The callback to call on resolve
* @return {Promise} This promise
*/
Promise.prototype.done = function (callback) {
var that = this;
if (typeof callback !== 'function') {
return this;
}
switch (this._state) {
case RESOLVED:
setTimeout(function () {
try {
callback.apply(that, that._answers);
} catch (ignore) {} // errors will never be retrieved by global
});
break;
case UNRESOLVED:
this._onResolve.push(callback);
break;
default:
break;
}
return this;
};
/**
* p.fail(callback): p
*
* Call the callback on reject.
*
* promisedTypeError().
* fail(function (e) { name_error(); }).
* fail(console.log); // shows TypeError
*
* @method fail
* @param {Function} callback The callback to call on reject
* @return {Promise} This promise
*/
Promise.prototype.fail = function (callback) {
var that = this;
if (typeof callback !== 'function') {
return this;
}
switch (this._state) {
case REJECTED:
setTimeout(function () {
try {
callback.apply(that, that._answers);
} catch (ignore) {} // errors will never be retrieved by global
});
break;
case UNRESOLVED:
this._onReject.push(callback);
break;
default:
break;
}
return this;
};
/**
* p.progress(callback): p
*
* Call the callback on notify.
*
* Promise.delay(100, 10).
* progress(function () { return null; }).
* progress(console.log); // does not show null
*
* @method progress
* @param {Function} callback The callback to call on notify
* @return {Promise} This promise
*/
Promise.prototype.progress = function (callback) {
if (typeof callback !== 'function') {
return this;
}
switch (this._state) {
case UNRESOLVED:
this._onProgress.push(callback);
break;
default:
break;
}
return this;
};
/**
* p.always(callback): p
*
* Call the callback on resolve or on reject.
*
* sayHello().
* done(iAnswer).
* fail(iHeardNothing).
* always(iKeepWalkingAnyway);
*
* @method always
* @param {Function} callback The callback to call on resolve or on reject
* @return {Promise} This promise
*/
Promise.prototype.always = function (callback) {
var that = this;
if (typeof callback !== 'function') {
return this;
}
switch (this._state) {
case RESOLVED:
case REJECTED:
setTimeout(function () {
try {
callback.apply(that, that._answers);
} catch (ignore) {} // errors will never be retrieved by global
});
break;
case UNRESOLVED:
that._onReject.push(callback);
that._onResolve.push(callback);
break;
default:
break;
}
return this;
};
exports.Promise = Promise;
/**
* Deferred(cancel)
*
* @class Deferred
* @constructor
*/
function Deferred(cancel) {
this.promise = new Promise(cancel);
}
/**
* resolve(*args): any
*
* Resolves the promise with the given arguments.
*
* @method resolve
* @param {Any} *[args] The arguments to give
*/
Deferred.prototype.resolve = function () {
return promiseResolve(this.promise, arguments);
};
/**
* reject(*args): any
*
* Rejects the promise with the given arguments.
*
* @method reject
* @param {Any} *[args] The arguments to give
*/
Deferred.prototype.reject = function () {
return promiseReject(this.promise, arguments);
};
/**
* notify(*args): any
*
* Notifies the promise with the given arguments.
*
* @method notify
* @param {Any} *[args] The arguments to give
*/
Deferred.prototype.notify = function () {
return promiseNotify(this.promise, arguments);
};
exports.Deferred = Deferred;
//////////////////////////////////////////////////////////////////////
// Inspired by Task.js
/**
* now(value): Promise
*
* Converts an ordinary value into a fulfilled promise.
*
* @param {Any} value The value to use
* @return {Promise} The resolved promise
*/
exports.now = function (value) {
var deferred = new Deferred();
deferred.resolve(value);
return deferred.promise;
};
/**
* join(*promises): Promise
*
* Produces a promise that is resolved when all the given promises are
* resolved. The resolved value is an array of each of the resolved values of
* the given promises.
*
* If any of the promises is rejected, the joined promise is rejected with the
* same error, and any remaining unfulfilled promises are cancelled.
*
* @param {Promise} *[promises] The promises to join
* @return {Promise} A new promise
*/
exports.join = function () {
var promises, results = [], i, count = 0, deferred;
promises = Array.prototype.slice.call(arguments);
function cancel() {
var j;
for (j = 0; j < promises.length; j += 1) {
if (typeof promises[j].cancel === 'function') {
promises[j].cancel();
}
}
}
deferred = new Deferred(cancel);
function succeed(j) {
return function (answer) {
results[j] = answer;
count += 1;
if (count !== promises.length) {
return;
}
deferred.resolve(results);
};
}
function failed(answer) {
cancel();
deferred.reject(answer);
}
function notify(j) {
return function (answer) {
deferred.notify({
"promise": this,
"index": j,
"answer": answer
});
};
}
for (i = 0; i < promises.length; i += 1) {
promises[i].then(succeed(i), failed, notify(i));
}
return deferred.promise;
};
/**
* choose(*promises): Promise
*
* Produces a promise that is fulfilled when any one of the given promises is
* fulfilled. As soon as one of the promises is fulfilled, whether by being
* resolved or rejected, all the other promises are cancelled.
*
* @param {Promise} *[promises] The promises to use
* @return {Promise} A new promise
*/
exports.choose = function () {
var promises, i, deferred;
promises = Array.prototype.slice.call(arguments);
function cancel() {
var j;
for (j = 0; j < promises.length; j += 1) {
if (typeof promises[j].cancel === 'function') {
promises[j].cancel();
}
}
}
deferred = new Deferred(cancel);
function succeed(answer) {
cancel();
deferred.resolve(answer);
}
function failed(answer) {
cancel();
deferred.reject(answer);
}
function notify(j) {
return function (answer) {
deferred.notify({
"promise": this,
"index": j,
"answer": answer
});
};
}
for (i = 0; i < promises.length; i += 1) {
promises[i].then(succeed, failed, notify(i));
}
return deferred.promise;
};
/**
* never(): Promise
*
* Produces a promise that is never fulfilled.
*
* @return {Promise} A promise
*/
exports.never = function () {
return new Promise();
};
/**
* sleep(delay[, every]): Promise
*
* Resolve the promise after `timeout` milliseconds and notfies us every
* `every` milliseconds.
*
* Deferred.delay(50, 10).then(console.log, console.error, console.log);
* // // shows
* // 10 // from progress
* // 20 // from progress
* // 30 // from progress
* // 40 // from progress
* // 50 // from progress
* // 50 // from success
*
* @param {Number} delay In milliseconds
* @param {Number} [every] In milliseconds
* @return {Promise} A new promise
*/
exports.sleep = function (delay, every) {
var deferred, timeout, interval, now = 0;
function cancel() {
clearTimeout(timeout);
clearInterval(interval);
}
deferred = new Deferred(cancel);
if (typeof every === 'number' && isFinite(every)) {
interval = setInterval(function () {
now += every;
deferred.notify(now);
}, every);
}
timeout = setTimeout(function () {
clearInterval(interval);
deferred.notify(delay);
deferred.resolve(delay);
}, delay);
return deferred.promise;
};
////////////////////////////////////////////////////////////
// http://wiki.commonjs.org/wiki/Promises/B
// when(value, callback, errback_opt)
/**
* when(item, [onSuccess], [onError], [onProgress]): Promise
*
* Return an item as first parameter of the promise answer. If item is of
* type Promise, the method will just return the promise. If item is of type
* Deferred, the method will return the deferred promise.
*
* Deferred.when('a').then(console.log); // shows 'a'
*
* @param {Any} item The item to use
* @param {Function} [onSuccess] The callback called on success
* @param {Function} [onError] the callback called on error
* @param {Function} [onProgress] the callback called on progress
* @return {Promise} The promise
*/
exports.when = function (item, onSuccess, onError, onProgress) {
if (typeof item === 'object' && item !== null) {
if (typeof item.promise === 'object' && item.promise !== null &&
typeof item.promise.then === 'function') {
// item seams to be a Deferred
return item.promise.then(onSuccess, onError, onProgress);
}
if (typeof item.then === 'function') {
// item seams to be a Promise
return item.then(onSuccess, onError, onProgress);
}
}
// item is just a value, convert into fulfilled promise
var deferred = new Deferred(), promise;
if (typeof onSuccess === 'function') {
promise = deferred.promise.then(onSuccess);
} else {
promise = deferred.promise;
}
deferred.resolve(item);
return promise;
};
////////////////////////////////////////////////////////////
// http://wiki.commonjs.org/wiki/Promises/B
// get(object, name)
/**
* get(dict, property): Promise
*
* Return the dict property as first parameter of the promise answer.
*
* Deferred.get({'a': 'b'}, 'a').then(console.log); // shows 'b'
*
* @param {Object} dict The object to use
* @param {String} property The object property name
* @return {Promise} The promise
*/
exports.get = function (dict, property) {
var p = new Deferred();
try {
p.resolve(dict[property]);
} catch (e) {
p.reject(e);
}
return p;
};
////////////////////////////////////////////////////////////
// http://wiki.commonjs.org/wiki/Promises/B
// put(object, name, value)
/**
* put(dict, property, value): Promise
*
* Set and return the dict property as first parameter of the promise answer.
*
* Deferred.put({'a': 'b'}, 'a', 'c').then(console.log); // shows 'c'
*
* @param {Object} dict The object to use
* @param {String} property The object property name
* @param {Any} value The value
* @return {Promise} The promise
*/
exports.put = function (dict, property, value) {
var p = new Deferred();
try {
dict[property] = value;
p.resolve(dict[property]);
} catch (e) {
p.reject(e);
}
return p;
};
////////////////////////////////////////////////////////////
// http://wiki.commonjs.org/wiki/Promises/B
// del(object, name)
/**
* del(dict, property): Promise
*
* Delete and return the dict property as first parameter of the promise
* answer.
*
* Deferred.del({'a': 'b'}, 'a').then(console.log); // shows undefined
*
* @param {Object} dict The object to use
* @param {String} property The object property name
* @return {Promise} The promise
*/
exports.del = function (dict, property) {
var p = new Deferred();
try {
delete dict[property];
p.resolve(dict[property]);
} catch (e) {
p.reject(e);
}
return p;
};
}));
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment