Skip to content

Commit

Permalink
(pouchdb/express-pouchdb#232) - Modernize pouchdb-rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
marten-de-vries committed Feb 5, 2016
1 parent e54316f commit fcbba7b
Show file tree
Hide file tree
Showing 9 changed files with 944 additions and 75 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
node_modules
dist
coverage
30 changes: 30 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
sudo: false
language: node_js

cache:
directories:
- node_modules

node_js:
- "0.10"

services:
- couchdb

before_install:
- npm i -g npm@^2.0.0

before_script:
- npm prune

script: npm run $COMMAND

env:
matrix:
- COMMAND='helper -- lint'
- COMMAND='helper -- js-test'
- COMMAND='build'

#after_success:
# - npm run helper -- semantic-release

55 changes: 53 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,60 @@
pouchdb-rewrite
===============

[![Build Status](https://travis-ci.org/pouchdb/pouchdb-rewrite.svg?branch=master)](https://travis-ci.org/pouchdb/pouchdb-rewrite)
[![Dependency Status](https://david-dm.org/pouchdb/pouchdb-rewrite.svg)](https://david-dm.org/pouchdb/pouchdb-rewrite)
[![devDependency Status](https://david-dm.org/pouchdb/pouchdb-rewrite/dev-status.svg)](https://david-dm.org/pouchdb/pouchdb-rewrite#info=devDependencies)

A PouchDB plug-in that allows you to re-use your CouchDB rewrites on the
client side. A browser version is available.

See also [pouchdb-rewrite's documentation](http://pythonhosted.org/Python-PouchDB/js-plugins.html#pouchdb-rewrite-plug-in)
#TODO: update, rst -> md, integrate
```rst
.. _pouchdb-rewrite-plug-in:
PouchDB Rewrite plug-in
=======================
+----------------------+--------------------+
| NodeJS package name: | `pouchdb-rewrite`_ |
+----------------------+--------------------+
| Browser object name: | ``window.Rewrite`` |
+----------------------+--------------------+
First, make sure you understand CouchDB rewrites. A good starting point
is `the rewrite documentation`_.
.. _pouchdb-rewrite: https://www.npmjs.org/package/pouchdb-rewrite
.. _the rewrite documentation: http://docs.couchdb.org/en/latest/api/ddoc/rewrites.html
.. js:function:: Rewrite.rewrite(rewritePath[, options[, callback]])
Figures out where to redirect to, and then executes the corresponding
PouchDB function, with the appropriate arguments gotten from the
request object that has been generated from the ``options``
parameter.
:param string rewritePath: a path of the form
``"designDocName/rewrite/path"``. Specifies the design document
to use the rewrites from, and the path you'd find in CouchDB
after the ``/_rewrite`` part of the URL. Keep in mind that you
can't specify a query parameter in the url form (i.e. no
``?a=b``). Instead use the ``options.query`` parameter.
:param object options: A CouchDB request object stub. Important
properties of those for rewrites are ``options.query`` and
``options.method``. An additional boolean option is available:
``options.withValidation``, if true, this function routes to
``db.validating*`` functions instead of ``db.*`` functions if
relevant.
:returns: whatever output the function that the rewrite routed to
produced. Or, in the case of an 'http' database, a CouchDB
response object.
.. js:function:: Rewrite.rewriteResultRequestObject(rewritePath[, options[, callback]])
See the :js:func:`Rewrite.rewrite` function for information on the
parameters. The difference with it is that this function doesn't try
to route the rewrite to a function.
[Website of this plug-in and a few others](http://python-pouchdb.marten-de-vries.nl/plugins.html)
:returns: A CouchDB request object that points to the resource
obtained by following the redirect.
```
78 changes: 38 additions & 40 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2014, Marten de Vries
Copyright 2014-2015, Marten de Vries
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
Expand All @@ -25,14 +25,14 @@
rewrite tests, which haven't (yet) been ported to Python/this plug-in.
*/

"use strict";
'use strict';

var couchdb_objects = require("couchdb-objects");
var nodify = require("promise-nodify");
var httpQuery = require("pouchdb-req-http-query");
var extend = require("extend");
var PouchPluginError = require("pouchdb-plugin-error");
var routePouchDB = require("pouchdb-route");
var couchdb_objects = require('couchdb-objects');
var nodify = require('promise-nodify');
var httpQuery = require('pouchdb-req-http-query');
var extend = require('extend');
var PouchPluginError = require('pouchdb-plugin-error');
var routePouchDB = require('pouchdb-route');

exports.rewriteResultRequestObject = function (rewritePath, options, callback) {
var args = parseArgs(this, rewritePath, options, callback);
Expand All @@ -42,7 +42,7 @@ exports.rewriteResultRequestObject = function (rewritePath, options, callback) {
};

function parseArgs(db, rewritePath, options, callback) {
if (["function", "undefined"].indexOf(typeof options) !== -1) {
if (['function', 'undefined'].indexOf(typeof options) !== -1) {
callback = options;
options = {};
}
Expand All @@ -51,57 +51,57 @@ function parseArgs(db, rewritePath, options, callback) {
callback: callback,
options: options,
designDocName: splitUrl(rewritePath)[0],
rewriteUrl: splitUrl(rewritePath).slice(1),
rewriteUrl: splitUrl(rewritePath).slice(1)
};
}

function splitUrl(url) {
return url.split("/").filter(function (part) {
return url.split('/').filter(function (part) {
return part;
});
}

function buildRewriteResultReqObj(db, designDocName, rewriteUrl, options) {
return db.get("_design/" + designDocName).then(function (ddoc) {
return db.get('_design/' + designDocName).then(function (ddoc) {
//rewrite algorithm source:
//https://github.com/apache/couchdb/blob/master/src/couchdb/couch_httpd_rewrite.erl
var rewrites = ddoc.rewrites;
if (typeof rewrites === "undefined") {
if (typeof rewrites === 'undefined') {
throw new PouchPluginError({
status: 404,
name: "rewrite_error",
message:"Invalid path."
name: 'rewrite_error',
message:'Invalid path.'
});
}
if (!Array.isArray(rewrites)) {
throw new PouchPluginError({
status: 400,
name: "rewrite_error",
message: "Rewrite rules should be a JSON Array."
name: 'rewrite_error',
message: 'Rewrite rules should be a JSON Array.'
});
}
var rules = rewrites.map(function (rewrite) {
if (typeof rewrite.to === "undefined") {
if (typeof rewrite.to === 'undefined') {
throw new PouchPluginError({
status: 500,
name:"error",
message:"invalid_rewrite_target"
name:'error',
message:'invalid_rewrite_target'
});
}
return {
method: rewrite.method || "*",
from: splitUrl(typeof rewrite.from == "undefined" ? "*" : rewrite.from),
method: rewrite.method || '*',
from: splitUrl(typeof rewrite.from == 'undefined' ? '*' : rewrite.from),
to: splitUrl(rewrite.to),
query: rewrite.query || {}
};
});
var match = tryToFindMatch({
method: options.method || "GET",
method: options.method || 'GET',
url: rewriteUrl,
query: options.query || {}
}, rules);

var pathEnd = ["_design", designDocName];
var pathEnd = ['_design', designDocName];
pathEnd.push.apply(pathEnd, match.url);

options.query = match.query;
Expand Down Expand Up @@ -131,7 +131,7 @@ function tryToFindMatch(input, rules) {

var ruleQueryArgs = replaceQueryBindings(rules[0].query, allBindings);
var query = extend(allBindings, ruleQueryArgs);
delete query["*"];
delete query['*'];

return {
url: url,
Expand All @@ -146,7 +146,7 @@ function tryToFindMatch(input, rules) {
}

function throw404() {
throw new PouchPluginError({status: 404, name: "not_found", message: "missing"});
throw new PouchPluginError({status: 404, name: 'not_found', message: 'missing'});
}

function arrayEquals(a, b) {
Expand All @@ -155,22 +155,22 @@ function arrayEquals(a, b) {

function methodMatch(required, given) {
//corresponds to bind_method in the couchdb code
return required === "*" || required === given;
return required === '*' || required === given;
}

function pathMatch(required, given, bindings) {
//corresponds to bind_path in the couchdb code
if (arrayEquals(required, []) && arrayEquals(given, [])) {
return {ok: true, remaining: []};
}
if (arrayEquals(required, ["*"])) {
bindings["*"] = given[0];
if (arrayEquals(required, ['*'])) {
bindings['*'] = given[0];
return {ok: true, remaining: given.slice(1)};
}
if (arrayEquals(given, [])) {
return {ok: false};
}
if ((required[0] || "")[0] === ":") {
if ((required[0] || '')[0] === ':') {
bindings[required[0].slice(1)] = given[0];
return pathMatch(required.slice(1), given.slice(1), bindings);
}
Expand All @@ -182,11 +182,8 @@ function pathMatch(required, given, bindings) {

function replacePathBindings(path, bindings) {
for (var i = 0; i < path.length; i += 1) {
if (typeof path[i] !== "string") {
continue;
}
var bindingName = path[i];
if (bindingName[0] === ":") {
if (bindingName[0] === ':') {
bindingName = bindingName.slice(1);
}
if (bindings.hasOwnProperty(bindingName)) {
Expand All @@ -198,21 +195,22 @@ function replacePathBindings(path, bindings) {

function replaceQueryBindings(query, bindings) {
for (var key in query) {
/* istanbul ignore if */
if (!query.hasOwnProperty(key)) {
continue;
}
if (typeof query[key] === "object") {
if (typeof query[key] === 'object') {
query[key] = replaceQueryBindings(query[key], bindings);
} else if (typeof query[key] === "string") {
} else if (typeof query[key] === 'string') {
var bindingKey = query[key];
if (bindingKey[0] === ":") {
if (bindingKey[0] === ':') {
bindingKey = bindingKey.slice(1);
}
if (bindings.hasOwnProperty(bindingKey)) {
var val = bindings[bindingKey];
try {
val = JSON.parse(val);
} catch (e) {}
} catch (e) {/* just use the raw string*/}
query[key] = val;
}
}
Expand All @@ -227,7 +225,7 @@ exports.rewrite = function (rewritePath, options, callback) {
var args = parseArgs(this, rewritePath, options, callback);

var promise;
if (["http", "https"].indexOf(args.db.type()) === -1) {
if (['http', 'https'].indexOf(args.db.type()) === -1) {
promise = offlineRewrite(args.db, args.designDocName, args.rewriteUrl, args.options);
} else {
promise = httpRewrite(args.db, args.designDocName, args.rewriteUrl, args.options);
Expand All @@ -252,7 +250,7 @@ function httpRewrite(db, designDocName, rewriteUrl, options) {
//no choice when http...
delete options.withValidation;

var pathEnd = ["_design", designDocName, "_rewrite"];
var pathEnd = ['_design', designDocName, '_rewrite'];
pathEnd.push.apply(pathEnd, rewriteUrl);
var reqPromise = couchdb_objects.buildRequestObject(db, pathEnd, options);
return reqPromise.then(httpQuery.bind(null, db));
Expand Down
74 changes: 41 additions & 33 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,35 +1,43 @@
{
"name": "pouchdb-rewrite",
"version": "1.0.6",
"main": "index.js",
"description": "A PouchDB plug-in that allows you to re-use your CouchDB rewrites on the client side.",
"repository": "pouchdb/pouchdb-rewrite",
"homepage": "http://python-pouchdb.marten-de-vries.nl/plugins.html",
"keywords": [
"pouch",
"pouchdb",
"couch",
"couchdb",
"rewrite",
"design"
],
"license": "Apache-2.0",
"author": "Marten de Vries",
"dependencies": {
"couchdb-objects": "^1.0.0",
"pouchdb-req-http-query": "^1.0.0",
"promise-nodify": "^1.0.0",
"extend": "^1.2.1",
"pouchdb-plugin-error": "^1.0.0",
"pouchdb-route": "^1.0.0"
},
"devDependencies": {
"browserify": "^4.1.8",
"es3ify": "^0.1.3",
"uglify-js": "^2.4.13"
},
"scripts": {
"build-js": "mkdir -p dist && browserify index.js -s Rewrite -g es3ify -o dist/pouchdb-rewrite.js",
"build": "npm run build-js; cd dist; uglifyjs pouchdb-rewrite.js -mc > pouchdb-rewrite.min.js"
}
"name": "pouchdb-rewrite",
"version": "1.0.6",
"main": "index.js",
"description": "A PouchDB plug-in that allows you to re-use your CouchDB rewrites on the client side.",
"repository": {
"type": "git",
"url": "https://github.com/pouchdb/pouchdb-rewrite.git"
},
"keywords": [
"pouch",
"pouchdb",
"couch",
"couchdb",
"rewrite",
"design"
],
"license": "Apache-2.0",
"author": "Marten de Vries",
"dependencies": {
"couchdb-objects": "^1.0.0",
"pouchdb-req-http-query": "^1.0.3",
"promise-nodify": "^1.0.0",
"extend": "^1.2.1",
"pouchdb-plugin-error": "^1.0.0",
"pouchdb-route": "^1.0.0"
},
"devDependencies": {
"pouchdb-all-dbs": "^1.0.1",
"pouchdb-list": "^1.0.6",
"pouchdb-plugin-helper": "^2.0.1",
"pouchdb-seamless-auth": "^1.0.2",
"pouchdb-security": "^1.2.5",
"pouchdb-show": "^1.0.7",
"pouchdb-update": "^1.0.7",
"pouchdb-validation": "^1.2.1"
},
"scripts": {
"helper": "./node_modules/.bin/pouchdb-plugin-helper",
"test": "npm run helper -- test",
"build": "npm run helper -- build Rewrite"
}
}
Loading

0 comments on commit fcbba7b

Please sign in to comment.