couchdb-emily-tools
Advanced tools
Comparing version 0.0.333dev to 1.0.0
@@ -6,5 +6,20 @@ /* | ||
*/ | ||
define("CouchDBSecurity",["CouchDBStore"],function(e){function d(){var a="_security";this.setName=function(c){return c?(a=c,true):false};this.getName=function(){return a};this.load=function(c){return this.sync(c,a)}}return function(){d.prototype=new e;return new d}}); | ||
define("CouchDBUser",["CouchDBStore","Promise"],function(e,d){function a(){var c="_users",a="org.couchdb.user:";this.getUserDB=function(){return c};this.setUserDB=function(b){return b?(c=b,true):false};this.getIdPrefix=function(){return a};this.setIdPrefix=function(b){return b?(a=b,true):false};this.setId=function(b){return b?(this.set("_id",a+b),true):false};this.getId=function(){return this.get("_id")};this.load=function(b){return this.sync(c,a+b)};this.login=function(){var b=new d,a=this.get("name"), | ||
c=this.get("password");a&&typeof a=="string"&&typeof c=="string"?this.getTransport().request("CouchDB",{method:"GET",path:"/_users/org.couchdb.user:"+a,auth:a+":"+c},b.resolve,b):b.reject({error:"name & password must be strings"});return b};this.create=function(){var b=new d;this.get("type")||this.set("type","user");this.get("roles")||this.set("roles",[]);this.load(this.get("name")).then(function(){b.reject({error:"Failed to create user. The user already exists"})},function(){this.upload().then(function(a){b.resolve(a)}, | ||
function(a){b.reject(a)})},this);return b}}return function(){a.prototype=new e;return new a}}); | ||
define("CouchDBSecurity",["CouchDBStore"],function(k){function j(){var f="_security";this.setName=function(h){return h?(f=h,true):false};this.getName=function(){return f};this.load=function(h){return this.sync(h,f)}}return function(){j.prototype=new k;return new j}}); | ||
define("CouchDBStore",["Store","StateMachine","Tools","Promise"],function(k,j,f,h){function l(){var b=null,a={},i=new h,d={getView:function(){a.query=a.query||{};b.request("CouchDB",{method:"GET",path:"/"+a.database+"/_design/"+a.design+"/"+a.view,query:a.query},function(c){var e=JSON.parse(c);if(e.rows)this.reset(e.rows),i.resolve(this),typeof e.total_rows=="undefined"&&this.setReducedViewInfo(true),g.event("subscribeToViewChanges");else throw Error("CouchDBStore ["+a.database+", "+a.design+", "+ | ||
a.view+"].sync() failed: "+c);},this)},getDocument:function(){b.request("CouchDB",{method:"GET",path:"/"+a.database+"/"+a.document,query:a.query},function(c){var a=JSON.parse(c);a._id?(this.reset(a),i.resolve(this),g.event("subscribeToDocumentChanges")):i.reject(c)},this)},getBulkDocuments:function(){var c={path:"/"+a.database+"/_all_docs",query:a.query},e;a.keys instanceof Array?(c.method="POST",c.data=JSON.stringify({keys:a.keys}),c.headers={"Content-Type":"application/json"},e=c.data):(c.method= | ||
"GET",e=JSON.stringify(a.query));a.query.include_docs=true;b.request("CouchDB",c,function(c){var b=JSON.parse(c);if(b.rows)this.reset(b.rows),i.resolve(this),g.event("subscribeToBulkChanges");else throw Error('CouchDBStore.sync("'+a.database+'", '+e+") failed: "+c);},this)},createDocument:function(c){b.request("CouchDB",{method:"PUT",path:"/"+a.database+"/"+a.document,headers:{"Content-Type":"application/json"},data:this.toJSON()},function(a){a=JSON.parse(a);a.ok?(c.resolve(a),g.event("subscribeToDocumentChanges")): | ||
c.reject(a)})},subscribeToViewChanges:function(){f.mixin({feed:"continuous",heartbeat:2E4,limit:0,descending:true},a.query);this.stopListening=b.listen("CouchDB",{path:"/"+a.database+"/_changes",query:a.query},function(c){if(c=="\n")return false;var c=JSON.parse(c),e;e=a.reducedView?"updateReduced":c.deleted?"delete":c.changes[0].rev.search("1-")==0?"add":"change";g.event(e,c.id)},this)},subscribeToDocumentChanges:function(){this.stopListening=b.listen("CouchDB",{path:"/"+a.database+"/_changes",query:{feed:"continuous", | ||
heartbeat:2E4,limit:0,descending:true}},function(c){if(c=="\n")return false;c=JSON.parse(c);c.id==a.document&&c.changes.pop().rev!=this.get("_rev")&&(c.deleted?g.event("deleteDoc"):g.event("updateDoc"))},this)},subscribeToBulkChanges:function(){f.mixin({feed:"continuous",heartbeat:2E4,limit:0,descending:true,include_docs:true},a.query);this.stopListening=b.listen("CouchDB",{path:"/"+a.database+"/_changes",query:a.query},function(a){if(a=="\n")return false;var a=JSON.parse(a),e;e=a.changes[0].rev.search("1-")== | ||
0?"bulkAdd":a.deleted?"delete":"bulkChange";g.event(e,a.id,a.doc)},this)},updateDocInStore:function(c){b.request("CouchDB",{method:"GET",path:"/"+a.database+"/_design/"+a.design+"/"+a.view,query:a.query},function(a){a=JSON.parse(a);a.rows.length==this.getNbItems()?a.rows.some(function(a,e){a.id==c&&this.set(e,a)},this):this.actions.evenDocsInStore.call(this,a.rows,c)},this)},evenDocsInStore:function(a,e){var b=this.getNbItems();a.length<b?this.loop(function(a,c){a.id==e&&this.del(c)},this):a.length> | ||
b&&a.some(function(a,c){if(a.id==e)return this.alter("splice",c,0,a)},this)},addBulkDocInStore:function(c){if(a.query.startkey||a.query.endkey)a.query.include_docs=true,a.query.update_seq=true,b.request("CouchDB",{method:"GET",path:"/"+a.database+"/_all_docs",query:a.query},function(a){JSON.parse(a).rows.forEach(function(a,e){a.id==c&&this.alter("splice",e,0,a.doc)},this)},this);else return false},updateBulkDocInStore:function(a,e){this.loop(function(b,d){b.id==a&&this.set(d,e)},this)},removeDocInStore:function(a){this.loop(function(e, | ||
b){e.id==a&&this.del(b)},this)},addDocInStore:function(c){b.request("CouchDB",{method:"GET",path:"/"+a.database+"/_design/"+a.design+"/"+a.view,query:a.query},function(a){JSON.parse(a).rows.some(function(a,b){a.id==c&&this.alter("splice",b,0,a)},this)},this)},updateReduced:function(){b.request("CouchDB",{method:"GET",path:"/"+a.database+"/_design/"+a.design+"/"+a.view,query:a.query},function(a){this.set(0,JSON.parse(a).rows[0])},this)},updateDoc:function(){b.request("CouchDB",{method:"GET",path:"/"+ | ||
a.database+"/"+a.document},function(a){this.reset(JSON.parse(a))},this)},deleteDoc:function(){this.reset({})},updateDatabase:function(c){b.request("CouchDB",{method:"PUT",path:"/"+a.database+"/"+a.document,headers:{"Content-Type":"application/json"},data:this.toJSON()},function(a){a=JSON.parse(a);a.ok?(this.set("_rev",a.rev),c.resolve(a)):c.reject(a)},this)},updateDatabaseWithBulkDoc:function(c){var e=[];this.loop(function(a){e.push(a.doc)});b.request("CouchDB",{method:"POST",path:"/"+a.database+ | ||
"/_bulk_docs",headers:{"Content-Type":"application/json"},data:JSON.stringify({docs:e})},function(a){c.resolve(JSON.parse(a))})},removeFromDatabase:function(){b.request("CouchDB",{method:"DELETE",path:"/"+a.database+"/"+a.document,query:{rev:this.get("_rev")}})},unsync:function(){this.stopListening();delete this.stopListening}},g=new j("Unsynched",{Unsynched:[["getView",d.getView,this,"Synched"],["getDocument",d.getDocument,this,"Synched"],["getBulkDocuments",d.getBulkDocuments,this,"Synched"]],Synched:[["updateDatabase", | ||
d.createDocument,this],["subscribeToViewChanges",d.subscribeToViewChanges,this,"Listening"],["subscribeToDocumentChanges",d.subscribeToDocumentChanges,this,"Listening"],["subscribeToBulkChanges",d.subscribeToBulkChanges,this,"Listening"],["unsync",function(){},"Unsynched"]],Listening:[["change",d.updateDocInStore,this],["bulkAdd",d.addBulkDocInStore,this],["bulkChange",d.updateBulkDocInStore,this],["delete",d.removeDocInStore,this],["add",d.addDocInStore,this],["updateReduced",d.updateReduced,this], | ||
["updateDoc",d.updateDoc,this],["deleteDoc",d.deleteDoc,this],["updateDatabase",d.updateDatabase,this],["updateDatabaseWithBulkDoc",d.updateDatabaseWithBulkDoc,this],["removeFromDatabase",d.removeFromDatabase,this],["unsync",d.unsync,this,"Unsynched"]]});this.sync=function(a,b,d,f){i=new h;if(typeof a=="string"&&typeof b=="string"&&typeof d=="string")return this.setSyncInfo(a,b,d,f),g.event("getView"),i;else if(typeof a=="string"&&typeof b=="string"&&typeof d!="string")return this.setSyncInfo(a,b, | ||
d),g.event("getDocument"),i;else if(typeof a=="string"&&b instanceof Object)return this.setSyncInfo(a,b),g.event("getBulkDocuments"),i;return false};this.setSyncInfo=function(c,b,d,g){this.clearSyncInfo();if(typeof c=="string"&&typeof b=="string"&&typeof d=="string")return a.database=c,a.design=b,a.view=d,a.query=g,true;else if(typeof c=="string"&&typeof b=="string"&&typeof d!="string")return a.database=c,a.document=b,a.query=d,true;else if(typeof c=="string"&&b instanceof Object){a.database=c;a.query= | ||
b;if(a.query.keys instanceof Array)a.keys=a.query.keys,delete a.query.keys;return true}return false};this.clearSyncInfo=function(){a={};return true};this.setReducedViewInfo=function(c){return typeof c=="boolean"?(a.reducedView=c,true):false};this.getSyncInfo=function(){return a};this.unsync=function(){return g.event("unsync")};this.upload=function(){var c=new h;if(a.document)return g.event("updateDatabase",c),c;else if(!a.view)return g.event("updateDatabaseWithBulkDoc",c),c;return false};this.remove= | ||
function(){return a.document?g.event("removeFromDatabase"):false};this.setTransport=function(a){return a&&typeof a.listen=="function"&&typeof a.request=="function"?(b=a,true):false};this.getStateMachine=function(){return g};this.getTransport=function(){return b};this.actions=d}return function(b){l.prototype=new k(b);return new l}}); | ||
define("CouchDBUser",["CouchDBStore","Promise"],function(k,j){function f(){var h="_users",f="org.couchdb.user:";this.getUserDB=function(){return h};this.setUserDB=function(b){return b?(h=b,true):false};this.getIdPrefix=function(){return f};this.setIdPrefix=function(b){return b?(f=b,true):false};this.setId=function(b){return b?(this.set("_id",f+b),true):false};this.getId=function(){return this.get("_id")};this.load=function(b){return this.sync(h,f+b)};this.login=function(){var b=new j,a=this.get("name"), | ||
f=this.get("password");a&&typeof a=="string"&&typeof f=="string"?this.getTransport().request("CouchDB",{method:"GET",path:"/_users/org.couchdb.user:"+a,auth:a+":"+f},b.resolve,b):b.reject({error:"name & password must be strings"});return b};this.create=function(){var b=new j;this.get("type")||this.set("type","user");this.get("roles")||this.set("roles",[]);this.load(this.get("name")).then(function(){b.reject({error:"Failed to create user. The user already exists"})},function(){this.upload().then(function(a){b.resolve(a)}, | ||
function(a){b.reject(a)})},this);return b}}return function(){f.prototype=new k;return new f}}); |
@@ -78,2 +78,765 @@ /** | ||
*/ | ||
define("CouchDBStore", | ||
["Store", "StateMachine", "Tools", "Promise"], | ||
/** | ||
* @class | ||
* CouchDBStore synchronises a Store with a CouchDB view or document | ||
* It subscribes to _changes to keep its data up to date. | ||
*/ | ||
function CouchDBStore(Store, StateMachine, Tools, Promise) { | ||
/** | ||
* Defines the CouchDBStore | ||
* @returns {CouchDBStoreConstructor} | ||
*/ | ||
function CouchDBStoreConstructor() { | ||
/** | ||
* The name of the channel on which to run the requests | ||
* @private | ||
*/ | ||
var _channel = "CouchDB", | ||
/** | ||
* The transport used to run the requests | ||
* @private | ||
*/ | ||
_transport = null, | ||
/** | ||
* That will store the synchronization info | ||
* @private | ||
*/ | ||
_syncInfo = {}, | ||
/** | ||
* The promise that is returned by sync | ||
* It's resolved when entering listening state | ||
* It's rejected when no such document to sync to | ||
* The promise is initialized here for testing purpose | ||
* but it's initialized again in sync | ||
* @private | ||
*/ | ||
_syncPromise = new Promise, | ||
/** | ||
* All the actions performed by the couchDBStore | ||
* They'll feed the stateMachine | ||
* @private | ||
*/ | ||
actions = { | ||
/** | ||
* Get a CouchDB view | ||
* @private | ||
*/ | ||
getView: function () { | ||
_syncInfo.query = _syncInfo.query || {}; | ||
_transport.request(_channel, { | ||
method: "GET", | ||
path: "/" + _syncInfo.database + "/_design/" + _syncInfo.design + "/" + _syncInfo.view, | ||
query: _syncInfo.query | ||
}, function (results) { | ||
var json = JSON.parse(results); | ||
if (!json.rows) { | ||
throw new Error("CouchDBStore [" + _syncInfo.database + ", " + _syncInfo.design + ", " + _syncInfo.view + "].sync() failed: " + results); | ||
} else { | ||
this.reset(json.rows); | ||
_syncPromise.resolve(this); | ||
if (typeof json.total_rows == "undefined") { | ||
this.setReducedViewInfo(true); | ||
} | ||
_stateMachine.event("subscribeToViewChanges"); | ||
} | ||
}, this); | ||
}, | ||
/** | ||
* Get a CouchDB document | ||
* @private | ||
*/ | ||
getDocument: function () { | ||
_transport.request(_channel, { | ||
method: "GET", | ||
path: "/" + _syncInfo.database + "/" + _syncInfo.document, | ||
query: _syncInfo.query | ||
}, function (results) { | ||
var json = JSON.parse(results); | ||
if (json._id) { | ||
this.reset(json); | ||
_syncPromise.resolve(this); | ||
_stateMachine.event("subscribeToDocumentChanges"); | ||
} else { | ||
_syncPromise.reject(results); | ||
} | ||
}, this); | ||
}, | ||
/** | ||
* Get a bulk of documents | ||
* @private | ||
*/ | ||
getBulkDocuments: function () { | ||
var reqData = { | ||
path: "/" + _syncInfo.database + "/_all_docs", | ||
query: _syncInfo.query | ||
}, | ||
errorString; | ||
// If an array of keys is defined, we POST it to _all_docs to get arbitrary docs. | ||
if (_syncInfo["keys"] instanceof Array) { | ||
reqData.method = "POST"; | ||
reqData.data = JSON.stringify({keys:_syncInfo.keys}); | ||
reqData.headers = { | ||
"Content-Type": "application/json" | ||
}; | ||
errorString = reqData.data; | ||
// Else, we just GET the documents using startkey/endkey | ||
} else { | ||
reqData.method = "GET"; | ||
errorString = JSON.stringify(_syncInfo.query); | ||
} | ||
_syncInfo.query.include_docs = true; | ||
_transport.request(_channel, | ||
reqData, | ||
function (results) { | ||
var json = JSON.parse(results); | ||
if (!json.rows) { | ||
throw new Error("CouchDBStore.sync(\"" + _syncInfo.database + "\", " + errorString + ") failed: " + results); | ||
} else { | ||
this.reset(json.rows); | ||
_syncPromise.resolve(this); | ||
_stateMachine.event("subscribeToBulkChanges"); | ||
} | ||
}, this); | ||
}, | ||
/** | ||
* Put a new document in CouchDB | ||
* @private | ||
*/ | ||
createDocument: function (promise) { | ||
_transport.request(_channel, { | ||
method: "PUT", | ||
path: "/" + _syncInfo.database + "/" + _syncInfo.document, | ||
headers: { | ||
"Content-Type": "application/json" | ||
}, | ||
data: this.toJSON() | ||
}, function (result) { | ||
var json = JSON.parse(result); | ||
if (json.ok) { | ||
promise.resolve(json); | ||
_stateMachine.event("subscribeToDocumentChanges"); | ||
} else { | ||
promise.reject(json); | ||
} | ||
}); | ||
}, | ||
/** | ||
* Subscribe to changes when synchronized with a view | ||
* @private | ||
*/ | ||
subscribeToViewChanges: function () { | ||
Tools.mixin({ | ||
feed: "continuous", | ||
heartbeat: 20000, | ||
limit: 0, | ||
descending: true | ||
}, _syncInfo.query); | ||
this.stopListening = _transport.listen(_channel, { | ||
path: "/" + _syncInfo.database + "/_changes", | ||
query: _syncInfo.query | ||
}, | ||
function (changes) { | ||
// Should I test for this very special case (heartbeat?) | ||
// Or do I have to try catch for any invalid json? | ||
if (changes == "\n") { | ||
return false; | ||
} | ||
var json = JSON.parse(changes), | ||
action; | ||
// reducedView is known on the first get view | ||
if (_syncInfo.reducedView) { | ||
action = "updateReduced"; | ||
} else { | ||
if (json.deleted) { | ||
action = "delete"; | ||
} else if (json.changes[0].rev.search("1-") == 0) { | ||
action = "add"; | ||
} else { | ||
action = "change"; | ||
} | ||
} | ||
_stateMachine.event(action, json.id); | ||
}, this); | ||
}, | ||
/** | ||
* Subscribe to changes when synchronized with a document | ||
* @private | ||
*/ | ||
subscribeToDocumentChanges: function () { | ||
this.stopListening = _transport.listen(_channel, { | ||
path: "/" + _syncInfo.database + "/_changes", | ||
query: { | ||
feed: "continuous", | ||
heartbeat: 20000, | ||
limit: 0, | ||
descending: true | ||
} | ||
}, | ||
function (changes) { | ||
var json; | ||
// Should I test for this very special case (heartbeat?) | ||
// Or do I have to try catch for any invalid json? | ||
if (changes == "\n") { | ||
return false; | ||
} | ||
json = JSON.parse(changes); | ||
// The document is the modified document is the current one | ||
if (json.id == _syncInfo.document && | ||
// And if it has a new revision | ||
json.changes.pop().rev != this.get("_rev")) { | ||
if (json.deleted) { | ||
_stateMachine.event("deleteDoc"); | ||
} else { | ||
_stateMachine.event("updateDoc"); | ||
} | ||
} | ||
}, this); | ||
}, | ||
/** | ||
* Subscribe to changes when synchronized with a bulk of documents | ||
* @private | ||
*/ | ||
subscribeToBulkChanges: function () { | ||
Tools.mixin({ | ||
feed: "continuous", | ||
heartbeat: 20000, | ||
limit: 0, | ||
descending: true, | ||
include_docs: true | ||
}, _syncInfo.query); | ||
this.stopListening = _transport.listen(_channel, { | ||
path: "/" + _syncInfo.database + "/_changes", | ||
query: _syncInfo.query | ||
}, | ||
function (changes) { | ||
var json; | ||
// Should I test for this very special case (heartbeat?) | ||
// Or do I have to try catch for any invalid json? | ||
if (changes == "\n") { | ||
return false; | ||
} | ||
var json = JSON.parse(changes), | ||
action; | ||
if (json.changes[0].rev.search("1-") == 0) { | ||
action = "bulkAdd"; | ||
} else if (json.deleted) { | ||
action = "delete"; | ||
} else { | ||
action = "bulkChange"; | ||
} | ||
_stateMachine.event(action, json.id, json.doc); | ||
}, this); | ||
}, | ||
/** | ||
* Update in the Store a document that was updated in CouchDB | ||
* Get the whole view :(, then get the modified document and update it. | ||
* I have no choice but to request the whole view and look for the document | ||
* so I can also retrieve its position in the store (idx) and update the item. | ||
* Maybe I've missed something | ||
* @private | ||
*/ | ||
updateDocInStore: function (id) { | ||
_transport.request(_channel,{ | ||
method: "GET", | ||
path: "/" + _syncInfo.database + "/_design/" + _syncInfo.design + "/" + _syncInfo.view, | ||
query: _syncInfo.query | ||
}, function (view) { | ||
var json = JSON.parse(view); | ||
if (json.rows.length == this.getNbItems()) { | ||
json.rows.some(function (value, idx) { | ||
if (value.id == id) { | ||
this.set(idx, value); | ||
} | ||
}, this); | ||
} else { | ||
this.actions.evenDocsInStore.call(this, json.rows, id); | ||
} | ||
}, this); | ||
}, | ||
/** | ||
* When a doc is removed from the view even though it still exists | ||
* or when it's added to a view, though it wasn't just created | ||
* This function must be called to even the store | ||
* @private | ||
*/ | ||
evenDocsInStore: function (view, id) { | ||
var nbItems = this.getNbItems(); | ||
// If a document was removed from the view | ||
if (view.length < nbItems) { | ||
// Look for it in the store to remove it | ||
this.loop(function (value, idx) { | ||
if (value.id == id) { | ||
this.del(idx); | ||
} | ||
}, this); | ||
// If a document was added to the view | ||
} else if (view.length > nbItems) { | ||
// Look for it in the view and add it to the store at the same place | ||
view.some(function (value, idx) { | ||
if (value.id == id) { | ||
return this.alter("splice", idx, 0, value); | ||
} | ||
}, this); | ||
} | ||
}, | ||
/** | ||
* Add in the Store a document that was added in CouchDB | ||
* @private | ||
*/ | ||
addBulkDocInStore: function (id) { | ||
if (_syncInfo["query"].startkey || _syncInfo["query"].endkey) { | ||
_syncInfo.query.include_docs = true; | ||
_syncInfo.query.update_seq = true; | ||
_transport.request(_channel, { | ||
method: "GET", | ||
path: "/" + _syncInfo.database + "/_all_docs", | ||
query: _syncInfo.query | ||
}, | ||
function (results) { | ||
var json = JSON.parse(results); | ||
json.rows.forEach(function (value, idx) { | ||
if (value.id == id) { | ||
this.alter("splice", idx, 0, value.doc); | ||
} | ||
}, this); | ||
}, this); | ||
} else { | ||
return false; | ||
} | ||
}, | ||
/** | ||
* Update in the Store a document that was updated in CouchDB | ||
* @private | ||
*/ | ||
updateBulkDocInStore: function (id, doc) { | ||
this.loop(function (value, idx) { | ||
if (value.id == id) { | ||
this.set(idx, doc); | ||
} | ||
}, this); | ||
}, | ||
/** | ||
* Remove from the Store a document that was removed in CouchDB | ||
* @private | ||
*/ | ||
removeDocInStore: function (id) { | ||
this.loop(function (value, idx) { | ||
if (value.id == id) { | ||
this.del(idx); | ||
} | ||
}, this); | ||
}, | ||
/** | ||
* Add in the Store a document that was added in CouchDB | ||
* @private | ||
*/ | ||
addDocInStore: function (id) { | ||
_transport.request(_channel,{ | ||
method: "GET", | ||
path: "/" + _syncInfo.database + "/_design/" + _syncInfo.design + "/" + _syncInfo.view, | ||
query: _syncInfo.query | ||
}, function (view) { | ||
var json = JSON.parse(view); | ||
json.rows.some(function (value, idx) { | ||
if (value.id == id) { | ||
this.alter("splice", idx, 0, value); | ||
} | ||
}, this); | ||
}, this); | ||
}, | ||
/** | ||
* Update a reduced view (it has one row with no id) | ||
* @private | ||
*/ | ||
updateReduced: function () { | ||
_transport.request(_channel,{ | ||
method: "GET", | ||
path: "/" + _syncInfo.database + "/_design/" + _syncInfo.design + "/" + _syncInfo.view, | ||
query: _syncInfo.query | ||
}, function (view) { | ||
var json = JSON.parse(view); | ||
this.set(0, json.rows[0]); | ||
}, this); | ||
}, | ||
/** | ||
* Update the document when synchronized with a document. | ||
* This differs than updating a document in a View | ||
* @private | ||
*/ | ||
updateDoc: function () { | ||
_transport.request(_channel, { | ||
method: "GET", | ||
path: "/"+_syncInfo.database+"/" + _syncInfo.document | ||
}, function (doc) { | ||
this.reset(JSON.parse(doc)); | ||
}, this); | ||
}, | ||
/** | ||
* Delete all document's properties | ||
* @private | ||
*/ | ||
deleteDoc: function () { | ||
this.reset({}); | ||
}, | ||
/** | ||
* Update a document in CouchDB through a PUT request | ||
* @private | ||
*/ | ||
updateDatabase: function (promise) { | ||
_transport.request(_channel, { | ||
method: "PUT", | ||
path: "/" + _syncInfo.database + "/" + _syncInfo.document, | ||
headers: { | ||
"Content-Type": "application/json" | ||
}, | ||
data: this.toJSON() | ||
}, function (response) { | ||
var json = JSON.parse(response); | ||
if (json.ok) { | ||
this.set("_rev", json.rev); | ||
promise.resolve(json); | ||
} else { | ||
promise.reject(json); | ||
} | ||
}, this); | ||
}, | ||
/** | ||
* Update the database with bulk documents | ||
* @private | ||
*/ | ||
updateDatabaseWithBulkDoc: function (promise) { | ||
var docs = []; | ||
this.loop(function (value) { | ||
docs.push(value.doc); | ||
}); | ||
_transport.request(_channel, { | ||
method: "POST", | ||
path: "/" + _syncInfo.database + "/_bulk_docs", | ||
headers: { | ||
"Content-Type": "application/json" | ||
}, | ||
data: JSON.stringify({"docs": docs}) | ||
}, function (response) { | ||
promise.resolve(JSON.parse(response)); | ||
}); | ||
}, | ||
/** | ||
* Remove a document from CouchDB through a DELETE request | ||
* @private | ||
*/ | ||
removeFromDatabase: function () { | ||
_transport.request(_channel, { | ||
method: "DELETE", | ||
path: "/" + _syncInfo.database + "/" + _syncInfo.document, | ||
query: { | ||
rev: this.get("_rev") | ||
} | ||
}); | ||
}, | ||
/** | ||
* The function call to unsync the store | ||
* @private | ||
*/ | ||
unsync: function () { | ||
this.stopListening(); | ||
delete this.stopListening; | ||
} | ||
}, | ||
/** | ||
* The state machine | ||
* @private | ||
* it concentrates almost the whole logic. | ||
*/ | ||
_stateMachine = new StateMachine("Unsynched", { | ||
"Unsynched": [ | ||
["getView", actions.getView, this, "Synched"], | ||
["getDocument", actions.getDocument, this, "Synched"], | ||
["getBulkDocuments", actions.getBulkDocuments, this, "Synched"] | ||
], | ||
"Synched": [ | ||
["updateDatabase", actions.createDocument, this], | ||
["subscribeToViewChanges", actions.subscribeToViewChanges, this, "Listening"], | ||
["subscribeToDocumentChanges", actions.subscribeToDocumentChanges, this, "Listening"], | ||
["subscribeToBulkChanges", actions.subscribeToBulkChanges, this, "Listening"], | ||
["unsync", function noop(){}, "Unsynched"] | ||
], | ||
"Listening": [ | ||
["change", actions.updateDocInStore, this], | ||
["bulkAdd", actions.addBulkDocInStore, this], | ||
["bulkChange", actions.updateBulkDocInStore, this], | ||
["delete", actions.removeDocInStore, this], | ||
["add", actions.addDocInStore, this], | ||
["updateReduced", actions.updateReduced, this], | ||
["updateDoc", actions.updateDoc, this], | ||
["deleteDoc", actions.deleteDoc, this], | ||
["updateDatabase", actions.updateDatabase, this], | ||
["updateDatabaseWithBulkDoc", actions.updateDatabaseWithBulkDoc, this], | ||
["removeFromDatabase", actions.removeFromDatabase, this], | ||
["unsync", actions.unsync, this, "Unsynched"] | ||
] | ||
}); | ||
/** | ||
* Synchronize the store with a view | ||
* @param {String} database the name of the database where to get... | ||
* @param {String} ...design the design document, in which... | ||
* @param {String} view ...the view is. | ||
* @returns {Boolean} | ||
*/ | ||
this.sync = function sync() { | ||
_syncPromise = new Promise; | ||
if (typeof arguments[0] == "string" && typeof arguments[1] == "string" && typeof arguments[2] == "string") { | ||
this.setSyncInfo(arguments[0], arguments[1], arguments[2], arguments[3]); | ||
_stateMachine.event("getView"); | ||
return _syncPromise; | ||
} else if (typeof arguments[0] == "string" && typeof arguments[1] == "string" && typeof arguments[2] != "string") { | ||
this.setSyncInfo(arguments[0], arguments[1], arguments[2]); | ||
_stateMachine.event("getDocument"); | ||
return _syncPromise; | ||
} else if (typeof arguments[0] == "string" && arguments[1] instanceof Object) { | ||
this.setSyncInfo(arguments[0], arguments[1]); | ||
_stateMachine.event("getBulkDocuments"); | ||
return _syncPromise; | ||
} | ||
return false; | ||
}; | ||
/** | ||
* Set the synchronization information | ||
* @private | ||
* @returns {Boolean} | ||
*/ | ||
this.setSyncInfo = function setSyncInfo() { | ||
this.clearSyncInfo(); | ||
if (typeof arguments[0] == "string" && typeof arguments[1] == "string" && typeof arguments[2] == "string") { | ||
_syncInfo["database"] = arguments[0]; | ||
_syncInfo["design"] = arguments[1]; | ||
_syncInfo["view"] = arguments[2]; | ||
_syncInfo["query"] = arguments[3]; | ||
return true; | ||
} else if (typeof arguments[0] == "string" && typeof arguments[1] == "string" && typeof arguments[2] != "string") { | ||
_syncInfo["database"] = arguments[0]; | ||
_syncInfo["document"] = arguments[1]; | ||
_syncInfo["query"] = arguments[2]; | ||
return true; | ||
} else if (typeof arguments[0] == "string" && arguments[1] instanceof Object) { | ||
_syncInfo["database"] = arguments[0]; | ||
_syncInfo["query"] = arguments[1]; | ||
if (_syncInfo["query"].keys instanceof Array) { | ||
_syncInfo["keys"] = _syncInfo["query"].keys; | ||
delete _syncInfo["query"].keys; | ||
} | ||
return true; | ||
} | ||
return false; | ||
}; | ||
/** | ||
* Between two synchs, the previous info must be cleared up | ||
* @private | ||
*/ | ||
this.clearSyncInfo = function clearSyncInfo() { | ||
_syncInfo = {}; | ||
return true; | ||
}; | ||
/** | ||
* Set a flag to tell that a synchronized view is reduced | ||
* @private | ||
*/ | ||
this.setReducedViewInfo = function setReducedViewInfo(reduced) { | ||
if (typeof reduced == "boolean") { | ||
_syncInfo.reducedView = reduced; | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
}; | ||
/** | ||
* Get the synchronization information | ||
* @private | ||
* @returns | ||
*/ | ||
this.getSyncInfo = function getSyncInfo() { | ||
return _syncInfo; | ||
}; | ||
/** | ||
* Unsync a store. Unsync must be called prior to resynchronization. | ||
* That will prevent any unwanted resynchronization. | ||
* Notice that previous data will still be available. | ||
* @returns | ||
*/ | ||
this.unsync = function unsync() { | ||
return _stateMachine.event("unsync"); | ||
}; | ||
/** | ||
* Upload the document to the database | ||
* Works for CouchDBStore that are synchronized with documents or bulk of documents. | ||
* If synchronized with a bulk of documents, you can set the documents to delete _deleted property to true. | ||
* No modification can be done on views. | ||
* @returns true if upload called | ||
*/ | ||
this.upload = function upload() { | ||
var promise = new Promise; | ||
if (_syncInfo.document) { | ||
_stateMachine.event("updateDatabase", promise); | ||
return promise; | ||
} else if (!_syncInfo.view){ | ||
_stateMachine.event("updateDatabaseWithBulkDoc", promise); | ||
return promise; | ||
} | ||
return false; | ||
}; | ||
/** | ||
* Remove the document from the database | ||
* @returns true if remove called | ||
*/ | ||
this.remove = function remove() { | ||
if (_syncInfo.document) { | ||
return _stateMachine.event("removeFromDatabase"); | ||
} | ||
return false; | ||
}; | ||
/** | ||
* The transport object to use | ||
* @param {Object} transport the transport object | ||
* @returns {Boolean} true if | ||
*/ | ||
this.setTransport = function setTransport(transport) { | ||
if (transport && typeof transport.listen == "function" && typeof transport.request == "function") { | ||
_transport = transport; | ||
return true; | ||
} else { | ||
return false; | ||
} | ||
}; | ||
/** | ||
* Get the state machine | ||
* Also only useful for debugging | ||
* @private | ||
* @returns {StateMachine} the state machine | ||
*/ | ||
this.getStateMachine = function getStateMachine() { | ||
return _stateMachine; | ||
}; | ||
/** | ||
* Get the current transport | ||
* Also only useful for debugging | ||
* @private | ||
* @returns {Object} the current transport | ||
*/ | ||
this.getTransport = function getTransport() { | ||
return _transport; | ||
}; | ||
/** | ||
* The functions called by the stateMachine made available for testing purpose | ||
* @private | ||
*/ | ||
this.actions = actions; | ||
}; | ||
return function CouchDBStoreFactory(data) { | ||
CouchDBStoreConstructor.prototype = new Store(data); | ||
return new CouchDBStoreConstructor; | ||
}; | ||
}); | ||
/** | ||
* https://github.com/flams/CouchDB-emily-tools | ||
* The MIT License (MIT) | ||
* Copyright (c) 2012 Olivier Scherrer <pode.fr@gmail.com> | ||
*/ | ||
define("CouchDBUser", | ||
@@ -80,0 +843,0 @@ |
{ | ||
"name": "couchdb-emily-tools", | ||
"description": "WIP - CouchDB Tools for Emily&Olives applications", | ||
"version": "0.0.333dev", | ||
"description": "CouchDB Tools for Emily&Olives applications", | ||
"version": "1.0.0", | ||
"homepage": "https://github.com/flams/CouchDB-emily-tools", | ||
@@ -18,3 +18,3 @@ "licenses": [{ | ||
"requirejs": ">=2.0.4", | ||
"emily": ">=1.1.4" | ||
"emily": ">=1.2.0" | ||
}, | ||
@@ -21,0 +21,0 @@ "keywords": ["CouchDB", "Emily", "Olives", "Tools"], |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
68716
946
0
1
Updatedemily@>=1.2.0