forked from brianleroux/lawnchair
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
TouchDB / CouchDB adapter with a dependency on $.ajax (Zepto, jQuery,…
… etc)
- Loading branch information
Showing
4 changed files
with
257 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,251 @@ | ||
// Original author: Chris Anderson jchris@couchbase.com | ||
// Copyright 2013 Couchbase | ||
Lawnchair.adapter('touchdb-couchdb', (function(){ | ||
function makePath (base, path) { | ||
var k, q, query = [], first = true, uri; | ||
if (path) { | ||
if ($.isArray(path)) { | ||
if (typeof path[path.length-1] == 'object') { | ||
q = path[path.length-1]; | ||
path = path.slice(0, path.length-1); | ||
for (k in q) { | ||
if (['startkey', 'endkey', 'start_key', 'end_key', 'key'].indexOf(k) !== -1) { | ||
v = JSON.stringify(q[k]) | ||
} else { | ||
v = q[k]; | ||
} | ||
query.push(encodeURIComponent(k)+'='+encodeURIComponent(v.toString())); | ||
} | ||
query = query.join('&'); | ||
} | ||
uri = (path.map(encodeURIComponent)).join('/'); | ||
if (query.toString()) { | ||
uri = uri + "?" + query.toString(); | ||
} | ||
return base + "/" + uri; | ||
} else { | ||
return base + "/" + path; | ||
} | ||
} else { | ||
return base; | ||
} | ||
} | ||
|
||
function makeDb(dbName) { | ||
if (location.protocol === 'file:') { | ||
console.log("file:// url, so assume TouchDB-iOS is whitelisted at http://localhost.touchdb./") | ||
dbName = "http://localhost.touchdb./"+dbName; | ||
} else { | ||
console.log("assume CouchDB is reachable at "+location.origin); | ||
dbName = "/"+dbName; | ||
} | ||
function dbReq(/* path, json, cb */) { | ||
var cb, opts = { | ||
contentType : "application/json", | ||
type : this | ||
}; | ||
if (typeof arguments[0] == 'function') { | ||
// the db path | ||
cb = arguments[0]; | ||
opts.url = makePath(dbName); | ||
} else if (typeof arguments[1] == 'function') { | ||
// new path | ||
cb = arguments[1]; | ||
opts.url = makePath(dbName, arguments[0]); | ||
} else { | ||
// we have a body | ||
cb = arguments[2]; | ||
opts.url = makePath(dbName, arguments[0]); | ||
opts.data = JSON.stringify(arguments[1]); | ||
} | ||
opts.complete = function(xhr, code) { | ||
var body; | ||
try { | ||
body = JSON.parse(xhr.responseText); | ||
} catch(e) { | ||
body = xhr.responseText; | ||
} | ||
if (xhr.status >= 400) { | ||
if (body.error) { | ||
cb(body, xhr); | ||
} else { | ||
cb(xhr.status, body, xhr) | ||
} | ||
} else { | ||
cb(null, body); | ||
} | ||
}; | ||
return $.ajax(opts); | ||
} | ||
function allReq(){ | ||
return dbReq.apply("GET", arguments); | ||
}; | ||
"get put post head del".split(" ").forEach(function(v){ | ||
allReq[v] = function() { | ||
var type; | ||
if (v == "del") { | ||
type = "DELETE"; | ||
} else { | ||
type = v.toUpperCase(); | ||
} | ||
return dbReq.apply(type, arguments); | ||
} | ||
}); | ||
return allReq; | ||
} | ||
|
||
function forceSave(db, objs, cb) { | ||
JSON.stringify(objs); // to catch cyclic errors while we can | ||
db.post("_all_docs", {keys:objs.map(function(o){return o._id;})}, | ||
function(err, view) { | ||
if (err) return cb(err); | ||
for (var i = 0; i < view.rows.length; i++) { | ||
if (view.rows[i].value) { | ||
objs[i]._rev = view.rows[i].value.rev; | ||
} | ||
}; | ||
db.post("_bulk_docs", {docs:objs}, function(err, ok) { | ||
if (err) return cb(err); | ||
for (var i = 0; i < ok.length; i++) { | ||
objs[i]._rev = ok[i].rev; | ||
}; | ||
cb(null, objs); | ||
}); | ||
}); | ||
}; | ||
|
||
return { | ||
valid: function() { | ||
if ($ && typeof $.ajax == "function") { | ||
return true; | ||
} else { | ||
console.log("touchdb-couchdb Lawnchair adapter requires Zepto or jQuery for $.ajax()"); | ||
return false; | ||
} | ||
}, | ||
|
||
init: function (options, callback) { | ||
var self = this; | ||
this.name = options.name; | ||
this.db = makeDb(this.name); | ||
this.db.put(function(err, ok) { | ||
if (err && err.error !== "file_exists") { | ||
throw(err); | ||
} else { | ||
self.fn(self.name, callback).call(self, self); | ||
} | ||
}); | ||
return this; | ||
}, | ||
|
||
keys: function (cb) { | ||
var self = this; | ||
this.db("_all_docs", function(err, view){ | ||
var ids = []; | ||
if (!err && view.rows) { | ||
ids = view.rows.map(function(r){return r.id;}); | ||
} | ||
self.fn('keys', cb).call(this, ids); | ||
}); | ||
return this; | ||
}, | ||
save: function(obj, cb) { | ||
obj._id = obj.key = obj.key || this.uuid(); | ||
var self = this; | ||
forceSave(self.db, [obj], function(err){ | ||
if (err) throw(err); | ||
if (cb) { | ||
self.lambda(cb).call(self, obj) | ||
} | ||
}); | ||
return this; | ||
}, | ||
|
||
batch: function (objs, cb) { | ||
var self = this; | ||
objs = objs.map(function(obj) { | ||
obj._id = obj.key = obj.key || self.uuid(); | ||
return obj; | ||
}); | ||
forceSave(self.db, objs, function(err){ | ||
if (err) throw(err); | ||
if (cb) { | ||
self.lambda(cb).call(self, objs) | ||
} | ||
}); | ||
return this; | ||
}, | ||
|
||
get: function (keyOrArray, cb) { | ||
var self = this; | ||
var keys = this.isArray(keyOrArray) ? keyOrArray : [keyOrArray]; | ||
this.db.post(["_all_docs", {include_docs:true}], {keys:keys}, function(err, view) { | ||
if (err) throw (err); | ||
var docs = []; | ||
view.rows.forEach(function(r){ | ||
docs.push(r.doc||null); | ||
}); | ||
if (cb) { | ||
if (self.isArray(keyOrArray)) { | ||
self.lambda(cb).call(self, docs); | ||
} else { | ||
self.lambda(cb).call(self, docs[0]); | ||
} | ||
} | ||
}); | ||
return this; | ||
}, | ||
|
||
exists: function (key, cb) { | ||
this.get(key, function(doc){ | ||
this.lambda(cb).call(this, !!doc); | ||
}); | ||
return this; | ||
}, | ||
|
||
all: function (cb) { | ||
var self = this; | ||
this.db(["_all_docs", {include_docs:true}], function(err, view){ | ||
var docs = []; | ||
if (!err && view.rows) { | ||
docs = view.rows.map(function(r){return r.doc;}); | ||
} | ||
self.fn(self.name, cb).call(self, docs); | ||
}); | ||
return this; | ||
}, | ||
|
||
remove: function (keyOrArray, cb) { | ||
var dels = this.isArray(keyOrArray) ? keyOrArray : [keyOrArray]; | ||
var self = this; | ||
dels = dels.map(function(d){ | ||
var id = d.key || d; | ||
return {_id : id, _deleted : true}; | ||
}); | ||
forceSave(this.db, dels, function(err) { | ||
if (err) throw (err); | ||
if (cb) self.lambda(cb).call(self); | ||
}); | ||
return this; | ||
}, | ||
|
||
nuke: function (cb) { | ||
// nuke leaves us with an empty database | ||
var self = this; | ||
this.db.del(function(err, ok) { | ||
if (err) { | ||
throw(err); | ||
} else { | ||
self.db.put(function(err, ok) { | ||
if (err && !(err.error == "file_exists")) { | ||
throw (err); | ||
} | ||
if (cb) self.lambda(cb).call(self) | ||
}); | ||
} | ||
}); | ||
return this; | ||
} | ||
} | ||
///// | ||
})()) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.