Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
  • 12 commits
  • 3 files changed
  • 0 commit comments
  • 1 contributor
Commits on Apr 21, 2010
@jasonwyatt jasonwyatt Moved the files around a bit and added package.json, so hopefully we …
…can use seedJS
1f41492
@jasonwyatt jasonwyatt Finished changes for the seedJS-ification of node-couch. 9d1caf5
@jasonwyatt jasonwyatt Merge branch 'master' of git://github.com/sixtus/node-couch b2aec75
Commits on Apr 22, 2010
@jasonwyatt jasonwyatt Added comments to most of the utility functions. 0f2b04c
Commits on Apr 24, 2010
@jasonwyatt jasonwyatt Finished adding function-level comments. bf1494f
@jasonwyatt jasonwyatt Merge branch 'master' of git://github.com/sixtus/node-couch 0e4464e
@jasonwyatt jasonwyatt Changed db.create to call success even if the database already exists…
…. Added an additional parameter to db.create called failOnDuplicate, if true, db.create will behave as it used to.
ce568bd
Commits on Apr 25, 2010
@jasonwyatt jasonwyatt Added the ability to serialize functions to JSON strings for document…
… creation (think: views)... also made it so the default mode for saveDoc is to ignore revision conflict.
933c99e
@jasonwyatt jasonwyatt There was an issue with the cached client blocking or something and a…
… call to db.view after doing a few other calls was hanging for no reason. I got rid of the cache because of this. Is there a good reason to keep the cache?
ecc77b0
@jasonwyatt jasonwyatt Added an example application: "blog" which uses node-couch to serve u…
…p a simple blog. It doesn't include support for creating new posts yet, and I'm kind of thinking it shouldn't include that support - CouchDB can be its own "admin panel".
b93ef69
@jasonwyatt jasonwyatt Fixed some minor formatting and removed a redundant variable in the b…
…log example.
6609fef
@jasonwyatt jasonwyatt Cleaned up blog.js and moved some more stuff to blog-support/index.js…
…... blog.js is now 60-something lines
a35875b
Showing with 278 additions and 27 deletions.
  1. +123 −0 examples/blog-support/index.js
  2. +66 −0 examples/blog.js
  3. +89 −27 lib/index.js
