Skip to content

Commit

Permalink
TouchDB / CouchDB adapter with a dependency on $.ajax (Zepto, jQuery,…
Browse files Browse the repository at this point in the history
… etc)
  • Loading branch information
jchris committed Jan 16, 2013
1 parent 34d1255 commit 4e5483a
Show file tree
Hide file tree
Showing 4 changed files with 257 additions and 0 deletions.
2 changes: 2 additions & 0 deletions makefile
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ build:
cp ./src/adapters/indexed-db.js ./lib/lawnchair-adapter-indexed-db-$(VERSION).js
cp ./src/adapters/webkit-sqlite.js ./lib/lawnchair-adapter-webkit-sqlite-$(VERSION).js
cp ./src/adapters/html5-filesystem.js ./lib/lawnchair-adapter-html5-filesystem-$(VERSION).js
cp ./src/adapters/touchdb-couchdb.js ./lib/lawnchair-adapter-touchdb-couchdb-$(VERSION).js

min:
java -jar ./util/compiler.jar --js ./lib/lawnchair-$(VERSION).js > ./lib/lawnchair-$(VERSION).min.js
Expand All @@ -58,6 +59,7 @@ test:
cp ./src/adapters/indexed-db.js ./test/lib/lawnchair-adapter-indexed-db.js
cp ./src/adapters/webkit-sqlite.js ./test/lib/lawnchair-adapter-webkit-sqlite.js
cp ./src/adapters/html5-filesystem.js ./test/lib/lawnchair-adapter-html5-filesystem.js
cp ./src/adapters/touchdb-couchdb.js ./test/lib/lawnchair-adapter-touchdb-couchdb.js

open ./test/index.html
#open ./test/plugin/aggregation.html
Expand Down
251 changes: 251 additions & 0 deletions src/adapters/touchdb-couchdb.js
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;
}
}
/////
})())
2 changes: 2 additions & 0 deletions test/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
<script src="lib/lawnchair-adapter-indexed-db.js"></script>
<script src="lib/lawnchair-adapter-webkit-sqlite.js"></script>
<script src="lib/lawnchair-adapter-html5-filesystem.js"></script>
<script src="lib/zepto.js"></script>
<script src="lib/lawnchair-adapter-touchdb-couchdb.js"></script>
<script src="lawnchair-spec.js"></script>

<script>
Expand Down
Loading

0 comments on commit 4e5483a

Please sign in to comment.