Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

install cradle

  • Loading branch information...
commit cec655e007bd2cd2d5a8f044aefd689dcc0d203e 1 parent 71bd5bc
@mamund mamund authored
Showing with 5,041 additions and 0 deletions.
  1. +20 −0 node_modules/cradle/LICENSE
  2. +10 −0 node_modules/cradle/Makefile
  3. +260 −0 node_modules/cradle/README.md
  4. +628 −0 node_modules/cradle/lib/cradle.js
  5. +102 −0 node_modules/cradle/lib/cradle/cache.js
  6. +123 −0 node_modules/cradle/lib/cradle/response.js
  7. +1 −0  node_modules/cradle/node_modules/.bin/vows
  8. +20 −0 node_modules/cradle/node_modules/vargs/LICENSE
  9. +34 −0 node_modules/cradle/node_modules/vargs/README.md
  10. +70 −0 node_modules/cradle/node_modules/vargs/lib/vargs.js
  11. +12 −0 node_modules/cradle/node_modules/vargs/package.json
  12. +1 −0  node_modules/cradle/node_modules/vows/.gitignore
  13. +20 −0 node_modules/cradle/node_modules/vows/LICENSE
  14. +7 −0 node_modules/cradle/node_modules/vows/Makefile
  15. +39 −0 node_modules/cradle/node_modules/vows/README.md
  16. +506 −0 node_modules/cradle/node_modules/vows/bin/vows
  17. +27 −0 node_modules/cradle/node_modules/vows/lib/assert/error.js
  18. +205 −0 node_modules/cradle/node_modules/vows/lib/assert/macros.js
  19. +77 −0 node_modules/cradle/node_modules/vows/lib/assert/utils.js
  20. +193 −0 node_modules/cradle/node_modules/vows/lib/vows.js
  21. +131 −0 node_modules/cradle/node_modules/vows/lib/vows/console.js
  22. +55 −0 node_modules/cradle/node_modules/vows/lib/vows/context.js
  23. +29 −0 node_modules/cradle/node_modules/vows/lib/vows/coverage/file.js
  24. +2 −0  node_modules/cradle/node_modules/vows/lib/vows/coverage/fragments/coverage-foot.html
  25. +61 −0 node_modules/cradle/node_modules/vows/lib/vows/coverage/fragments/coverage-head.html
  26. +54 −0 node_modules/cradle/node_modules/vows/lib/vows/coverage/report-html.js
  27. +38 −0 node_modules/cradle/node_modules/vows/lib/vows/coverage/report-plain.js
  28. +28 −0 node_modules/cradle/node_modules/vows/lib/vows/extras.js
  29. +67 −0 node_modules/cradle/node_modules/vows/lib/vows/reporters/dot-matrix.js
  30. +16 −0 node_modules/cradle/node_modules/vows/lib/vows/reporters/json.js
  31. +8 −0 node_modules/cradle/node_modules/vows/lib/vows/reporters/silent.js
  32. +44 −0 node_modules/cradle/node_modules/vows/lib/vows/reporters/spec.js
  33. +39 −0 node_modules/cradle/node_modules/vows/lib/vows/reporters/watch.js
  34. +90 −0 node_modules/cradle/node_modules/vows/lib/vows/reporters/xunit.js
  35. +319 −0 node_modules/cradle/node_modules/vows/lib/vows/suite.js
  36. +14 −0 node_modules/cradle/node_modules/vows/package.json
  37. +119 −0 node_modules/cradle/node_modules/vows/test/assert-test.js
  38. +18 −0 node_modules/cradle/node_modules/vows/test/fixtures/isolate/failing.js
  39. +18 −0 node_modules/cradle/node_modules/vows/test/fixtures/isolate/log.js
  40. +17 −0 node_modules/cradle/node_modules/vows/test/fixtures/isolate/passing.js
  41. +18 −0 node_modules/cradle/node_modules/vows/test/fixtures/isolate/stderr.js
  42. +140 −0 node_modules/cradle/node_modules/vows/test/isolate-test.js
  43. +133 −0 node_modules/cradle/node_modules/vows/test/testInherit.js
  44. +374 −0 node_modules/cradle/node_modules/vows/test/vows-test.js
  45. +13 −0 node_modules/cradle/package.json
  46. +104 −0 node_modules/cradle/test/cache-test.js
  47. +612 −0 node_modules/cradle/test/cradle-test.js
  48. +76 −0 node_modules/cradle/test/response-test.js
  49. +49 −0 node_modules/cradle/test/scripts/prepare-db.js