View
123 examples/blog-support/index.js
@@ -0,0 +1,123 @@
+// posts:
+// Array of posts to seed the database with. (taken from http://jwf.us,
+// by Jason Feinstein with permission)
+exports.posts = [
+ {
+ _id: '1',
+ title: 'Firefox Heads-up Display for Developers',
+ body: '<p>Looks like we might have a potential competitor to Firebug coming out soon&#8230; This is exciting!</p><blockquote>&#8230; an interactive Console to help web developers understand everything that happens during the creation of a web-page. Console entries will be time-stamped objects representing errors, network traffic, javascript events, DOM/HTML mutation and logging output. The Console will also have an interactive command line for evaluating javascript against the current webpage and an interactive Workspace window for entering and evaluating larger blocks of code.</blockquote><p>I know it&#8217;s far-fetched, and kinda beside the point for Mozilla/Firefox developers to make it cross-platform, but I&#8217;d love to see something along the lines of <a href="http://getfirebug.com/firebuglite" target="_blank">Firebug Lite</a> with this too.</p>',
+ type: 'post',
+ date: 'Sun Apr 4 2010 01:37:38 GMT-0400 (EDT)'
+ },
+ {
+ _id: '2',
+ title: 'My latest GitHub project: willow',
+ body: '<p>Willow is a JavaScript logging tool which uses the &#8220;magic&#8221; of JavaScript&#8217;s <code>arguments</code> object to introduce valuable information to logging and trace messages sent to Firebug&#8217;s console.</p><p>It&#8217;s not ready to be officially released yet, which is why I&#8217;m not really going to describe it too much here (yet). <b>I&#8217;m curious to see what kind of additional features people would be interested in using, beyond the ones I&#8217;ve implemented.</b> </p><p>If you download the source from the link, and build it (see the README for how to build, it&#8217;s very easy) - you&#8217;ll be able to see some example applications of willow.</p>',
+ type: 'post',
+ date: 'Sun Feb 7 2010 01:37:38 GMT-0400 (EDT)'
+ },
+ {
+ _id: '3',
+ title: 'Make alert() behave like console.log(), when possible..',
+ body: '<p>The other day, <a href="http://twitter.com/mrspeaker" target="_blank">@mrspeaker</a> tweeted <a href="http://twitter.com/mrspeaker/status/6745908321" target="_blank">this</a>:</p> <blockquote>c&#8217;mon alert() - just magically be a _bit_ more like console.log and you&#8217;d save me a lot of time</blockquote> <p>This got the creative juices flowing a little and I coded up a small script to overwrite how <code>alert()</code> operates and, if available, it will choose to behave exactly like <code>console.log()</code> by writing to the console instead of popping up the dialog (if it&#8217;s available). It still needs some work to support formatted strings in <code>alert()</code> dialogs, for when <code>console.log()</code> isn&#8217;t available.</p> <pre><code>var oldAlert = alert;\nalert = function(){\n if(window.console != null){\n console.log.apply(null, arguments);\n } else {\n oldAlert.apply(null, arguments);\n }\n};</code></pre> <p>What this means now is that you can exclusively use <code>alert()</code> in your code and not have to use <code>console.log()</code> and worry about your JavaScript breaking in browsers that don&#8217;t have Firebug or a console.</p> <p>Find it on GitHub, <a href="http://gist.github.com/258352" target="_blank">here</a>.</p>',
+ type: 'post',
+ date: 'Fri Dec 18 2009 01:37:38 GMT-0400 (EDT)'
+ }
+];
+
+// designDoc:
+// The CouchDB view page that will be used to get a reverse
+// chronologically-ordered list of posts for rendering.
+exports.designDoc = {
+ _id: "_design/blog",
+ language: "javascript",
+ views: {
+ posts_by_date: {
+ map: function(doc){
+ if(doc.type == "post"){
+ emit(-Date.parse(doc.date), doc);
+ }
+ }
+ }
+ }
+};
+
+
+function CouchBlogRenderer(title){
+ // summary:
+ // Tool used to render the blog to HTML and RSS.
+ // description:
+ // Assumes blog posts will contain a format similar to those above
+ // and can render them to an RSS Feed, a main page (listing posts
+ // in reverse-chronological order), and individual pages.
+ // title: String
+ // Title of the blog, defaults to "Blog".
+ this.title = title || "Blog";
+}
+CouchBlogRenderer.prototype = {
+ renderFeed: function(req, res, posts){
+ res.writeHead(200, {
+ 'Content-Type': 'application/rss+xml'
+ });
+
+ res.write('<?xml version="1.0" encoding="UTF-8"?>\n');
+ res.write('<rss version="2.0">');
+ res.write('<channel> \n');
+ res.write('<title>node-couch example blog</title> \n');
+ res.write('<link>http://github.com/jasonwyatt/node-couch</link> \n');
+ res.write('<description>RSS Feed for the example blog app that comes with node-couch.</description>\n');
+ res.write('<pubDate>'+(new Date().toString())+'</pubDate> \n');
+ res.write('<generator>http://github.com/jasonwyatt/node-couch</generator>\n');
+ res.write('<language>en</language> \n');
+ for(var i = 0, len = posts.length; i < len; i++){
+ res.write('<item> \n');
+ res.write(' <title>'+posts[i].value.title+'</title> \n');
+ res.write(' <link>http://localhost:8080/posts/'+posts[i].value._id+'</link> \n');
+ res.write(' <pubDate>'+posts[i].value.date+'</pubDate> \n');
+ res.write(' <guid isPermaLink="true">http://localhost:8080/posts/'+posts[i].value._id+'</guid>\n');
+ res.write(' <description>'+posts[i].value.body+'</description> \n');
+ res.write(' <content>'+posts[i].value.body+'</content> \n');
+ res.write('</item> \n');
+ }
+ res.write('</channel> \n');
+ res.write('</rss>\n');
+ res.end();
+ },
+ renderMain: function(req, res, posts){
+ this.renderPageHeader(req,res);
+
+ for(var i = 0, len = posts.length; i < len; i++){
+ this._renderPost(req,res,posts[i].value);
+ }
+
+ this.renderPageFooter(req,res);
+ },
+ renderPostPage: function(req, res, post){
+ this.renderPageHeader(req,res,post.title);
+ this._renderPost(req,res,post);
+ this.renderPageFooter(req,res);
+ },
+ _renderPost: function(req, res, post){
+ res.write('<h2><a href="http://localhost:8080/posts/'+post._id+'">'+post.title+'</a></h2>\n');
+ res.write('<div class="post">'+post.body+'</div> \n');
+ res.write('<div class="date">at '+post.date+'</div>\n');
+ },
+ renderPageHeader: function(req, res, title){
+ res.writeHead(200, {
+ 'Content-Type': 'text/html'
+ });
+ res.write('<html> \n');
+ res.write('<head> \n');
+ res.write(' <title>'+this.title+(title ? " | "+title : "")+'</title>\n');
+ res.write('</head> \n');
+ res.write('<body style="width: 600px; margin: 0 auto;">\n');
+ res.write('<h1><a href="/">'+this.title+'</a></h1>\n');
+ },
+ renderPageFooter: function(req, res){
+ res.write('<p>Check out the <a href="/feed">RSS feed</a>.</p>');
+ res.write('</body>\n');
+ res.write('</html>\n');
+ res.end();
+ }
+};
+exports.CouchBlogRenderer = CouchBlogRenderer;
View
66 examples/blog.js
@@ -0,0 +1,66 @@
+#!/usr/local/bin/node
+
+var sys = require('sys');
+var http = require('http');
+var url = require('url');
+var repl = require('repl');
+
+var CouchDB = require('../lib').CouchDB;
+var blogsupport = require('./blog-support');
+
+var blogTitle = "node-couch example blog";
+var dbName = "node_couch_blog";
+var blogPort = 8080;
+var renderer = new blogsupport.CouchBlogRenderer(blogTitle);
+var designDoc = blogsupport.designDoc;
+var postRegex = /^\/posts\/([a-f0-9]*)$/;
+var feedRegex = /^\/feed$/;
+
+var db = CouchDB.db(dbName);
+db.create({
+ success: function(res){
+ db.saveDoc(designDoc, {
+ success: startBlog
+ });
+ }
+});
+
+function startBlog(){
+ for(var i = 0, len = blogsupport.posts.length; i < len; i++){
+ db.saveDoc(blogsupport.posts[i]);
+ }
+
+ var server = http.createServer(function (req, res) {
+ var pathDetails = url.parse(req.url, true);
+
+ if(pathDetails.pathname === "/"){
+ db.view("blog/posts_by_date", {
+ success: function(result){
+ renderer.renderMain(req, res, result.rows);
+ }
+ });
+ } else if(postRegex.test(pathDetails.pathname)){
+ var match = postRegex.exec(pathDetails.pathname);
+
+ var postURI = pathDetails.pathname;
+
+ var docId = match[1];
+ db.openDoc(docId, {
+ success: function(doc){
+ renderer.renderPostPage(req, res, doc);
+ }
+ });
+ } else if(feedRegex.test(pathDetails.pathname)){
+ db.view("blog/posts_by_date", {
+ success: function(result){
+ renderer.renderFeed(req, res, result.rows);
+ }
+ });
+ } else {
+ res.writeHead(404, {'Content-Type': 'text/plain'});
+ res.end("Cannot find specified page: "+req.url);
+ }
+ }).listen(blogPort);
+
+ sys.puts("Blog started.");
+}
View
116 lib/index.js
@@ -25,27 +25,18 @@ var sys = require('sys'),
url = require('url'),
base64 = require('./base64');
-var clients = {};
-function cache_client(host) {
+function createClient(host) {
// summary:
- // Creates or fetches an HTTP Client for the specified host.
+ // Creates an HTTP Client for the specified host.
// description:
- // If we've already created an HTTPClient object for the host
- // argument, fetch it from the client cache object. Otherwise,
- // create a new HTTPClient object and return it after adding it to
- // the cache.
+ // Create a new HTTPClient object and return it
// host: String
// CouchDB Host URL.
// returns:
// An HTTPClient object associated with the specified host.
- var client = clients[host];
- if (client) {
- return client;
- } else {
- var uri = url.parse(host);
- return clients[host] = http.createClient(uri.port, uri.hostname);
- }
+ var uri = url.parse(host);
+ return http.createClient(uri.port, uri.hostname);
}
function _interact(verb, path, successStatus, options, host) {
@@ -59,8 +50,8 @@ function _interact(verb, path, successStatus, options, host) {
// HTTP request type (or verb). E.g. GET, PUT, POST, DELETE
// path: String
// CouchDB resource path.
- // successStatus: Integer
- // The HTTP response code we expect on a successful interaction
+ // successStatus: Integer|Array
+ // The HTTP response code(s) we expect on a successful interaction
// with the server.
// options: Object
// Options for the interaction with the server.
@@ -84,7 +75,7 @@ function _interact(verb, path, successStatus, options, host) {
// placeholder for the HTTP request object.
var request;
- var client = cache_client(host);
+ var client = createClient(host);
var requestPath = path + encodeOptions(options);
if (CouchDB.debug) {
sys.puts("COUCHING " + requestPath + " -> " + verb);
@@ -132,7 +123,7 @@ function _interact(verb, path, successStatus, options, host) {
}
responseBody = JSON.parse(responseBody);
- if (response.statusCode === successStatus) {
+ if (isAcceptableStatus(response.statusCode,successStatus)) {
if (options.success) {
options.success(responseBody);
}
@@ -146,6 +137,30 @@ function _interact(verb, path, successStatus, options, host) {
}
+function isAcceptableStatus(testCode, acceptable){
+ // summary:
+ // Quick utility function to test if an HTTP response code is
+ // acceptable for a purpose.
+ // testCode: Integer
+ // Code we're testing.
+ // acceptable: Integer|Array
+ // Either an individual status code, or an array of acceptable
+ // status codes.
+ // returns:
+ // True if testCode is an acceptable status code. False otherwise.
+
+ if (acceptable.constructor.toString().indexOf("Array") == -1) {
+ acceptable = [acceptable];
+ }
+
+ for(var i = 0, len = acceptable.length; i < len; i++){
+ if(acceptable[i] == testCode){
+ return true;
+ }
+ }
+ return false;
+}
+
function encodeOptions(options) {
// summary:
// Builds an HTTP query string given an object containing options
@@ -190,7 +205,15 @@ function toJSON(obj) {
// Object to convert to JSON
// returns:
// JSON string representation of obj.
- return obj !== null ? JSON.stringify(obj) : null;
+ if(obj !== null){
+ return JSON.stringify(obj, function(key, val) {
+ if (typeof val == 'function') {
+ return val.toString();
+ }
+ return val;
+ });
+ }
+ return null;
}
var CouchDB = {
@@ -315,8 +338,8 @@ var CouchDB = {
// That is, it's the stuff that comes after
// [host]/[name]/ in a URL. Otherwise, it's a full
// path.
- // successStatus: Integer
- // HTTP status code that indicates successful
+ // successStatus: Integer|Array
+ // HTTP status code(s) that indicate successful
// interaction.
// options: Object
// Standard options object.
@@ -341,7 +364,7 @@ var CouchDB = {
this.interact("post", "_compact", 202, options);
},
- create : function(options) {
+ create : function(options, failOnDuplicate) {
// summary:
// Creates the database.
// description:
@@ -350,7 +373,19 @@ var CouchDB = {
// http://wiki.apache.org/couchdb/HTTP_database_API
// options: Object
// Standard options object.
- this.interact("put", "", 201, options);
+ // failOnDuplicate: Boolean - Optional
+ // If set to true, this function will fail and the
+ // error option callback will be run if the database
+ // already exists. If set to false, or left unset, the
+ // success function will be called even if the database
+ // exists.
+
+ var acceptableStatuses = [201, 412];
+ if(failOnDuplicate){
+ acceptableStatuses.pop();
+ }
+
+ this.interact("put", "", acceptableStatuses, options);
},
drop : function(options) {
@@ -424,7 +459,7 @@ var CouchDB = {
this.interact("get", path, 200, options); // interact will override get to post when needed
},
- saveDoc : function(doc, options) {
+ saveDoc : function(doc, options, enforceRevision) {
// summary:
// Creates or updates a particular document.
// description:
@@ -438,17 +473,40 @@ var CouchDB = {
// Document to save.
// options: Object
// Standard options object.
+ // enforceRevision: Boolean - Optional
+ // If true, we will enforce revision-checking when
+ // saving the document. Otherwise, if the document
+ // doesn't have a valid _rev attribute, we'll retrieve
+ // it and then re-try the PUT request.
options = options || {};
doc = doc || {};
+
+ var me = this;
+
var success = options.success;
options.success = function(result) {
if (!result.ok) {
- options.error(result);
+ if(enforceRevision){
+ options.error(result);
+ } else {
+ me.openDoc(doc._id, {
+ success: function(existingDoc){
+ doc._rev = existingDoc._rev;
+ var opts = options;
+ opts.success = success;
+ me.saveDoc(doc,opts);
+ },
+ error: options.error
+ });
+ }
+ return;
} else {
doc._id = result.id;
doc._rev = result.rev;
}
- if (success) { success(doc); }
+ if (success) {
+ success(doc);
+ }
};
options.body = doc;
@@ -456,7 +514,11 @@ var CouchDB = {
if (doc._id === undefined) {
this.interact("post", "", 201, options);
} else {
- this.interact("put", doc._id, 201, options);
+ var successCodes = [201, 409];
+ if(enforceRevision){
+ successCodes.pop();
+ }
+ this.interact("put", doc._id, successCodes, options);
}
},

No commit comments for this range

Something went wrong with that request. Please try again.