Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Made caching based off etags, added caching for views and lists, added shows support for documents. #147

Open
wants to merge 7 commits into from

2 participants

@mintplant

"caching based off etags" - see: #135

Caching for views and lists works in the same way.

Database.prototype.show now exists in documents.js

@jawsome jawsome referenced this pull request from a commit in jawsome/cradle
@jawsome jawsome Merge #147 from minthead/master b5258b6
@jawsome

Looks like changes made here are not passing the unit tests. I've updated versions, should the queries or arguments have changed in the tests?

https://travis-ci.org/jawsome/cradle/jobs/9866884

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Feb 24, 2012
  1. @mintplant
Commits on Feb 28, 2012
  1. @mintplant

    Added caching for views.

    mintplant authored
  2. @mintplant

    Added in caching for lists; fixed up views a bit; changed the way lis…

    mintplant authored
    …ts work so the raw data is actually returned, rather than an empty object if it's not JSON - basically, it bypasses the Response object entirely.
  3. @mintplant
Commits on Mar 2, 2012
  1. @mintplant
Commits on Mar 5, 2012
  1. @mintplant
Commits on Mar 21, 2012
  1. @mintplant

    Added Travis CI support.

    mintplant authored
This page is out of date. Refresh to see the latest.
View
6 .travis.yml
@@ -0,0 +1,6 @@
+language: node_js
+node_js:
+ - 0.4
+ - 0.6
+ - 0.7
+install: npm install -d
View
12 lib/cradle.js
@@ -175,8 +175,16 @@ cradle.Connection.prototype.request = function (options, callback) {
options.headers["Content-Length"] = Buffer.byteLength(options.body);
options.headers["Content-Type"] = "application/json";
}
+
+ if (options.cache) {
+ options.headers["If-None-Match"] = options.cache.etag;
+ }
return this.rawRequest(options, function (err, res, body) {
+ if (options.cache && res.statusCode === 304) {
+ return callback(null, options.cache.store, true, res.headers.etag);
+ }
+
if (err) {
return callback(err);
}
@@ -188,7 +196,7 @@ cradle.Connection.prototype.request = function (options, callback) {
body.headers.status = res.statusCode;
return callback(body);
}
-
+
try { body = JSON.parse(body) }
catch (err) { }
@@ -198,7 +206,7 @@ cradle.Connection.prototype.request = function (options, callback) {
return callback(body);
}
- callback(null, self.options.raw ? body : new cradle.Response(body, res));
+ callback(null, (self.options.raw || options.raw) ? body : new cradle.Response(body, res), false, res.headers.etag);
});
};
View
32 lib/cradle/cache.js
@@ -16,10 +16,11 @@ this.Cache = function (options) {
this.Cache.prototype = {
// API
- get: function (id) { return this.query('get', id) },
- save: function (id, doc) { return this.query('save', id, doc) },
- purge: function (id) { return this.query('purge', id) },
- has: function (id) { return this.query('has', id) },
+ get: function (id) { return this.query('get', id) },
+ headers: function (id) { return this.query('headers', id) },
+ save: function (id, doc) { return this.query('save', id, doc) },
+ purge: function (id) { return this.query('purge', id) },
+ has: function (id) { return this.query('has', id) },
_get: function (id) {
var entry;
@@ -42,6 +43,13 @@ this.Cache.prototype = {
}
}
},
+ _headers: function (id) {
+ if (id in this.store) {
+ return this.store[id].document.headers;
+ } else {
+ return null;
+ }
+ },
_has: function (id) {
return id in this.store;
},
@@ -93,10 +101,14 @@ this.Cache.prototype = {
};
function clone(obj) {
- return Object.keys(obj).reduce(function (clone, k) {
- if (! obj.__lookupGetter__(k)) {
- clone[k] = obj[k];
- }
- return clone;
- }, {});
+ if (Array.isArray(obj)) {
+ return obj.slice(0);
+ } else {
+ return Object.keys(obj).reduce(function (clone, k) {
+ if (! obj.__lookupGetter__(k)) {
+ clone[k] = obj[k];
+ }
+ return clone;
+ }, {});
+ }
}
View
44 lib/cradle/database/documents.js
@@ -19,6 +19,7 @@ Database.prototype.head = function (id, callback) {
Database.prototype.get = function (id, rev) {
var args = new (Args)(arguments),
options = null,
+ cache = null,
that = this;
if (Array.isArray(id)) { // Bulk GET
@@ -35,13 +36,15 @@ Database.prototype.get = function (id, rev) {
if (typeof(rev) === 'string') { options = { rev: rev } }
else if (typeof(rev) === 'object') { options = rev }
} else if (this.cache.has(id)) {
- return args.callback(null, this.cache.get(id));
+ cache = { store: this.cache.get(id) };
+ cache.etag = '"' + cache.store._rev + '"';
}
this.query({
path: cradle.escape(id),
- query: options
- }, function (err, res) {
- if (! err) that.cache.save(res.id, res.json);
+ query: options,
+ cache: cache
+ }, function (err, res, cached) {
+ if (! err && ! cached) that.cache.save(res.id, res.json);
args.callback(err, res);
});
}
@@ -212,4 +215,35 @@ Database.prototype.remove = function (id, rev) {
if (! err) { that.cache.purge(id) }
args.callback(err, res);
});
-};
+};
+
+// Query a show, passing any options to the query string.
+// Some query string parameters' values have to be JSON-encoded.
+Database.prototype.show = function (path, options) {
+ var args = new(Args)(arguments),
+ that = this,
+ cachepath = path,
+ cache = null;
+
+ path = path.split('/');
+ path = ['_design', path[0], '_show', path[1], path[2]].map(querystring.escape).join('/');
+
+ if (typeof(options) === 'object') {
+ cachepath += '?' + querystring.stringify(options);
+ }
+
+ if (this.cache.has(cachepath)) {
+ cache = { store: this.cache.get(cachepath).value, etag: this.cache.headers(cachepath).etag };
+ }
+
+ return this.query({
+ method: 'GET',
+ path: path,
+ query: options,
+ raw: true,
+ cache: cache
+ }, function(err, res, cached, etag) {
+ if (! err && ! cached) that.cache.save(cachepath, { value: res, headers: { etag: etag } });
+ args.callback(err, (! cached && Array.isArray(res)) ? res.slice(0) : res, etag);
+ });
+};
View
82 lib/cradle/database/views.js
@@ -9,19 +9,36 @@ Database.prototype.all = function (options, callback) {
options = {};
}
- return this._getOrPostView('/_all_docs', options, callback);
+ return this._getOrPostView('/_all_docs', { query: options }, callback);
};
// Query a view, passing any options to the query string.
// Some query string parameters' values have to be JSON-encoded.
Database.prototype.view = function (path, options) {
var callback = new(Args)(arguments).callback,
- body;
+ body,
+ cachepath,
+ cache = null,
+ that = this;
path = path.split('/');
path = ['_design', path[0], '_view', path[1]].map(querystring.escape).join('/');
- return this._getOrPostView(path, options, callback);
+ cachepath = path;
+
+ if (!options.body) {
+ if (options && typeof options === 'object') {
+ cachepath += '?' + querystring.stringify(options);
+ }
+ if(this.cache.has(cachepath)) {
+ cache = { store: this.cache.get(cachepath), etag: this.cache.headers(cachepath).etag };
+ }
+ }
+
+ return this._getOrPostView(path, { query: options, cache: cache }, function(err, res, cached, etag) {
+ if (! err && ! cached) that.cache.save(cachepath, res);
+ callback(err, (!cached && Array.isArray(res)) ? res.slice(0) : res, etag);
+ });
};
Database.prototype.temporaryView = function (doc, options, callback) {
@@ -67,41 +84,54 @@ Database.prototype.compact = function (design) {
// Query a list, passing any options to the query string.
// Some query string parameters' values have to be JSON-encoded.
Database.prototype.list = function (path, options) {
- var callback = new(Args)(arguments).callback;
- path = path.split('/');
+ var callback = new(Args)(arguments).callback,
+ cachepath = path,
+ cache = null,
+ that = this;
+
+ path = path.split('/'),
+ path = ['_design', path[0], '_list', path[1], path[2]].map(querystring.escape).join('/');
+
+ if (!options.body) {
+ if (options && typeof options === 'object') {
+ cachepath += '?' + querystring.stringify(options);
+ }
+ if(this.cache.has(cachepath)) {
+ cache = { store: this.cache.get(cachepath).value, etag: this.cache.headers(cachepath).etag };
+ }
+ }
this._getOrPostView(
- ['_design', path[0], '_list', path[1], path[2]].map(querystring.escape).join('/'),
- options,
- callback
+ path,
+ { query: options, cache: cache, raw: true },
+ function(err, res, cached, etag) {
+ if (! err && ! cached) that.cache.save(cachepath, { value: res, headers: { etag: etag } });
+ callback(err, (! cached && Array.isArray(res)) ? res.slice(0) : res, etag);
+ }
);
};
//
// Helper function which parses options and makes either a `GET`
-// or `POST` request to `path` depending on if `options.keys` or
-// `options.body` is present.
+// or `POST` request to `path` depending on if `options.query.keys` or
+// `options.query.body` is present.
//
Database.prototype._getOrPostView = function (path, options, callback) {
- options = parseOptions(options);
+ var query = parseOptions(options.query);
- if (options && options.body) {
- body = options.body;
- delete options.body;
+ if (query && query.body) {
+ options.body = query.body;
+ delete query.body;
- return this.query({
- method: 'POST',
- path: path,
- query: options,
- body: body
- }, callback);
- }
+ options.method = 'POST';
+ } else {
+ options.method = 'GET';
+ }
- return this.query({
- method: 'GET',
- path: path,
- query: options
- }, callback);
+ options.path = path;
+ options.query = query;
+
+ return this.query(options, callback);
}
//
Something went wrong with that request. Please try again.