Merge pull request #303 from shtaft/patch-2

Update README.md
diff --git a/.travis.yml b/.travis.yml
index 7de7b97..c0f8448 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -11,10 +11,11 @@
   - "0.11"
   - "0.12"
   - "iojs"
+  - "4.2"
+  - "node"
 services:
   - couchdb
 os:
   - linux
-  - osx
 before_install:
   - npm update -g npm
diff --git a/README.md b/README.md
index a51d6f7..d2ebc3a 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,7 @@
   - [nano.db.replicate(source, target, [opts], [callback])](#nanodbreplicatesource-target-opts-callback)
   - [nano.db.changes(name, [params], [callback])](#nanodbchangesname-params-callback)
   - [nano.db.follow(name, [params], [callback])](#nanodbfollowname-params-callback)
+  - [nano.db.info([callback])](#nanodbinfocallback)
   - [nano.use(name)](#nanousename)
   - [nano.request(opts, [callback])](#nanorequestopts-callback)
   - [nano.config](#nanoconfig)
@@ -300,6 +301,16 @@
 });
 ```
 
+### nano.db.info([callback])
+
+gets database information.
+
+nano.db.info(function(err, body) {
+  if (!err) {
+    console.log('got database info'', body);
+  }
+});
+
 ### nano.use(name)
 
 creates a scope where you operate inside `name`.
@@ -411,7 +422,7 @@
 The `insert` function can also be used with the method signature `db.insert(doc,[callback])`, where the `doc` contains the `_id` field e.g.
 
 ~~~ js
-var alice = cloudant.use('alice')
+var alice = nano.use('alice')
 alice.insert({ _id: 'myid', crazy: true }, function(err, body) {
   if (!err)
     console.log(body)
@@ -421,7 +432,7 @@
 and also used to update an existing document, by including the `_rev` token in the document being saved:
 
 ~~~ js
-var alice = cloudant.use('alice')
+var alice = nano.use('alice')
 alice.insert({ _id: 'myid', _rev: '1-23202479633c2b380f79507a776743d5', crazy: false }, function(err, body) {
   if (!err)
     console.log(body)
@@ -482,10 +493,10 @@
 
 ### db.list([params], [callback])
 
-list all the docs in the database with optional query string additions `params`.
+list all the docs in the database with optional query string additions `params`. This is useful for searching.
 
 ``` js
-alice.list(function(err, body) {
+alice.list({startkey:'cat', limit:3}, function(err, body) {
   if (!err) {
     body.rows.forEach(function(doc) {
       console.log(doc);
@@ -493,6 +504,7 @@
   }
 });
 ```
+For a full list of params, see [couchdb doc](https://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options).
 
 ### db.fetch(docnames, [params], [callback])
 
diff --git a/examples/express.js b/examples/express.js
index 51dceac..b49ba1d 100644
--- a/examples/express.js
+++ b/examples/express.js
@@ -12,16 +12,19 @@
 
 var express = require('express')
    , db    = require('nano')('http://localhost:5984/my_couch')
-   , app     = module.exports = express.createServer()
+   , app     = module.exports = express()
    ;
 
-app.get('/', function(request,response) {
-    db.get('foo', function (error, body, headers) {
-      if(error) { return response.send(error.message, error['status-code']); }
-      response.send(body, 200);
-    });
-  });
+app.get('/', function(req, res) {
+   db.get('foo', function (error, body, headers) {
+      if(error) {
+         res.status(error.statusCode);
+         return res.send(error.message); 
+      }
+      res.status(200);
+      res.send(body);
+   });
 });
 
 app.listen(3333);
-console.log('server is running. check expressjs.org for more cool tricks');
+console.log('server is running. check expressjs.com for more cool tricks');
diff --git a/lib/nano.js b/lib/nano.js
index ab97228..81d12d7 100644
--- a/lib/nano.js
+++ b/lib/nano.js
@@ -61,6 +61,13 @@
     }
   }
 
+  function scrub(str) {
+    if (str) {
+      str = str.replace(/\/\/(.*)@/,"//XXXXXX:XXXXXX@");
+    }
+    return str;
+  }
+
   function relax(opts, callback) {
     if (typeof opts === 'function') {
       callback = opts;
@@ -224,6 +231,13 @@
       // fix cloudant issues where they give an erlang stacktrace as js
       delete parsed.stack;
 
+      // scrub credentials
+      req.uri = scrub(req.uri);
+      rh.uri = scrub(rh.uri);
+      if (req.headers.cookie) {
+        req.headers.cookie = "XXXXXXX";
+      }
+
       callback(errs.merge({
         message: 'couch returned ' + rh.statusCode,
         scope: 'couch',
@@ -379,12 +393,18 @@
 
     // http://docs.couchdb.org/en/latest/api/document/common.html#delete--db-docid
     function destroyDoc(docName, rev, callback) {
-      return relax({
-        db: dbName,
-        doc: docName,
-        method: 'DELETE',
-        qs: {rev: rev}
-      }, callback);
+      if(!docName) {
+        if(callback)
+          callback("Invalid doc id", null);
+      }
+      else {
+        return relax({
+          db: dbName,
+          doc: docName,
+          method: 'DELETE',
+          qs: {rev: rev}
+        }, callback);
+      }
     }
 
     // http://docs.couchdb.org/en/latest/api/document/common.html#get--db-docid
@@ -452,6 +472,7 @@
         qs = {};
       }
 
+      qs = qs || {};
       qs['include_docs'] = true;
 
       return relax({
@@ -485,6 +506,15 @@
 
       var viewPath = '_design/' + ddoc + '/_' + meta.type + '/'  + viewName;
 
+      // Several search parameters must be JSON-encoded; but since this is an
+      // object API, several parameters need JSON endoding.
+      var paramsToEncode = ['counts', 'drilldown', 'group_sort', 'ranges', 'sort'];
+      paramsToEncode.forEach(function(param) {
+        if (param in qs) {
+          qs[param] = JSON.stringify(qs[param]);
+        }
+      });
+
       if (qs && qs.keys) {
         var body = {keys: qs.keys};
         delete qs.keys;
@@ -533,6 +563,10 @@
 
     // http://docs.couchdb.org/en/latest/api/ddoc/render.html#put--db-_design-ddoc-_update-func-docid
     function updateWithHandler(ddoc, viewName, docName, body, callback) {
+      if (typeof body === 'function') {
+          callback = body;
+          body = undefined;
+      }
       return view(ddoc, viewName + '/' + encodeURIComponent(docName), {
         type: 'update',
         method: 'PUT',
diff --git a/package.json b/package.json
index 6ee064b..7517baf 100644
--- a/package.json
+++ b/package.json
@@ -1,11 +1,11 @@
 {
   "name": "nano",
-  "description": "minimalistic couchdb driver for node.js",
+  "description": "The official CouchDB client for Node.js",
   "license": "Apache-2.0",
-  "homepage": "http://github.com/dscape/nano",
-  "repository": "git://github.com/dscape/nano",
-  "version": "6.1.5",
-  "author": "Nuno Job <nunojobpinto@gmail.com> (http://nunojob.com)",
+  "homepage": "http://github.com/apache/couchdb-nano",
+  "repository": "git://github.com/apache/couchdb-nano",
+  "version": "6.2.0",
+  "author": "Apache CouchDB <dev@couchdb.apache.org> (http://couchdb.apache.org)",
   "keywords": [
     "couchdb",
     "data",
@@ -31,8 +31,7 @@
     "jscs": "^1.7.0",
     "nock": "^0.48.1",
     "endswith": "^0.0.0",
-    "tape-it": "^0.3.1",
-    "pre-commit": "0.0.9"
+    "tape-it": "^0.3.1"
   },
   "scripts": {
     "test": "DEBUG=* NOCK_OFF=true istanbul cover tape tests/*/*/*.js",
diff --git a/tests/fixtures/design/atomic.json b/tests/fixtures/design/atomic.json
index 5a58b91..5712da0 100644
--- a/tests/fixtures/design/atomic.json
+++ b/tests/fixtures/design/atomic.json
@@ -22,6 +22,10 @@
   , "response" : "{\"foo\": \"bar\"}"
   }
 , { "method"   : "put"
+  , "path"     : "/design_atomic/_design/update/_update/addbaz/baz"
+  , "response" : "{\"baz\": \"biz\"}"
+  }
+, { "method"   : "put"
   , "status"   : 201
   , "path"     : "/design_atomic/wat%2Fwat"
   , "body"     : "{\"wat\":\"wat\"}"
diff --git a/tests/integration/design/atomic.js b/tests/integration/design/atomic.js
index 8fedaf1..f948387 100644
--- a/tests/integration/design/atomic.js
+++ b/tests/integration/design/atomic.js
@@ -26,6 +26,10 @@
         var body = JSON.parse(req.body);
         doc[body.field] = body.value;
         return [doc, JSON.stringify(doc)];
+      },
+      addbaz: function (doc, req) {
+        doc.baz = "biz";
+        return [doc, JSON.stringify(doc)];
       }
     }
   }, '_design/update', function(err) {
@@ -51,6 +55,16 @@
   });
 });
 
+it('should be able to update atomically without a body', function (assert) {
+  db.insert({}, 'baz', function (error, doc) {
+    db.atomic('update', 'addbaz', 'baz', function (error, response) {
+      assert.equal(error, null, 'should be able to update');
+      assert.equal(response.baz, 'biz', 'and the new field is present');
+      assert.end();
+    });
+  });
+});
+
 it('should be able to update with slashes on the id', function(assert) {
   db.insert({'wat': 'wat'}, 'wat/wat', function(error, foo) {
     assert.equal(error, null, 'stores `wat`');
diff --git a/tests/integration/document/destroy.js b/tests/integration/document/destroy.js
index 0d6bdf7..7e07448 100644
--- a/tests/integration/document/destroy.js
+++ b/tests/integration/document/destroy.js
@@ -29,6 +29,14 @@
   });
 });
 
+it('should not delete a db', function(assert) {
+  db.destroy(undefined, undefined, function(error, response) {
+    assert.equal(error, 'Invalid doc id', 'validated delete parameters');
+    assert.equal(response, null, 'ok!');
+    assert.end();
+  });
+});
+
 it('should delete a document', function(assert) {
   db.destroy('foobaz', rev, function(error, response) {
     assert.equal(error, null, 'deleted foo');