View
20 node_modules/cradle/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2009 cloudhead
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
10 node_modules/cradle/Makefile
@@ -0,0 +1,10 @@
+default: test
+
+#
+# Run all tests
+#
+test:
+ node test/scripts/prepare-db.js
+ vows test/*-test.js
+
+.PHONY: test
View
260 node_modules/cradle/README.md
@@ -0,0 +1,260 @@
+cradle
+======
+
+A high-level, caching, CouchDB client for Node.js
+
+introduction
+------------
+
+Cradle is an asynchronous javascript client for [CouchDB](http://couchdb.apache.org).
+It is somewhat higher-level than most other CouchDB clients, requiring a little less knowledge of CouchDB's REST API.
+Cradle also has built-in write-through caching, giving you an extra level of speed, and making document _updates_ and _deletion_ easier.
+Cradle was built from the love of CouchDB and Node.js, and tries to make the most out of this wonderful marriage of technologies.
+
+philosophy
+----------
+
+The key concept here is the common ground shared by CouchDB and Node.js, that is, _javascript_. The other important aspect of this marriage is the asynchronous behaviors of both these technologies. Cradle tries to make use of these symmetries, whenever it can.
+Cradle's API, although closely knit with CouchDB's, isn't overly so. Whenever the API can be abstracted in a friendlier, simpler way, that's the route it takes. So even though a large part of the `Cradle <--> CouchDB` mappings are one to one, some Cradle functions, such as `save()`, can perform more than one operation, depending on how they are used.
+
+synopsis
+--------
+
+ var cradle = require('cradle');
+ var db = new(cradle.Connection)().database('starwars');
+
+ db.get('vader', function (err, doc) {
+ doc.name; // 'Darth Vader'
+ assert.equal(doc.force, 'dark');
+ });
+
+ db.save('skywalker', {
+ force: 'light',
+ name: 'Luke Skywalker'
+ }, function (err, res) {
+ if (err) {
+ // Handle error
+ } else {
+ // Handle success
+ }
+ });
+
+installation
+------------
+
+ $ npm install cradle
+
+API
+---
+
+Cradle's API builds right on top of Node's asynch API. Every asynch method takes a callback as its last argument. The return value is an `event.EventEmitter`, so listeners can also be optionally added.
+
+### Opening a connection ###
+
+ new(cradle.Connection)('http://living-room.couch', 5984, {
+ cache: true,
+ raw: false
+ });
+
+_Defaults to `127.0.0.1:5984`_
+
+Note that you can also use `cradle.setup` to set a global configuration:
+
+ cradle.setup({host: 'living-room.couch',
+ options: {cache: true, raw: false}});
+ var c = new(cradle.Connection),
+ cc = new(cradle.Connection)('173.45.66.92');
+
+### creating a database ###
+
+ var db = c.database('starwars');
+ db.create();
+
+> You can check if a database exists with the `exists()` method.
+
+### fetching a document _(GET)_ ###
+
+ db.get('vader', function (err, doc) {
+ sys.puts(doc);
+ });
+
+> If you want to get a specific revision for that document, you can pass it as the 2nd parameter to `get()`.
+
+Cradle is also able to fetch multiple documents if you have a list of ids, just pass an array to `get`:
+
+ db.get(['luke', 'vader'], function (err, doc) { ... });
+
+### Querying a view ###
+
+ db.view('characters/all', function (err, res) {
+ res.forEach(function (row) {
+ sys.puts(row.name + " is on the " +
+ row.force + " side of the force.");
+ });
+ });
+
+### creating/updating documents ###
+
+In general, document creation is done with the `save()` method, while updating is done with `merge()`.
+
+#### creating with an id _(PUT)_ ####
+
+ db.save('vader', {
+ name: 'darth', force: 'dark'
+ }, function (err, res) {
+ // Handle response
+ });
+
+#### creating without an id _(POST)_ ####
+
+ db.save({
+ force: 'dark', name: 'Darth'
+ }, function (err, res) {
+ // Handle response
+ });
+
+#### updating an existing document with the revision ####
+
+ db.save('luke', '1-94B6F82', {
+ force: 'dark', name: 'Luke'
+ }, function (err, res) {
+ // Handle response
+ });
+
+Note that when saving a document this way, CouchDB overwrites the existing document with the new one. If you want to update only certain fields of the document, you have to fetch it first (with `get`), make your changes, then resave the modified document with the above method.
+
+If you only want to update one or more attributes, and leave the others untouched, you can use the `merge()` method:
+
+ db.merge('luke', {jedi: true}, function (err, res) {
+ // Luke is now a jedi,
+ // but remains on the dark side of the force.
+ });
+
+Note that we didn't pass a `_rev`, this only works because we previously saved a full version of 'luke', and the `cache` option is enabled.
+
+#### bulk insertion ####
+
+If you want to insert more than one document at a time, for performance reasons, you can pass an array to `save()`:
+
+ db.save([
+ {name: 'Yoda'},
+ {name: 'Han Solo'},
+ {name: 'Leia'}
+ ], function (err, res) {
+ // Handle response
+ });
+
+#### creating views ####
+
+Here we create a design document named 'characters', with two views: 'all' and 'darkside'.
+
+ db.save('_design/characters', {
+ all: {
+ map: function (doc) {
+ if (doc.name) emit(doc.name, doc);
+ }
+ },
+ darkside: {
+ map: function (doc) {
+ if (doc.name && doc.force == 'dark') {
+ emit(null, doc);
+ }
+ }
+ }
+ });
+
+These views can later be queried with `db.view('characters/all')`, for example.
+
+Here we create a temporary view. WARNING: do not use this in production as it is extremely slow (use it to test views).
+ db.temporaryView({
+ map: function(doc) {
+ if (doc.color) emit(doc._id, doc);
+ }
+ }, function (err, res) {
+ if (err) console.log(err);
+ console.log(res);
+ });
+
+### removing documents _(DELETE)_ ###
+
+To remove a document, you call the `remove()` method, passing the latest document revision.
+
+ db.remove('luke', '1-94B6F82', function (err, res) {
+ // Handle response
+ });
+
+
+If `remove` is called without a revision, and the document was recently fetched from the database, it will attempt to use the cached document's revision, providing caching is enabled.
+
+Connecting with authentication and SSL
+--------------------------------------
+
+ var connection = new(cradle.Connection)('https://couch.io', 443, {
+ auth: { username: 'john', password: 'fha82l' }
+ });
+
+or
+
+ var connection = new(cradle.Connection)('couch.io', 443, {
+ secure: true,
+ auth: { username: 'john', password: 'fha82l' }
+ });
+
+Changes API
+-----------
+
+For a one-time `_changes` query, simply call `db.changes` with a callback:
+
+ db.changes(function (list) {
+ list.forEach(function (change) { console.log(change) });
+ });
+
+Or if you want to see changes since a specific sequence number:
+
+ db.changes({ since: 42 }, function (list) {
+ ...
+ });
+
+The callback will receive the list of changes as an *Array*. If you want to include
+the affected documents, simply pass `include_docs: true` in the options.
+
+### Streaming #
+
+You can also *stream* changes, by calling `db.changes` without the callback:
+
+ db.changes({ since: 42 }).on('response', function (res) {
+ res.on('data', function (change) {
+ console.log(change);
+ });
+ res.on('end', function () { ... });
+ });
+
+In this case, it returns an `EventEmitter`, which behaves very similarly to node's `Stream` API.
+
+
+Other API methods
+-----------------
+
+### CouchDB Server level ###
+
+ new(cradle.Connection)().*
+
+- `databases()`: Get list of databases
+- `config()`: Get server config
+- `info()`: Get server information
+- `stats()`: Statistics overview
+- `activeTasks()`: Get list of currently active tasks
+- `uuids(count)`: Get _count_ list of UUIDs
+- `replicate(options)`: Replicate a database.
+
+### database level ###
+
+ new(cradle.Connection)().database('starwars').*
+
+- `info()`: Database information
+- `all()`: Get all documents
+- `allBySeq()`: Get all documents by sequence
+- `compact()`: Compact database
+- `viewCleanup()`: Cleanup old view data
+- `replicate(target, options)`: Replicate this database to `target`.
+
View
628 node_modules/cradle/lib/cradle.js
@@ -0,0 +1,628 @@
+var path = require('path');
+
+require.paths.unshift(path.join(__dirname, 'cradle'));
+
+var sys = require("sys"),
+ http = require("http"),
+ https = require("https"),
+ events = require('events'),
+ fs = require("fs"),
+ url = require('url'),
+ buffer = require('buffer');
+
+var querystring = require('querystring');
+var Args = require('vargs').Constructor;
+
+var cradle = exports;
+
+cradle.extend = require('response').extend;
+cradle.Response = require('response').Response;
+cradle.Cache = require('cache').Cache;
+
+cradle.host = '127.0.0.1';
+cradle.port = 5984;
+cradle.auth = null;
+cradle.options = {
+ cache: true,
+ raw: false,
+ timeout: 0,
+ secure: false,
+ headers: {}
+};
+
+cradle.setup = function (settings) {
+ this.host = settings.host;
+ this.auth = settings.auth;
+ this.port = parseInt(settings.port);
+ cradle.merge(this.options, settings);
+
+ return this;
+};
+
+var protocolPattern = /^(https?):\/\//;
+
+cradle.Connection = function Connection(/* variable args */) {
+ var args = Array.prototype.slice.call(arguments),
+ host, port, remote, auth, options = {};
+
+ args.forEach(function (a) {
+ if (typeof(a) === 'number' || (typeof(a) === 'string' && /^\d{2,5}$/.test(a))) {
+ port = parseInt(a);
+ } else if (typeof(a) === 'object') {
+ options = a;
+ host = host || options.host;
+ port = port || options.port;
+ auth = options.auth;
+ } else {
+ host = a;
+ }
+ });
+
+ this.host = host || cradle.host;
+ this.port = port || cradle.port;
+ this.auth = auth || cradle.auth;
+ this.options = cradle.merge({}, cradle.options, options);
+
+ this.options.secure = this.options.secure || this.options.ssl;
+
+ if (protocolPattern.test(this.host)) {
+ this.protocol = this.host.match(protocolPattern)[1];
+ this.host = this.host.replace(protocolPattern, '');
+ }
+
+ if (this.protocol === 'https') this.options.secure = true;
+
+ if (this.auth && this.auth.user) { // Deprecation warning
+ console.log('Warning: "user" & "pass" parameters ignored. Use "username" & "password"');
+ }
+ if (this.options.ssl) { // Deprecation warning
+ console.log('Warning: "ssl" option is deprecated. Use "secure" instead.');
+ }
+
+ this.socket = (this.options.secure) ? https : http;
+};
+
+//
+// Connection.rawRequest()
+//
+// This is a base wrapper around connections to CouchDB. Given that it handles
+// *all* requests, including those for attachments, it knows nothing about
+// JSON serialization and does not presuppose it is sending or receiving JSON
+// content
+//
+cradle.Connection.prototype.rawRequest = function (method, path, options, data, headers) {
+ var promise = new(events.EventEmitter), request, that = this;
+
+ // HTTP Headers
+ headers = headers || {};
+
+ // Set HTTP Basic Auth
+ if (this.auth) {
+ headers['Authorization'] = "Basic " + new Buffer(this.auth.username + ':' + this.auth.password).toString('base64');
+ }
+
+ // Set client-wide headers
+ for (var h in this.options.headers) {
+ headers[h] = this.options.headers[h];
+ }
+
+ path = (path || '/').replace(/https?:\/\//, '').replace(/\/{2,}/g, '/');
+ if (path.charAt(0) !== '/') { path = '/' + path }
+
+ if (options) {
+ for (var k in options) {
+ if (typeof(options[k]) === 'boolean') {
+ options[k] = String(options[k]);
+ }
+ }
+ path += '?' + querystring.stringify(options);
+ }
+
+ request = this.socket.request({
+ host: this.host,
+ port: this.port,
+ method: method.toUpperCase(),
+ path: path,
+ headers: headers
+ });
+
+ if (data && data.on) { headers['Transfer-Encoding'] = 'chunked' }
+
+ headers['Connection'] = 'keep-alive';
+
+ request.on('response', function (res) {
+ promise.emit('response', res);
+ res.on('data', function (chunk) { promise.emit('data', chunk) });
+ res.on('end', function () { promise.emit('end') });
+ });
+
+ if (data) {
+ if (data.on) {
+ data.on('data', function (chunk) { request.write(chunk) });
+ data.on('end', function () { request.end() });
+ } else {
+ request.write(data, 'utf8');
+ request.end();
+ }
+ } else {
+ request.end();
+ }
+
+ return promise;
+}
+//
+// Connection.request()
+//
+// This is the entry point for all requests to CouchDB, at this point,
+// the database name has been embed in the url, by one of the wrappers.
+//
+cradle.Connection.prototype.request = function (method, path, /* [options], [data], [headers] */ callback) {
+ var request, that = this, args = Array.prototype.slice.call(arguments, 2);
+
+ if (typeof(callback = args.pop()) !== 'function') {
+ args.push(callback);
+ callback = function () {};
+ }
+
+ var options = args.shift() || {},
+ data = args.shift() || null,
+ headers = cradle.merge({ host: this.host }, args.shift() || {});
+
+ //
+ // Handle POST/PUT data. We also convert functions to strings,
+ // so they can be used in _design documents.
+ //
+ if (data) {
+ data = JSON.stringify(data, function (k, val) {
+ if (typeof(val) === 'function') {
+ return val.toString();
+ } else { return val }
+ });
+ headers["Content-Length"] = Buffer.byteLength(data);
+ headers["Content-Type"] = "application/json";
+ }
+
+ request = this.rawRequest(method, path, options, data, headers);
+
+ //
+ // Initialize the request, send the body, and finally,
+ // dispatch the request.
+ //
+ request.on('response', function (res) {
+ var body = [];
+
+ res.setEncoding('utf8');
+ res.on('data', function (chunk) {
+ chunk && body.push(chunk);
+ }).on('end', function () {
+ var json, response;
+
+ if (method === 'HEAD') {
+ callback(null, res.headers, res.statusCode);
+ } else {
+ try { json = JSON.parse(body.join('')) }
+ catch (e) { return callback(e) }
+
+
+ if (json.error) {
+ cradle.extend(json, { headers: res.headers });
+ json.headers.status = res.statusCode;
+ callback(json);
+ } else {
+ // If the `raw` option was set, we return the parsed
+ // body as-is. If not, we wrap it in a `Response` object.
+ callback(null, that.options.raw ? json : new(cradle.Response)(json, res));
+ }
+ }
+ });
+ });
+};
+
+//
+// The database object
+//
+// We return an object with database functions,
+// closing around the `name` argument.
+//
+cradle.Connection.prototype.database = function (name) {
+ var that = this, connection = this;
+
+ return {
+ name: name,
+ //
+ // The database document cache.
+ //
+ cache: new(cradle.Cache)(that.options),
+
+ // A wrapper around `Connection.request`,
+ // which prepends the database name.
+ query: function (method, path /* [options], [data], [headers], [callback] */) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ that.request.apply(that, [method, [name, path].join('/')].concat(args));
+ },
+ exists: function (callback) {
+ this.query('HEAD', '/', function (err, res, status) {
+ if (err) {
+ callback(err);
+ } else {
+ if (status === 404) {
+ callback(null, false);
+ } else {
+ callback(null, true);
+ }
+ }
+ });
+ },
+
+ // Fetch either a single document from the database, or cache,
+ // or multiple documents from the database.
+ // If it's a single doc from the db, attempt to save it to the cache.
+ get: function (id, rev) {
+ var that = this, options = null,
+ args = new(Args)(arguments);
+
+ if (Array.isArray(id)) { // Bulk GET
+ this.query('POST', '/_all_docs', { include_docs: true }, { keys: id },
+ function (err, res) { args.callback(err, res) });
+ } else {
+ if (rev && args.length === 2) {
+ 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));
+ }
+ this.query('GET', id, options, function (err, res) {
+ if (! err) that.cache.save(res.id, res.json);
+ args.callback(err, res);
+ });
+ }
+ },
+
+ save: function (/* [id], [rev], doc | [doc, ...] */) {
+ var args = new(Args)(arguments),
+ array = args.all.slice(0), doc, id, rev;
+
+ if (Array.isArray(args.first)) {
+ doc = args.first;
+ } else {
+ doc = array.pop(),
+ id = array.shift(),
+ rev = array.shift();
+ }
+ this._save(id, rev, doc, args.callback);
+ },
+ _save: function (id, rev, doc, callback) {
+ var options = connection.options;
+ var document = {}, that = this;
+
+ // Bulk Insert
+ if (Array.isArray(doc)) {
+ document.docs = doc;
+ if (options.allOrNothing) { document.all_or_nothing = true }
+ this.query('POST', '/_bulk_docs', {}, document, callback);
+ } else {
+ // PUT a single document, with an id (Create or Update)
+ if (id) {
+ // Design document
+ if (/^_design\/(\w|%)+$/.test(id) && !('views' in doc)) {
+ document.language = "javascript";
+ document.views = doc;
+ } else {
+ document = doc;
+ }
+ // Try to set the '_rev' attribute of the document.
+ // If it wasn't passed, attempt to retrieve it from the cache.
+ rev && (document._rev = rev);
+
+ if (document._rev) {
+ this.put(id, document, callback);
+ } else if (this.cache.has(id)) {
+ document._rev = this.cache.get(id)._rev;
+ this.put(id, document, callback);
+ } else {
+ // Attempt to create a new document. If it fails,
+ // because an existing document with that _id exists (409),
+ // perform a HEAD, to get the _rev, and try to re-save.
+ this.put(id, document, function (e, res) {
+ if (e && e.headers.status === 409) { // Conflict
+ that.head(id, function (e, headers) {
+ document._rev = headers['etag'].slice(1, -1);
+ that.put(id, document, callback);
+ });
+ } else { callback(e, res) }
+ });
+ }
+ // POST a single document, without an id (Create)
+ } else {
+ this.post(doc, callback);
+ }
+ }
+ },
+
+ merge: function (/* [id], doc */) {
+ var args = Array.prototype.slice.call(arguments),
+ callback = args.pop(),
+ doc = args.pop(),
+ id = args.pop() || doc._id;
+
+ this._merge(id, doc, callback);
+ },
+ _merge: function (id, doc, callback) {
+ var that = this;
+ this.get(id, function (e, res) {
+ if (e) { return callback(e) }
+ doc = cradle.merge({}, res.json || res, doc);
+ that.save(id, res._rev, doc, callback);
+ });
+ },
+
+ //
+ // PUT a document, and write through cache
+ //
+ put: function (id, doc, callback) {
+ var cache = this.cache;
+ if (typeof(id) !== 'string') { throw new(TypeError)("id must be a string") }
+ this.query('PUT', id, null, doc, function (e, res) {
+ if (! e) { cache.save(id, cradle.merge({}, doc, { _rev: res.rev })) }
+ callback && callback(e, res);
+ });
+ },
+
+ //
+ // POST a document, and write through cache
+ //
+ post: function (doc, callback) {
+ var cache = this.cache;
+ this.query('POST', '/', null, doc, function (e, res) {
+ if (! e) { cache.save(res.id, cradle.merge({}, doc, { _rev: res.rev })) }
+ callback && callback(e, res);
+ });
+ },
+
+ //
+ // Perform a HEAD request
+ //
+ head: function (id, callback) {
+ this.query('HEAD', id, null, callback);
+ },
+
+ insert: function () {
+ throw new(Error)("`insert` is deprecated, use `save` instead");
+ },
+
+ replicate: function (target, options, callback) {
+ if (typeof(options) === 'function') { callback = options, options = {} }
+ that.replicate(cradle.merge({ source: name, target: target }, options), callback);
+ },
+
+ // Destroys a database with 'DELETE'
+ // we raise an exception if arguments were supplied,
+ // as we don't want users to confuse this function with `remove`.
+ destroy: function (callback) {
+ if (arguments.length > 1) {
+ throw new(Error)("destroy() doesn't take any additional arguments");
+ } else {
+ this.query('DELETE', '/', callback);
+ }
+ },
+
+ // Delete a document
+ // if the _rev wasn't supplied, we attempt to retrieve it from the
+ // cache. If the deletion was successful, we purge the cache.
+ remove: function (id, rev) {
+ var that = this, doc, args = new(Args)(arguments);
+
+ if (typeof(rev) !== 'string') {
+ if (doc = this.cache.get(id)) { rev = doc._rev }
+ else { throw new(Error)("rev needs to be supplied") }
+ }
+ this.query('DELETE', id, {rev: rev}, function (err, res) {
+ if (! err) { that.cache.purge(id) }
+ args.callback(err, res);
+ });
+ },
+ create: function (callback) {
+ this.query('PUT', '/', callback);
+ },
+ info: function (callback) {
+ this.query('GET', '/', callback);
+ },
+ all: function (options, callback) {
+ if (arguments.length === 1) { callback = options, options = {} }
+ this.query('GET', '/_all_docs', options, callback);
+ },
+ compact: function (design) {
+ var headers = {};
+ headers['Content-Type'] = "application/json";
+ this.query('POST', '/_compact' + (typeof(design) === 'string' ? '/' + design : ''),
+ {}, {}, headers, Args.last(arguments));
+ },
+ viewCleanup: function (callback) {
+ this.query('POST', '/_view_cleanup', callback);
+ },
+ allBySeq: function (options) {
+ options = typeof(options) === 'object' ? options : {};
+ this.query('GET', '/_all_docs_by_seq', options, Args.last(arguments));
+ },
+
+ // Query a view, passing any options to the query string.
+ // Some query string parameters' values have to be JSON-encoded.
+ view: function (path, options) {
+ var args = new(Args)(arguments);
+
+ path = path.split('/');
+ path = ['_design', path[0], '_view', path[1]].join('/');
+
+ if (typeof(options) === 'object') {
+ ['key', 'startkey', 'endkey'].forEach(function (k) {
+ if (k in options) { options[k] = JSON.stringify(options[k]) }
+ });
+ }
+
+ if (options && options.keys) {
+ this.query('POST', path, {}, options, args.callback);
+ } else {
+ this.query('GET', path, options, args.callback);
+ }
+ },
+
+ // Query a list, passing any options to the query string.
+ // Some query string parameters' values have to be JSON-encoded.
+ list: function (path, options) {
+ var args = new(Args)(arguments);
+ path = path.split('/');
+
+ if (typeof(options) === 'object') {
+ ['key', 'startkey', 'endkey'].forEach(function (k) {
+ if (k in options) { options[k] = JSON.stringify(options[k]) }
+ });
+ }
+ this.query('GET', ['_design', path[0], '_list', path[1], path[2]].join('/'), options, args.callback);
+ },
+
+ update: function(path, id, options) {
+ var args = new(Args)(arguments);
+ path = path.split('/');
+
+ if (id) {
+ this.query('PUT', ['_design', path[0], '_update', path[1], id].join('/'), options, args.callback);
+ } else {
+ this.query('POST', ['_design', path[0], '_update', path[1]].join('/'), options, args.callback);
+ }
+ },
+
+ push: function (doc) {},
+
+ changes: function (options, callback) {
+ var promise = new(events.EventEmitter);
+
+ if (typeof(options) === 'function') { callback = options, options = {}; }
+
+ if (callback) {
+ this.query('GET', '_changes', options, callback);
+ } else {
+ options = options || {};
+ options.feed = options.feed || 'continuous';
+ options.heartbeat = options.heartbeat || 1000;
+
+ that.rawRequest('GET', [name, '_changes'].join('/'), options).on('response', function (res) {
+ var response = new(events.EventEmitter), buffer = [];
+ res.setEncoding('utf8');
+
+ response.statusCode = res.statusCode;
+ response.headers = res.headers;
+
+ promise.emit('response', response);
+
+ res.on('data', function (chunk) {
+ if (chunk.trim()) {
+ buffer.push(chunk);
+
+ if (chunk.indexOf('\n') !== -1) {
+ buffer.length && response.emit('data', JSON.parse(buffer.join('')));
+ buffer = [];
+ }
+ }
+ }).on('end', function () {
+ response.emit('end');
+ });
+ });
+ return promise;
+ }
+ },
+
+ saveAttachment: function (/* id, [rev], attachmentName, contentType, dataOrStream */) {
+ var doc, pathname, headers = {}, response, body = [], resHeaders, error, db = this;
+
+ var args = new(Args)(arguments), params = args.all;
+
+ if (typeof(args.first) === 'object') { throw new(TypeError)("first argument must be a document id") }
+
+ var id = params.shift(),
+ dataOrStream = params.pop(),
+ contentType = params.pop(),
+ attachmentName = params.pop(),
+ rev = params.pop();
+
+ if (!rev && db.cache.has(id)) {
+ doc = { rev: db.cache.get(id)._rev };
+ } else if (rev) {
+ doc = { rev: rev };
+ } else {
+ doc = {};
+ }
+
+ pathname = '/' + [name, id, attachmentName].join('/');
+ headers['Content-Type'] = contentType;
+
+ that.rawRequest('PUT', pathname, doc, dataOrStream, headers)
+ .on('response', function (res) { resHeaders = { status: res.statusCode } })
+ .on('data', function (chunk) { body.push(chunk) })
+ .on('end', function () {
+ response = JSON.parse(body.join(''));
+ response.headers = resHeaders;
+
+ if (response.headers.status == 201) {
+ if (db.cache.has(id)) {
+ cached = db.cache.store[id].document;
+ cached._rev = response.rev;
+ cached._attachments = cached._attachments || {};
+ cached._attachments[attachmentName] = { stub: true };
+ }
+ args.callback(null, response);
+ } else {
+ args.callback(response);
+ }
+ });
+ },
+
+ getAttachment: function(docId, attachmentName) {
+ var pathname, req;
+ pathname = '/' + [name, docId, attachmentName].join('/');
+ return that.rawRequest('GET', pathname);
+ },
+
+ temporaryView: function (doc, callback) {
+ this.query('POST', '_temp_view', null, doc, callback);
+ }
+ }
+
+};
+
+//
+// Wrapper functions for the server API
+//
+cradle.Connection.prototype.databases = function (c) {
+ this.request('GET', '/_all_dbs', c);
+};
+cradle.Connection.prototype.config = function (c) {
+ this.request('GET', '/_config', c);
+};
+cradle.Connection.prototype.info = function (c) {
+ this.request('GET', '/', c);
+};
+cradle.Connection.prototype.stats = function (c) {
+ this.request('GET', '/_stats', c);
+};
+cradle.Connection.prototype.activeTasks = function (c) {
+ this.request('GET', '/_active_tasks', c);
+};
+cradle.Connection.prototype.uuids = function (count, callback) {
+ if (typeof(count) === 'function') { callback = count, count = null }
+ this.request('GET', '/_uuids', count ? {count: count} : {}, callback);
+};
+cradle.Connection.prototype.replicate = function (options, callback) {
+ this.request('POST', '/_replicate', null, options, callback);
+};
+
+cradle.merge = function (target) {
+ var objs = Array.prototype.slice.call(arguments, 1);
+ objs.forEach(function(o) {
+ Object.keys(o).forEach(function (attr) {
+ if (! o.__lookupGetter__(attr)) {
+ target[attr] = o[attr];
+ }
+ });
+ });
+ return target;
+}
View
102 node_modules/cradle/lib/cradle/cache.js
@@ -0,0 +1,102 @@
+var Response = require('./response').Response;
+//
+// Each database object has its own cache store.
+// The cache.* methods are all wrappers around
+// `cache.query`, which transparently checks if
+// caching is enabled, before performing any action.
+//
+this.Cache = function (options) {
+ var that = this;
+
+ this.store = {};
+ this.options = options;
+ this.size = options.cacheSize || 0;
+ this.keys = 0;
+};
+
+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) {
+ var entry;
+
+ if (id in this.store) {
+ entry = this.store[id];
+ entry.atime = Date.now();
+
+ if (this.options.raw) {
+ return entry.document;
+ } else {
+ // If the document is already wrapped in a `Response`,
+ // just return it. Else, wrap it first. We clone the documents
+ // before returning them, to protect them from modification.
+ if (entry.document.toJSON) {
+ return clone(entry.document);
+ } else {
+ return new(Response)(clone(entry.document));
+ }
+ }
+ }
+ },
+ _has: function (id) {
+ return id in this.store;
+ },
+ _save: function (id, doc) {
+ if (! this._has(id)) {
+ this.keys ++;
+ this.prune();
+ }
+
+ return this.store[id] = {
+ atime: Date.now(),
+ document: doc
+ };
+ },
+ _purge: function (id) {
+ if (id) {
+ delete(this.store[id]);
+ this.keys --;
+ } else {
+ this.store = {};
+ }
+ },
+ query: function (op, id, doc) {
+ if (this.options.cache) {
+ return this['_' + op](id, doc);
+ } else {
+ return false;
+ }
+ },
+ prune: function () {
+ var that = this;
+ if (this.size && this.keys > this.size) {
+ process.nextTick(function () {
+ var store = that.store,
+ keys = Object.keys(store),
+ pruned = Math.ceil(that.size / 8);
+
+ keys.sort(function (a, b) {
+ return store[a].atime > store[b].atime ? 1 : -1;
+ });
+
+ for (var i = 0; i < pruned; i++) {
+ delete(store[keys[i]]);
+ }
+ that.keys -= pruned;
+ });
+ }
+ }
+};
+
+function clone(obj) {
+ return Object.keys(obj).reduce(function (clone, k) {
+ if (! obj.__lookupGetter__(k)) {
+ clone[k] = obj[k];
+ }
+ return clone;
+ }, {});
+}
View
123 node_modules/cradle/lib/cradle/response.js
@@ -0,0 +1,123 @@
+//
+// HTTP response wrapper
+//
+// It allows us to call array-like methods on documents
+// with a 'row' attribute.
+//
+this.Response = function Response(json, response) {
+ var obj, headers;
+
+ // If there's rows, this is the result
+ // of a view function.
+ // We want to return this as an Array.
+ if (json.rows) {
+ obj = json.rows.slice(0);
+ obj.__proto__ = new(Array);
+ Object.keys(json).forEach(function (k) {
+ Object.defineProperty(obj.__proto__, k, {
+ value: json[k],
+ enumerable: false
+ });
+ });
+ } else if (json.results) {
+ obj = json.results.slice(0);
+ obj.__proto__ = new(Array);
+ obj.last_seq = json.last_seq;
+ } else if (json.uuids) {
+ obj = json.uuids;
+ obj.__proto__ = new(Array);
+ } else if (Array.isArray(json)) {
+ obj = json.slice(0);
+ obj.__proto__ = new(Array);
+ } else {
+ obj = {};
+ obj.__proto__ = new(Object);
+ Object.keys(json).forEach(function (k) {
+ obj[k] = json[k];
+ });
+ }
+
+ // If the response was originally a document,
+ // give access to it via the 'json' getter.
+ if (!Array.isArray(json) && !obj.json) {
+ Object.defineProperty(obj, 'json', {
+ value: json,
+ enumerable: false
+ });
+ }
+
+ if (response) {
+ headers = { status: response.statusCode };
+ Object.keys(response.headers).forEach(function (k) {
+ headers[k] = response.headers[k];
+ });
+
+ // Set the 'headers' special field, with the response's status code.
+ exports.extend(obj, 'headers' in obj ? { _headers: headers }
+ : { headers: headers });
+ }
+
+ // Alias '_rev' and '_id'
+ if (obj.id && obj.rev) {
+ exports.extend(obj, { _id: obj.id, _rev: obj.rev });
+ } else if (obj._id && obj._rev) {
+ exports.extend(obj, { id: obj._id, rev: obj._rev });
+ }
+
+ if (Array.isArray(obj) && json.rows) {
+ exports.extend(obj, exports.collectionPrototype);
+ }
+ exports.extend(obj, exports.basePrototype);
+
+ // Set the constructor to be this function
+ Object.defineProperty(obj, 'constructor', {
+ value: arguments.callee
+ });
+
+ return obj;
+};
+
+this.basePrototype = {
+ toJSON: function () {
+ return this;
+ },
+ toString: function () {
+ return JSON.stringify(this);
+ }
+};
+
+this.collectionPrototype = {
+ forEach: function (f) {
+ for (var i = 0, value; i < this.length; i++) {
+ value = this[i].doc || this[i].json || this[i].value || this[i];
+ if (f.length === 1) {
+ f.call(this[i], value);
+ } else {
+ f.call(this[i], this[i].key, value, this[i].id);
+ }
+ }
+ },
+ map: function (f) {
+ var ary = [];
+ if (f.length === 1) {
+ this.forEach(function (a) { ary.push(f.call(this, a)) });
+ } else {
+ this.forEach(function () { ary.push(f.apply(this, arguments)) });
+ }
+ return ary;
+ },
+ toArray: function () {
+ return this.map(function (k, v) { return v });
+ }
+};
+
+this.extend = function (obj, properties) {
+ var descriptor = Object.keys(properties).reduce(function (hash, k) {
+ hash[k] = {
+ value: properties[k],
+ enumerable: false
+ };
+ return hash;
+ }, {});
+ return Object.defineProperties(obj, descriptor);
+};
View
1  node_modules/cradle/node_modules/.bin/vows
View
20 node_modules/cradle/node_modules/vargs/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2009-2010 Alexis Sellier
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
34 node_modules/cradle/node_modules/vargs/README.md
@@ -0,0 +1,34 @@
+
+vargs
+=====
+
+> variable argument handling for functions taking a callback
+
+rationale
+----------
+
+JavaScript has very poor argument handling. *vargs* tries to make it simpler.
+
+synopsis
+--------
+
+ var Args = require("vargs").Constructor;
+
+ function (/* [arg1, arg2, ...][,callback] */) {
+ var args = new(Args)(arguments);
+
+ args.first; // first argument
+ args.last; // last argument before callback
+ args.callback; // callback argument, or an empty function
+ args.all; // all arguments except callback
+ args.length; // number of arguments, not including callback
+
+ args.callbackGiven(); // returns true or false
+ args.at(-1); // last argument, including callback
+ args.array; // all arguments, including callback
+ }
+
+example
+-------
+
+For a real-world example of *vargs*, check <http://github.com/cloudhead/cradle>
View
70 node_modules/cradle/node_modules/vargs/lib/vargs.js
@@ -0,0 +1,70 @@
+//
+// vargs.js
+//
+// variable argument handling for functions taking a callback
+//
+// usage:
+//
+// var Args = new("vargs").Constructor;
+//
+// function (/* [arg1, arg2, ...][,callback] */) {
+// var args = new(Args)(arguments);
+//
+// args.first; // first argument
+// args.last; // last argument before callback
+// args.callback; // callback argument, or an empty function
+// args.all; // all arguments except callback
+// args.length; // number of arguments, not including callback
+//
+// args.callbackGiven() // returns true or false
+// args.at(-1) // last argument, including callback
+// args.array // all arguments, including callback
+// }
+//
+exports.Constructor = function Vargs(arguments) {
+ this.array = Array.prototype.slice.call(arguments);
+ this.__defineGetter__('length', function () {
+ if (this.callbackGiven()) {
+ return this.array.length - 1;
+ } else {
+ return this.array.length;
+ }
+ });
+ this.__defineGetter__('all', function () {
+ if (this.callbackGiven()) {
+ return this.array.slice(0, -1);
+ } else {
+ return this.array;
+ }
+ });
+ this.__defineGetter__('last', function () {
+ if (typeof(this.at(-1)) === 'function') {
+ return this.at(-2);
+ } else {
+ return this.at(-1);
+ }
+ });
+ this.__defineGetter__('first', function () {
+ return this.array[0];
+ });
+ this.callback = this.callbackGiven() ? this.at(-1)
+ : function () {};
+};
+
+exports.Constructor.prototype = {
+ callbackGiven: function () {
+ return typeof(this.at(-1)) === 'function';
+ },
+ at: function (n) {
+ if (n < 0) {
+ return this.array[this.array.length + n];
+ } else {
+ return this.array[n];
+ }
+ }
+};
+
+exports.Constructor.last = function (args) {
+ return args[args.length - 1];
+};
+
View
12 node_modules/cradle/node_modules/vargs/package.json
@@ -0,0 +1,12 @@
+{
+ "name" : "vargs",
+ "description" : "practical variable argument handling",
+ "url" : "http://github.com/cloudhead/vargs",
+ "keywords" : ["argument", "arguments"],
+ "author" : "Alexis Sellier <self@cloudhead.net>",
+ "contributors" : [],
+ "version" : "0.1.0",
+ "main" : "./lib/vargs",
+ "directories" : { "lib": "./lib" },
+ "engines" : { "node": ">=0.1.93" }
+}
View
1  node_modules/cradle/node_modules/vows/.gitignore
@@ -0,0 +1 @@
+node_modules
View
20 node_modules/cradle/node_modules/vows/LICENSE
@@ -0,0 +1,20 @@
+Copyright (c) 2009 cloudhead
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
View
7 node_modules/cradle/node_modules/vows/Makefile
@@ -0,0 +1,7 @@
+#
+# Run all tests
+#
+test:
+ @@bin/vows test/*
+
+.PHONY: test install
View
39 node_modules/cradle/node_modules/vows/README.md
@@ -0,0 +1,39 @@
+Vows
+====
+
+> Asynchronous BDD & continuous integration for node.js
+
+#### <http://vowsjs.org> #
+
+introduction
+------------
+There are two reasons why we might want asynchronous testing. The first, and obvious reason is that node.js is asynchronous, and therefore our tests need to be. The second reason is to make test suites which target I/O libraries run much faster.
+
+_Vows_ is an experiment in making this possible, while adding a minimum of overhead.
+
+synopsis
+--------
+
+ var vows = require('vows'),
+ assert = require('assert');
+
+ vows.describe('Deep Thought').addBatch({
+ 'An instance of DeepThought': {
+ topic: new DeepThought,
+
+ 'should know the answer to the ultimate question of life': function (deepThought) {
+ assert.equal (deepThought.question('what is the answer to the universe?'), 42);
+ }
+ }
+ });
+
+installation
+------------
+
+ $ npm install vows
+
+documentation
+-------------
+
+Head over to <http://vowsjs.org>
+
View
506 node_modules/cradle/node_modules/vows/bin/vows
@@ -0,0 +1,506 @@
+#!/usr/bin/env node
+
+var path = require('path'),
+ fs = require('fs'),
+ util = require('util'),
+ events = require('events');
+
+//
+// Attempt to load Coffee-Script. If it's not available, continue on our
+// merry way, if it is available, set it up so we can include `*.coffee`
+// scripts and start searching for them.
+//
+var fileExt, specFileExt;
+
+try {
+ var coffee = require('coffee-script');
+ if (require.extensions) {
+ require.extensions['.coffee'] = function (module, filename) {
+ var content = coffee.compile(fs.readFileSync(filename, 'utf8'));
+ return module._compile(content, filename);
+ };
+ } else {
+ require.registerExtension('.coffee', function (content) { return coffee.compile(content) });
+ }
+ fileExt = /\.(js|coffee)$/;
+ specFileExt = /[.-](test|spec)\.(js|coffee)$/;
+} catch (_) {
+ fileExt = /\.js$/;
+ specFileExt = /[.-](test|spec)\.js$/;
+}
+
+var inspect = require('eyes').inspector({
+ stream: null,
+ styles: { string: 'grey', regexp: 'grey' }
+});
+
+var vows = require('../lib/vows');
+var cutils = require('../lib/vows/console');
+var stylize = require('../lib/vows/console').stylize;
+var _reporter = require('../lib/vows/reporters/dot-matrix'), reporter = {
+ name: _reporter.name
+};
+var _coverage;
+
+var help = [
+ "usage: vows [FILE, ...] [options]",
+ "",
+ "options:",
+ " -v, --verbose Enable verbose output",
+ " -w, --watch Watch mode",
+ " -s, --silent Don't report",
+ " -i, --isolate Run each test in it's own vows process",
+ " -m PATTERN Only run tests matching the PATTERN string",
+ " -r PATTERN Only run tests matching the PATTERN regexp",
+ " --json Use JSON reporter",
+ " --spec Use Spec reporter",
+ " --dot-matrix Use Dot-Matrix reporter",
+ " --xunit Use xUnit reporter",
+ " --cover-plain Print plain coverage map if detected",
+ " --cover-html Write coverage map to \"coverage.html\"",
+ //" --no-color Don't use terminal colors",
+ " --version Show version",
+ " -h, --help You're staring at it"
+].join('\n');
+
+var options = {
+ reporter: reporter,
+ matcher: /.*/,
+ watch: false,
+ coverage: false,
+ isolate: false
+};
+
+var files = [];
+
+// Get rid of process runner
+// ('node' in most cases)
+var arg, args = [], argv = process.argv.slice(2);
+
+// Current directory index,
+// and path of test folder.
+var root, testFolder;
+
+//
+// Parse command-line parameters
+//
+while (arg = argv.shift()) {
+ if (arg === __filename) { continue }
+
+ if (arg[0] !== '-') {
+ args.push(arg);
+ } else {
+ arg = arg.match(/^--?(.+)/)[1];
+
+ if (arg[0] === 'r') {
+ options.matcher = new(RegExp)(argv.shift());
+ } else if (arg[0] === 'm') {
+ options.matcher = (function (str) { // Create an escaped RegExp
+ var specials = '. * + ? | ( ) [ ] { } \\ ^ ? ! = : $'.split(' ').join('|\\'),
+ regex = new(RegExp)('(\\' + specials + ')', 'g');
+ return new(RegExp)(str.replace(regex, '\\$1'));
+ })(argv.shift());
+ } else if (arg in options) {
+ options[arg] = true;
+ } else {
+ switch (arg) {
+ case 'json':
+ _reporter = require('../lib/vows/reporters/json');
+ break;
+ case 'spec':
+ _reporter = require('../lib/vows/reporters/spec');
+ break;
+ case 'dot-matrix':
+ _reporter = require('../lib/vows/reporters/dot-matrix');
+ break;
+ case 'silent':
+ case 's':
+ _reporter = require('../lib/vows/reporters/silent');
+ break;
+ case 'xunit':
+ _reporter = require('../lib/vows/reporters/xunit');
+ break;
+ case 'cover-plain':
+ options.coverage = true;
+ _coverage = require('../lib/vows/coverage/report-plain');
+ break;
+ case 'cover-html':
+ options.coverage = true;
+ _coverage = require('../lib/vows/coverage/report-html');
+ break;
+ case 'verbose':
+ case 'v':
+ options.verbose = true;
+ break;
+ case 'watch':
+ case 'w':
+ options.watch = true;
+ break;
+ case 'supress-stdout':
+ options.supressStdout = true;
+ break;
+ case 'isolate':
+ case 'i':
+ options.isolate = true;
+ break;
+ case 'no-color':
+ options.nocolor = true;
+ break;
+ case 'no-error':
+ options.error = false;
+ break;
+ case 'version':
+ console.log('vows ' + vows.version);
+ process.exit(0);
+ case 'help':
+ case 'h':
+ console.log(help);
+ process.exit(0);
+ break;
+ }
+ }
+ }
+}
+
+if (options.supressStdout) {
+ _reporter.setStream && _reporter.setStream(process.stdout);
+ process.stdout = fs.createWriteStream('/dev/null');
+}
+
+if (options.watch) {
+ options.reporter = reporter = require('../lib/vows/reporters/watch');
+}
+
+msg('bin', 'argv', args);
+msg('bin', 'options', { reporter: options.reporter.name, matcher: options.matcher });
+
+if (args.length === 0 || options.watch) {
+ msg('bin', 'discovering', 'folder structure');
+ root = fs.readdirSync('.');
+
+ if (root.indexOf('test') !== -1) {
+ testFolder = 'test';
+ } else if (root.indexOf('spec') !== -1) {
+ testFolder = 'spec';
+ } else {
+ abort("runner", "couldn't find test folder");
+ }
+ msg('bin', 'discovered', "./" + testFolder);
+
+ if (args.length === 0) {
+ args = paths(testFolder).filter(function (f) {
+ return new(RegExp)('-' + testFolder + '.(js|coffee)$').test(f);
+ });
+
+ if (options.watch) {
+ args = args.concat(paths('lib'),
+ paths('src'));
+ }
+ }
+}
+
+if (! options.watch) {
+ reporter.report = function (data, filename) {
+ switch (data[0]) {
+ case 'subject':
+ case 'vow':
+ case 'context':
+ case 'error':
+ _reporter.report(data, filename);
+ break;
+ case 'end':
+ (options.verbose || _reporter.name === 'json') &&
+ _reporter.report(data);
+ break;
+ case 'finish':
+ options.verbose ?
+ _reporter.print('\n')
+ :
+ _reporter.print(' ');
+ break;
+ }
+ };
+ reporter.reset = function () { _reporter.reset && _reporter.reset() };
+ reporter.print = _reporter.print;
+
+ files = args.map(function (a) {
+ return (!a.match(/^\//))
+ ? path.join(process.cwd(), a.replace(fileExt, ''))
+ : a.replace(fileExt, '');
+ });
+
+ runSuites(importSuites(files), function (results) {
+ var status = results.errored ? 2 : (results.broken ? 1 : 0);
+
+ !options.verbose && _reporter.print('\n');
+ msg('runner', 'finish');
+ _reporter.report(['finish', results], {
+ write: function (str) {
+ util.print(str.replace(/^\n\n/, '\n'));
+ }
+ });
+ if (options.coverage === true && _$jscoverage !== undefined) {
+ _coverage.report(_$jscoverage);
+ }
+ if (process.stdout.write('')) { // Check if stdout is drained
+ process.exit(status);
+ } else {
+ process.stdout.on('drain', function () {
+ process.exit(status);
+ });
+ }
+ });
+} else {
+ //
+ // Watch mode
+ //
+ (function () {
+ var pendulum = [
+ '. ', '.. ', '... ', ' ...',
+ ' ..', ' .', ' .', ' ..',
+ '... ', '.. ', '. '
+ ];
+ var strobe = ['.', ' '];
+ var status,
+ cue,
+ current = 0,
+ running = 0,
+ lastRun,
+ colors = ['32m', '33m', '31m'],
+ timer = setInterval(tick, 100);
+
+ process.on('uncaughtException', cleanup);
+ process.on('exit', cleanup);
+ process.on('SIGINT', function () {
+ process.exit(0);
+ });
+ process.on('SIGQUIT', function () {
+ changed();
+ });
+
+ cursorHide();
+
+ // Run every 100ms
+ function tick() {
+ if (running && (cue !== strobe)) {
+ cue = strobe, current = 0;
+ } else if (!running && (cue !== pendulum)) {
+ cue = pendulum, current = 0;
+ }
+
+ eraseLine();
+ lastRun && !running && esc(colors[status.errored ? 2 : (status.broken ? 1 : 0)]);
+ print(cue[current]);
+
+ if (current == cue.length - 1) { current = -1 }
+
+ current ++;
+ esc('39m');
+ cursorRestore();
+ }
+
+ //
+ // Utility functions
+ //
+ function print(str) { util.print(str) }
+ function esc(str) { print("\x1b[" + str) }
+ function eraseLine() { esc("0K") }
+ function cursorRestore() { esc("0G") }
+ function cursorHide() { esc("?25l") }
+ function cursorShow() { esc("?25h") }
+ function cleanup() { eraseLine(), cursorShow(), clearInterval(timer), print('\n') }
+
+ //
+ // Called when a file has been modified.
+ // Run the matching tests and change the status.
+ //
+ function changed(file) {
+ status = { honored: 0, broken: 0, errored: 0, pending: 0 };
+
+ msg('watcher', 'detected change in', file);
+
+ file = (specFileExt.test(file) ? path.join(testFolder, file)
+ : path.join(testFolder, file + '-' + testFolder));
+
+ try {
+ fs.statSync(file);
+ } catch (e) {
+ msg('watcher', 'no equivalence found, running all tests.');
+ file = null;
+ }
+
+ var files = (specFileExt.test(file) ? [file] : paths(testFolder)).map(function (p) {
+ return path.join(process.cwd(), p);
+ }).map(function (p) {
+ var cache = require.main.moduleCache || require.cache;
+ if (cache[p]) { delete(cache[p]) }
+ return p;
+ }).map(function (p) {
+ return p.replace(fileExt, '');
+ });
+
+ running ++;
+
+ runSuites(importSuites(files), function (results) {
+ delete(results.time);
+ print(cutils.result(results).join('') + '\n\n');
+ lastRun = new(Date);
+ status = results;
+ running --;
+ });
+ }
+
+ msg('watcher', 'watching', args);
+
+ //
+ // Watch all relevant files,
+ // and call `changed()` on change.
+ //
+ args.forEach(function (p) {
+ fs.watchFile(p, function (current, previous) {
+ if (new(Date)(current.mtime).valueOf() ===
+ new(Date)(previous.mtime).valueOf()) { return }
+ else {
+ changed(p);
+ }
+ });
+ });
+ })();
+}
+
+function runSuites(suites, callback) {
+ var results = {
+ honored: 0,
+ broken: 0,
+ errored: 0,
+ pending: 0,
+ total: 0,
+ time: 0
+ };
+ reporter.reset();
+
+ (function run(suites, callback) {
+ var suite = suites.shift();
+ if (suite) {
+ msg('runner', "running", suite.subject + ' ', options.watch ? false : true);
+ suite.run(options, function (result) {
+ Object.keys(result).forEach(function (k) {
+ results[k] += result[k];
+ });
+ run(suites, callback);
+ });
+ } else {
+ callback(results);
+ }
+ })(suites, callback);
+}
+
+function importSuites(files) {
+ msg(options.watcher ? 'watcher' : 'runner', 'loading', files);
+
+ var spawn = require('child_process').spawn;
+
+ function cwdname(f) {
+ return f.replace(process.cwd() + '/', '') + '.js';
+ }
+
+ function wrapSpawn(f) {
+ f = cwdname(f);
+ return function (options, callback) {
+ var args = [process.argv[1], '--json', '--supress-stdout', f],
+ p = spawn(process.argv[0], args),
+ result;
+
+ p.on('exit', function (code) {
+ callback(
+ !result ?
+ {errored: 1, total: 1}
+ :
+ result
+ );
+ });
+
+ var buffer = [];
+ p.stdout.on('data', function (data) {
+ data = data.toString().split(/\n/g);
+ if (data.length == 1) {
+ buffer.push(data[0]);
+ } else {
+ data[0] = buffer.concat(data[0]).join('');
+ buffer = [data.pop()];
+
+ data.forEach(function (data) {
+ if (data) {
+ data = JSON.parse(data);
+ if (data && data[0] === 'finish') {
+ result = data[1];
+ } else {
+ reporter.report(data);
+ }
+ }
+ });
+ }
+ });
+
+ p.stderr.pipe(process.stderr);
+ }
+ }
+
+ return files.reduce(options.isolate ? function (suites, f) {
+ return suites.concat({
+ run: wrapSpawn(f)
+ });
+ } : function (suites, f) {
+ var obj = require(f);
+ return suites.concat(Object.keys(obj).map(function (s) {
+ obj[s]._filename = cwdname(f);
+ return obj[s];
+ }));
+ }, [])
+}
+
+//
+// Recursively traverse a hierarchy, returning
+// a list of all relevant .js files.
+//
+function paths(dir) {
+ var paths = [];
+
+ try { fs.statSync(dir) }
+ catch (e) { return [] }
+
+ (function traverse(dir, stack) {
+ stack.push(dir);
+ fs.readdirSync(stack.join('/')).forEach(function (file) {
+ var path = stack.concat([file]).join('/'),
+ stat = fs.statSync(path);
+
+ if (file[0] == '.' || file === 'vendor') {
+ return;
+ } else if (stat.isFile() && fileExt.test(file)) {
+ paths.push(path);
+ } else if (stat.isDirectory()) {
+ traverse(file, stack);
+ }
+ });
+ stack.pop();
+ })(dir || '.', []);
+
+ return paths;
+}
+
+function msg(cmd, subject, str, p) {
+ if (options.verbose) {
+ util[p ? 'print' : 'puts']( stylize('vows ', 'green')
+ + stylize(cmd, 'bold')
+ + ' ' + subject + ' '
+ + (str ? (typeof(str) === 'string' ? str : inspect(str)) : '')
+ );
+ }
+}
+
+function abort(cmd, str) {
+ console.log(stylize('vows ', 'red') + stylize(cmd, 'bold') + ' ' + str);
+ console.log(stylize('vows ', 'red') + stylize(cmd, 'bold') + ' exiting');
+ process.exit(-1);
+}
View
27 node_modules/cradle/node_modules/vows/lib/assert/error.js
@@ -0,0 +1,27 @@
+var stylize = require('../vows/console').stylize;
+var inspect = require('../vows/console').inspect;
+
+require('assert').AssertionError.prototype.toString = function () {
+ var that = this,
+ source = this.stack.match(/([a-zA-Z0-9._-]+\.js)(:\d+):\d+/);
+
+ function parse(str) {
+ return str.replace(/{actual}/g, inspect(that.actual)).
+ replace(/{operator}/g, stylize(that.operator, 'bold')).
+ replace(/{expected}/g, (that.expected instanceof Function)
+ ? that.expected.name
+ : inspect(that.expected));
+ }
+
+ if (this.message) {
+ return stylize(parse(this.message), 'yellow') +
+ stylize(' // ' + source[1] + source[2], 'grey');
+ } else {
+ return stylize([
+ this.expected,
+ this.operator,
+ this.actual
+ ].join(' '), 'yellow');
+ }
+};
+
View
205 node_modules/cradle/node_modules/vows/lib/assert/macros.js
@@ -0,0 +1,205 @@
+var assert = require('assert'),
+ utils = require('./utils');
+
+var messages = {
+ 'equal' : "expected {expected},\n\tgot\t {actual} ({operator})",
+ 'notEqual' : "didn't expect {actual} ({operator})"
+};
+messages['strictEqual'] = messages['deepEqual'] = messages['equal'];
+messages['notStrictEqual'] = messages['notDeepEqual'] = messages['notEqual'];
+
+for (var key in messages) {
+ assert[key] = (function (key, callback) {
+ return function (actual, expected, message) {
+ callback(actual, expected, message || messages[key]);
+ };
+ })(key, assert[key]);
+}
+
+assert.ok = (function (callback) {
+ return function (actual, message) {
+ callback(actual, message || "expected expression to evaluate to {expected}, but was {actual}");
+ };
+})(assert.ok);
+
+assert.match = function (actual, expected, message) {
+ if (! expected.test(actual)) {
+ assert.fail(actual, expected, message || "expected {actual} to match {expected}", "match", assert.match);
+ }
+};
+assert.matches = assert.match;
+
+assert.isTrue = function (actual, message) {
+ if (actual !== true) {
+ assert.fail(actual, true, message || "expected {expected}, got {actual}", "===", assert.isTrue);
+ }
+};
+assert.isFalse = function (actual, message) {
+ if (actual !== false) {
+ assert.fail(actual, false, message || "expected {expected}, got {actual}", "===", assert.isFalse);
+ }
+};
+assert.isZero = function (actual, message) {
+ if (actual !== 0) {
+ assert.fail(actual, 0, message || "expected {expected}, got {actual}", "===", assert.isZero);
+ }
+};
+assert.isNotZero = function (actual, message) {
+ if (actual === 0) {
+ assert.fail(actual, 0, message || "expected non-zero value, got {actual}", "===", assert.isNotZero);
+ }
+};
+
+assert.greater = function (actual, expected, message) {
+ if (actual <= expected) {
+ assert.fail(actual, expected, message || "expected {actual} to be greater than {expected}", ">", assert.greater);
+ }
+};
+assert.lesser = function (actual, expected, message) {
+ if (actual >= expected) {
+ assert.fail(actual, expected, message || "expected {actual} to be lesser than {expected}", "<", assert.lesser);
+ }
+};
+
+assert.inDelta = function (actual, expected, delta, message) {
+ var lower = expected - delta;
+ var upper = expected + delta;
+ if (actual < lower || actual > upper) {
+ assert.fail(actual, expected, message || "expected {actual} to be in within *" + delta.toString() + "* of {expected}", null, assert.inDelta);
+ }
+};
+
+//
+// Inclusion
+//
+assert.include = function (actual, expected, message) {
+ if ((function (obj) {
+ if (isArray(obj) || isString(obj)) {
+ return obj.indexOf(expected) === -1;
+ } else if (isObject(actual)) {
+ return ! obj.hasOwnProperty(expected);
+ }
+ return false;
+ })(actual)) {
+ assert.fail(actual, expected, message || "expected {actual} to include {expected}", "include", assert.include);
+ }
+};
+assert.includes = assert.include;
+
+assert.deepInclude = function (actual, expected, message) {
+ if (!isArray(actual)) {
+ return assert.include(actual, expected, message);
+ }
+ if (!actual.some(function (item) { return utils.deepEqual(item, expected) })) {
+ assert.fail(actual, expected, message || "expected {actual} to include {expected}", "include", assert.deepInclude);
+ }
+};
+assert.deepIncludes = assert.deepInclude;
+
+//
+// Length
+//
+assert.isEmpty = function (actual, message) {
+ if ((isObject(actual) && Object.keys(actual).length > 0) || actual.length > 0) {
+ assert.fail(actual, 0, message || "expected {actual} to be empty", "length", assert.isEmpty);
+ }
+};
+
+assert.length = function (actual, expected, message) {
+ if (actual.length !== expected) {
+ assert.fail(actual, expected, message || "expected {actual} to have {expected} element(s)", "length", assert.length);
+ }
+};
+
+//
+// Type
+//
+assert.isArray = function (actual, message) {
+ assertTypeOf(actual, 'array', message || "expected {actual} to be an Array", assert.isArray);
+};
+assert.isObject = function (actual, message) {
+ assertTypeOf(actual, 'object', message || "expected {actual} to be an Object", assert.isObject);
+};
+assert.isNumber = function (actual, message) {
+ if (isNaN(actual)) {
+ assert.fail(actual, 'number', message || "expected {actual} to be of type {expected}", "isNaN", assert.isNumber);
+ } else {
+ assertTypeOf(actual, 'number', message || "expected {actual} to be a Number", assert.isNumber);
+ }
+};
+assert.isBoolean = function (actual, message) {
+ if (actual !== true && actual !== false) {
+ assert.fail(actual, 'boolean', message || "expected {actual} to be a Boolean", "===", assert.isBoolean);
+ }
+};
+assert.isNaN = function (actual, message) {
+ if (actual === actual) {
+ assert.fail(actual, 'NaN', message || "expected {actual} to be NaN", "===", assert.isNaN);
+ }
+};
+assert.isNull = function (actual, message) {
+ if (actual !== null) {
+ assert.fail(actual, null, message || "expected {expected}, got {actual}", "===", assert.isNull);
+ }
+};
+assert.isNotNull = function (actual, message) {
+ if (actual === null) {
+ assert.fail(actual, null, message || "expected non-null value, got {actual}", "===", assert.isNotNull);
+ }
+};
+assert.isUndefined = function (actual, message) {
+ if (actual !== undefined) {
+ assert.fail(actual, undefined, message || "expected {actual} to be {expected}", "===", assert.isUndefined);
+ }
+};
+assert.isString = function (actual, message) {
+ assertTypeOf(actual, 'string', message || "expected {actual} to be a String", assert.isString);
+};
+assert.isFunction = function (actual, message) {
+ assertTypeOf(actual, 'function', message || "expected {actual} to be a Function", assert.isFunction);
+};
+assert.typeOf = function (actual, expected, message) {
+ assertTypeOf(actual, expected, message, assert.typeOf);
+};
+assert.instanceOf = function (actual, expected, message) {
+ if (! (actual instanceof expected)) {
+ assert.fail(actual, expected, message || "expected {actual} to be an instance of {expected}", "instanceof", assert.instanceOf);
+ }
+};
+
+//
+// Utility functions
+//
+
+function assertTypeOf(actual, expected, message, caller) {
+ if (typeOf(actual) !== expected) {
+ assert.fail(actual, expected, message || "expected {actual} to be of type {expected}", "typeOf", caller);
+ }
+};
+
+function isArray (obj) {
+ return Array.isArray(obj);
+}
+
+function isString (obj) {
+ return typeof(obj) === 'string' || obj instanceof String;
+}
+
+function isObject (obj) {
+ return typeof(obj) === 'object' && obj && !isArray(obj);
+}
+
+// A better `typeof`
+function typeOf(value) {
+ var s = typeof(value),
+ types = [Object, Array, String, RegExp, Number, Function, Boolean, Date];
+
+ if (s === 'object' || s === 'function') {
+ if (value) {
+ types.forEach(function (t) {
+ if (value instanceof t) { s = t.name.toLowerCase() }
+ });
+ } else { s = 'null' }
+ }
+ return s;
+}
View
77 node_modules/cradle/node_modules/vows/lib/assert/utils.js
@@ -0,0 +1,77 @@
+
+// Taken from node/lib/assert.js
+exports.deepEqual = function (actual, expected) {
+ if (actual === expected) {
+ return true;
+
+ } else if (Buffer.isBuffer(actual) && Buffer.isBuffer(expected)) {
+ if (actual.length != expected.length) return false;
+
+ for (var i = 0; i < actual.length; i++) {
+ if (actual[i] !== expected[i]) return false;
+ }
+ return true;
+
+ } else if (actual instanceof Date && expected instanceof Date) {
+ return actual.getTime() === expected.getTime();
+
+ } else if (typeof actual != 'object' && typeof expected != 'object') {
+ return actual == expected;
+
+ } else {
+ return objEquiv(actual, expected);
+ }
+}
+
+// Taken from node/lib/assert.js
+exports.notDeepEqual = function (actual, expected, message) {
+ if (exports.deepEqual(actual, expected)) {
+ fail(actual, expected, message, 'notDeepEqual', assert.notDeepEqual);
+ }
+}
+
+// Taken from node/lib/assert.js
+function isUndefinedOrNull(value) {
+ return value === null || value === undefined;
+}
+
+// Taken from node/lib/assert.js
+function isArguments(object) {
+ return Object.prototype.toString.call(object) == '[object Arguments]';
+}
+
+// Taken from node/lib/assert.js
+function objEquiv(a, b) {
+ if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
+ return false;
+ if (a.prototype !== b.prototype) return false;
+ if (isArguments(a)) {
+ if (!isArguments(b)) {
+ return false;
+ }
+ a = pSlice.call(a);
+ b = pSlice.call(b);
+ return exports.deepEqual(a, b);
+ }
+ try {
+ var ka = Object.keys(a),
+ kb = Object.keys(b),
+ key, i;
+ } catch (e) {
+ return false;
+ }
+ if (ka.length != kb.length)
+ return false;
+ ka.sort();
+ kb.sort();
+ for (i = ka.length - 1; i >= 0; i--) {
+ if (ka[i] != kb[i])
+ return false;
+ }
+ for (i = ka.length - 1; i >= 0; i--) {
+ key = ka[i];
+ if (!exports.deepEqual(a[key], b[key])) return false;
+ }
+ return true;
+}
+
View
193 node_modules/cradle/node_modules/vows/lib/vows.js
@@ -0,0 +1,193 @@
+//
+// Vows.js - asynchronous event-based BDD for node.js
+//
+// usage:
+//
+// var vows = require('vows');
+//
+// vows.describe('Deep Thought').addBatch({
+// "An instance of DeepThought": {
+// topic: new DeepThought,
+//
+// "should know the answer to the ultimate question of life": function (deepThought) {
+// assert.equal (deepThought.question('what is the answer to the universe?'), 42);
+// }
+// }
+// }).run();
+//
+var sys = require('sys'),
+ path = require('path'),
+ events = require('events'),
+ vows = exports;
+
+// Options
+vows.options = {
+ Emitter: events.EventEmitter,
+ reporter: require('./vows/reporters/dot-matrix'),
+ matcher: /.*/,
+ error: true // Handle "error" event
+};
+
+vows.__defineGetter__('reporter', function () {
+ return vows.options.reporter;
+});
+
+var stylize = require('./vows/console').stylize;
+var console = require('./vows/console');
+
+vows.inspect = require('./vows/console').inspect;
+vows.prepare = require('./vows/extras').prepare;
+vows.tryEnd = require('./vows/suite').tryEnd;
+
+//
+// Assertion Macros & Extensions
+//
+require('./assert/error');
+require('./assert/macros');
+
+//
+// Suite constructor
+//
+var Suite = require('./vows/suite').Suite;
+
+//
+// This function gets added to events.EventEmitter.prototype, by default.
+// It's essentially a wrapper around `on`, which adds all the specification
+// goodness.
+//
+function addVow(vow) {
+ var batch = vow.batch;
+
+ batch.total ++;
+ batch.vows.push(vow);
+
+ return this.on("success", function () {
+ var args = Array.prototype.slice.call(arguments);
+ // If the callback is expecting two or more arguments,
+ // pass the error as the first (null) and the result after.
+ if (vow.callback.length >= 2 && batch.suite.options.error) {
+ args.unshift(null);
+ }
+ runTest(args, this.ctx);
+ vows.tryEnd(batch);
+
+ }).on("error", function (err) {
+ if (vow.callback.length >= 2 || !batch.suite.options.error) {
+ runTest([err], this.ctx);
+ } else {
+ output('errored', { type: 'promise', error: err.stack || err.message || JSON.stringify(err) });
+ }
+ vows.tryEnd(batch);
+ });
+
+ function runTest(args, ctx) {
+ var topic, status;
+
+ if (vow.callback instanceof String) {
+ return output('pending');
+ }
+
+ // Run the test, and try to catch `AssertionError`s and other exceptions;
+ // increment counters accordingly.
+ try {
+ vow.callback.apply(ctx === global || !ctx ? vow.binding : ctx, args);
+ output('honored');
+ } catch (e) {
+ if (e.name && e.name.match(/AssertionError/)) {
+ output('broken', e.toString());
+ } else {
+ output('errored', e.stack || e.message || e);
+ }
+ }
+ }
+
+ function output(status, exception) {
+ batch[status] ++;
+ vow.status = status;
+
+ if (vow.context && batch.lastContext !== vow.context) {
+ batch.lastContext = vow.context;
+ batch.suite.report(['context', vow.context]);
+ }
+ batch.suite.report(['vow', {
+ title: vow.description,
+ context: vow.context,
+ status: status,
+ exception: exception || null
+ }]);
+ }
+};
+
+//
+// On exit, check that all promises have been fired.
+// If not, report an error message.
+//
+process.on('exit', function () {
+ var results = { honored: 0, broken: 0, errored: 0, pending: 0, total: 0 }, failure;
+
+ vows.suites.forEach(function (s) {
+ if ((s.results.total > 0) && (s.results.time === null)) {
+ s.reporter.print('\n\n');
+ s.reporter.report(['error', { error: "Asynchronous Error", suite: s }]);
+ }
+ s.batches.forEach(function (b) {
+ var unFired = [];
+
+ b.vows.forEach(function (vow) {
+ if (! vow.status) {
+ if (unFired.indexOf(vow.context) === -1) {
+ unFired.push(vow.context);
+ }
+ }
+ });
+
+ if (unFired.length > 0) { sys.print('\n') }
+
+ unFired.forEach(function (title) {
+ s.reporter.report(['error', {
+ error: "callback not fired",
+ context: title,
+ batch: b,
+ suite: s
+ }]);
+ });
+
+ if (b.status === 'begin') {
+ failure = true;
+ results.errored ++;
+ results.total ++;
+ }
+ Object.keys(results).forEach(function (k) { results[k] += b[k] });
+ });
+ });
+ if (failure) {
+ sys.puts(console.result(results));
+ }
+});
+
+vows.suites = [];
+
+//
+// Create a new test suite
+//
+vows.describe = function (subject) {
+ var suite = new(Suite)(subject);
+
+ this.options.Emitter.prototype.addVow = addVow;
+ this.suites.push(suite);
+
+ //
+ // Add any additional arguments as batches if they're present
+ //
+ if (arguments.length > 1) {
+ for (var i = 1, l = arguments.length; i < l; ++i) {
+ suite.addBatch(arguments[i]);
+ }
+ }
+
+ return suite;
+};
+
+
+vows.version = require('fs').readFileSync(path.join(__dirname, '..', 'package.json'))
+ .toString().match(/"version"\s*:\s*"([\d.]+)"/)[1];
View
131 node_modules/cradle/node_modules/vows/lib/vows/console.js
@@ -0,0 +1,131 @@
+var eyes = require('eyes').inspector({ stream: null, styles: false });
+
+// Stylize a string
+this.stylize = function stylize(str, style) {
+ var styles = {
+ 'bold' : [1, 22],
+ 'italic' : [3, 23],
+ 'underline' : [4, 24],
+ 'cyan' : [96, 39],
+ 'yellow' : [33, 39],
+ 'green' : [32, 39],
+ 'red' : [31, 39],
+ 'grey' : [90, 39],
+ 'green-hi' : [92, 32],
+ };
+ return '\033[' + styles[style][0] + 'm' + str +
+ '\033[' + styles[style][1] + 'm';
+};
+
+var $ = this.$ = function (str) {
+ str = new(String)(str);
+
+ ['bold', 'grey', 'yellow', 'red', 'green', 'white', 'cyan', 'italic'].forEach(function (style) {
+ Object.defineProperty(str, style, {
+ get: function () {
+ return exports.$(exports.stylize(this, style));
+ }
+ });
+ });
+ return str;
+};
+
+this.puts = function (options) {
+ var stylize = exports.stylize;
+ options.stream || (options.stream = process.stdout);
+ options.tail = options.tail || '';
+
+ return function (args) {
+ args = Array.prototype.slice.call(arguments);
+ if (!options.raw) {
+ args = args.map(function (a) {
+ return a.replace(/`([^`]+)`/g, function (_, capture) { return stylize(capture, 'italic') })
+ .replace(/\*([^*]+)\*/g, function (_, capture) { return stylize(capture, 'bold') });
+ });
+ }
+ return options.stream.write(args.join('\n') + options.tail);
+ };
+};
+
+this.result = function (event) {