couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jch...@apache.org
Subject svn commit: r782294 - in /couchdb/branches/list-iterator: ./ share/server/ share/www/script/test/ src/couchdb/ test/
Date Sat, 06 Jun 2009 18:18:38 GMT
Author: jchris
Date: Sat Jun  6 18:18:37 2009
New Revision: 782294

URL: http://svn.apache.org/viewvc?rev=782294&view=rev
Log:
new API for _list function. See test suite for examples.

Modified:
    couchdb/branches/list-iterator/.gitignore
    couchdb/branches/list-iterator/share/server/loop.js
    couchdb/branches/list-iterator/share/server/render.js
    couchdb/branches/list-iterator/share/server/util.js
    couchdb/branches/list-iterator/share/www/script/test/list_views.js
    couchdb/branches/list-iterator/share/www/script/test/show_documents.js
    couchdb/branches/list-iterator/src/couchdb/couch_httpd.erl
    couchdb/branches/list-iterator/src/couchdb/couch_httpd_show.erl
    couchdb/branches/list-iterator/src/couchdb/couch_js.c
    couchdb/branches/list-iterator/src/couchdb/couch_os_process.erl
    couchdb/branches/list-iterator/src/couchdb/couch_query_servers.erl
    couchdb/branches/list-iterator/test/query_server_spec.rb

Modified: couchdb/branches/list-iterator/.gitignore
URL: http://svn.apache.org/viewvc/couchdb/branches/list-iterator/.gitignore?rev=782294&r1=782293&r2=782294&view=diff
==============================================================================
--- couchdb/branches/list-iterator/.gitignore (original)
+++ couchdb/branches/list-iterator/.gitignore Sat Jun  6 18:18:37 2009
@@ -10,6 +10,7 @@
 configure
 autom4te.cache
 build-aux
+*.diff
 
 # ./configure
 

Modified: couchdb/branches/list-iterator/share/server/loop.js
URL: http://svn.apache.org/viewvc/couchdb/branches/list-iterator/share/server/loop.js?rev=782294&r1=782293&r2=782294&view=diff
==============================================================================
--- couchdb/branches/list-iterator/share/server/loop.js (original)
+++ couchdb/branches/list-iterator/share/server/loop.js Sat Jun  6 18:18:37 2009
@@ -21,6 +21,9 @@
   sandbox.toJSON = toJSON;
   sandbox.respondWith = respondWith;
   sandbox.registerType = registerType;
+  sandbox.start = start;
+  sandbox.send = send;
+  sandbox.getRow = getRow;
 } catch (e) {}
 
 // Commands are in the form of json arrays:
@@ -31,21 +34,19 @@
 var line, cmd, cmdkey;
 
 var dispatch = {
-  "reset"      : State.reset,
-  "add_fun"    : State.addFun,
-  "map_doc"    : Views.mapDoc,
-  "reduce"     : Views.reduce,
-  "rereduce"   : Views.rereduce,
-  "validate"   : Validate.validate,
-  "show_doc"   : Render.showDoc,
-  "list_begin" : Render.listBegin,
-  "list_row"   : Render.listRow,
-  "list_tail"  : Render.listTail 
+  "reset"    : State.reset,
+  "add_fun"  : State.addFun,
+  "map_doc"  : Views.mapDoc,
+  "reduce"   : Views.reduce,
+  "rereduce" : Views.rereduce,
+  "validate" : Validate.validate,
+  "show"     : Render.show,
+  "list"     : Render.list
 };
 
 while (line = eval(readline())) {
-  cmd = eval(line)
-  line_length = line.length
+  cmd = eval(line);
+  line_length = line.length;
   try {
     cmdkey = cmd.shift();
     if (dispatch[cmdkey]) {

Modified: couchdb/branches/list-iterator/share/server/render.js
URL: http://svn.apache.org/viewvc/couchdb/branches/list-iterator/share/server/render.js?rev=782294&r1=782293&r2=782294&view=diff
==============================================================================
--- couchdb/branches/list-iterator/share/server/render.js (original)
+++ couchdb/branches/list-iterator/share/server/render.js Sat Jun  6 18:18:37 2009
@@ -12,9 +12,10 @@
 
 // mimeparse.js
 // http://code.google.com/p/mimeparse/
+// MIT Licensed http://www.opensource.org/licenses/mit-license.php
 // Code with comments: http://mimeparse.googlecode.com/svn/trunk/mimeparse.js
 // Tests: http://mimeparse.googlecode.com/svn/trunk/mimeparse-js-test.html
-// Ported from version 0.1.2
+// Ported by Chris Anderson from version 0.1.2
 
 var Mimeparse = (function() {
   function strip(string) {
@@ -111,6 +112,7 @@
   return publicMethods;
 })();
 
+
 // this function provides a shortcut for managing responses by Accept header
 respondWith = function(req, responders) {
   var bestKey = null, accept = req.headers["Accept"];
@@ -131,7 +133,7 @@
     var resp = maybeWrapResponse(rFunc());
     resp["headers"] = resp["headers"] || {};
     resp["headers"]["Content-Type"] = bestMime;
-    respond(resp);
+    respond(["resp", resp]);
   } else {
     throw({code:406, body:"Not Acceptable: "+accept});    
   }
@@ -162,8 +164,6 @@
 registerType("html", "text/html");
 registerType("xhtml", "application/xhtml+xml", "xhtml");
 registerType("xml", "application/xml", "text/xml", "application/x-xml");
-// http://www.ietf.org/rfc/rfc4627.txt
-registerType("json", "application/json", "text/x-json");
 registerType("js", "text/javascript", "application/javascript", "application/x-javascript");
 registerType("css", "text/css");
 registerType("ics", "text/calendar");
@@ -171,58 +171,131 @@
 registerType("rss", "application/rss+xml");
 registerType("atom", "application/atom+xml");
 registerType("yaml", "application/x-yaml", "text/yaml");
+
 // just like Rails
 registerType("multipart_form", "multipart/form-data");
 registerType("url_encoded_form", "application/x-www-form-urlencoded");
 
+// http://www.ietf.org/rfc/rfc4627.txt
+registerType("json", "application/json", "text/x-json");
+
+
+
+
+//  Start chunks
+var startResp = {};
+function start(resp) {
+  startResp = resp || {};
+};
+
+function sendStart(label) {
+  respond([label||"start", chunks, startResp]);
+  chunks = [];
+  startResp = {};
+}
+//  Send chunk
+var chunks = [];
+function send(chunk) {
+  chunks.push(chunk.toString());
+};
+
+function blowChunks(label) {
+  respond([label||"chunks", chunks]);
+  chunks = [];
+};
+
+var gotRow = false;
+function getRow() {
+  if (!gotRow) {
+    gotRow = true;
+    sendStart();
+  } else {
+    blowChunks()  
+  }
+  var line = readline();
+  var json = eval(line);
+  if (json[0] == "list_end") return null;
+  if (json[0] != "list_row") {
+    respond({
+      error: "query_server_error",
+      reason: "not a row '" + json[0] + "'"});
+    quit();
+  }
+  return json[1];
+};
+
+////
+////  Render dispatcher
+////
+////
+////
+////
 
 var Render = (function() {
   var row_info;
+  
   return {
-    showDoc : function(funSrc, doc, req) {
+    show : function(funSrc, doc, req) {
       var formFun = compileFunction(funSrc);
-      runRenderFunction(formFun, [doc, req], funSrc);
-    },
-    listBegin : function(head, req) {
-      row_info = { first_key: null, row_number: 0, prev_key: null };
-      runRenderFunction(funs[0], [head, null, req, null], funsrc[0]);
-    },
-    listRow : function(row, req) {
-      if (row_info.first_key == null) {
-        row_info.first_key = row.key;
-      }
-      runRenderFunction(funs[0], [null, row, req, row_info], funsrc[0], true);
-      row_info.prev_key = row.key;
-      row_info.row_number++;
+      runShowRenderFunction(formFun, [doc, req], funSrc, true);
     },
-    listTail : function(req) {
-      runRenderFunction(funs[0], [null, null, req, row_info], funsrc[0]);
+    list : function(head, req) {
+      runListRenderFunction(funs[0], [head, req], funsrc[0]);
     }
   }
 })();
 
-function runRenderFunction(renderFun, args, funSrc, htmlErrors) {
-  responseSent = false;
+function maybeWrapResponse(resp) {
+  var type = typeof resp;
+  if ((type == "string") || (type == "xml")) {
+    return {body:resp};
+  } else {
+    return resp;
+  }
+};
+
+function runShowRenderFunction(renderFun, args, funSrc, htmlErrors) {
   try {
     var resp = renderFun.apply(null, args);
-    if (!responseSent) {
-      if (resp) {
-        respond(maybeWrapResponse(resp));       
-      } else {
-        respond({error:"render_error",reason:"undefined response from render function"});
-      }      
+    if (resp) {
+      respond(["resp", maybeWrapResponse(resp)]);
+    } else {
+      renderError("undefined response from render function");
     }
   } catch(e) {
-    var logMessage = "function raised error: "+e.toString();
-    log(logMessage);
-    // log("stacktrace: "+e.stack);
-    var errorMessage = htmlErrors ? htmlRenderError(e, funSrc) : logMessage;
-    respond({
-      error:"render_error",
-      reason:errorMessage});
+    respondError(e);
+  }
+};
+function runListRenderFunction(renderFun, args, funSrc, htmlErrors) {
+  try {
+    gotRow = false;
+    var resp = renderFun.apply(null, args);
+    if (resp) chunks.push(resp);
+    if (!gotRow) {
+      sendStart("resp");
+    } else {
+      blowChunks("end");      
+    }
+  } catch(e) {
+    respondError(e);
   }
 };
 
+function renderError(m) {
+  respond({error : "render_error", reason : m});
+}
+
+
+function respondError(e) {
+  var logMessage = "function raised error: "+e.toString();
+  log(logMessage);
+  log("stacktrace: "+e.stack);
+  var errorMessage = htmlErrors ? htmlRenderError(e, funSrc) : logMessage;
+  respond({
+    error:"render_error",
+    reason:errorMessage});
+}
+
 function escapeHTML(string) {
   return string.replace(/&/g, "&")
                .replace(/</g, "&lt;")
@@ -241,11 +314,3 @@
   return {body:msg};
 };
 
-function maybeWrapResponse(resp) {
-  var type = typeof resp;
-  if ((type == "string") || (type == "xml")) {
-    return {body:resp};
-  } else {
-    return resp;
-  }
-};

Modified: couchdb/branches/list-iterator/share/server/util.js
URL: http://svn.apache.org/viewvc/couchdb/branches/list-iterator/share/server/util.js?rev=782294&r1=782293&r2=782294&view=diff
==============================================================================
--- couchdb/branches/list-iterator/share/server/util.js (original)
+++ couchdb/branches/list-iterator/share/server/util.js Sat Jun  6 18:18:37 2009
@@ -91,10 +91,8 @@
   }
 }
 
-var responseSent;
 // prints the object as JSON, and rescues and logs any toJSON() related errors
 function respond(obj) {
-  responseSent = true;
   try {
     print(toJSON(obj));  
   } catch(e) {
@@ -103,10 +101,11 @@
 };
 
 log = function(message) {
+  // return;
   if (typeof message == "undefined") {
     message = "Error: attempting to log message of 'undefined'.";
   } else if (typeof message != "string") {
     message = toJSON(message);
   }
-  print(toJSON({log: message}));
+  respond(["log", message]);
 };

Modified: couchdb/branches/list-iterator/share/www/script/test/list_views.js
URL: http://svn.apache.org/viewvc/couchdb/branches/list-iterator/share/www/script/test/list_views.js?rev=782294&r1=782293&r2=782294&view=diff
==============================================================================
--- couchdb/branches/list-iterator/share/www/script/test/list_views.js (original)
+++ couchdb/branches/list-iterator/share/www/script/test/list_views.js Sat Jun  6 18:18:37
2009
@@ -39,106 +39,114 @@
       }
     },
     lists: {
-      simpleForm: stringFun(function(head, row, req, row_info) {
-        if (row) {
-          // we ignore headers on rows and tail
-          return {
-                  body : '\n<li>Key: '+row.key
-                  +' Value: '+row.value
-                  +' LineNo: '+row_info.row_number+'</li>'
-          };
-        } else if (head) {
-          // we return an object (like those used by external and show)
-          // so that we can specify headers
-          return {
-            body : '<h1>Total Rows: '
-              + head.total_rows
-              + ' Offset: ' + head.offset
-              + '</h1><ul>'
-          };
-        } else {
-          // tail
-          return {body : '</ul>'+
-              '<p>FirstKey: '+(row_info ? row_info.first_key : '')+ 
-              ' LastKey: '+(row_info ? row_info.prev_key : '')+'</p>'};
+      basicBasic : stringFun(function(head, req) {
+        send("head");
+        var row;
+        while(row = getRow()) {
+          log("row: "+toJSON(row));
+          send(row.key);        
+        };
+        return "tail";
+      }),
+      basicJSON : stringFun(function(head, req) {
+        start({"headers":{"Content-Type" : "application/json"}}); 
+        send('{"head":'+toJSON(head)+', ');
+        send('"req":'+toJSON(req)+', ');
+        send('"rows":[');
+        var row, sep = '';
+        while (row = getRow()) {
+          send(sep + toJSON(row));
+          sep = ', ';
+        }
+        return "]}";
+      }),
+      simpleForm: stringFun(function(head, req) {
+        log("simpleForm");
+        send('<h1>Total Rows: '
+              // + head.total_rows
+              // + ' Offset: ' + head.offset
+              + '</h1><ul>');
+
+        // rows
+        var row, row_number = 0, prevKey, firstKey = null;
+        while (row = getRow()) {
+          row_number += 1;
+          if (!firstKey) firstKey = row.key;
+          prevKey = row.key;
+          send('\n<li>Key: '+row.key
+          +' Value: '+row.value
+          +' LineNo: '+row_number+'</li>');
         }
+
+        // tail
+        return '</ul><p>FirstKey: '+ firstKey + ' LastKey: '+ prevKey+'</p>';
       }),
-      acceptSwitch: stringFun(function(head, row, req, row_info) {
-        return respondWith(req, {
+      acceptSwitch: stringFun(function(head, req) {
+        // respondWith takes care of setting the proper headers
+        respondWith(req, {
           html : function() {
-            // If you're outputting text and you're not setting
-            // any headers, you can just return a string.
-            if (head) {
-              return "HTML <ul>";
-            } else if (row) {
-              return '\n<li>Key: '
-                +row.key+' Value: '+row.value
-                +' LineNo: '+row_info.row_number+'</li>';
-            } else { // tail
-              return '</ul>';
+            send("HTML <ul>");
 
+            var row;
+            while (row = getRow()) {
+              send('\n<li>Key: '
+                +row.key+' Value: '+row.value
+                +' LineNo: '+row_info.row_number+'</li>');
             }
+
+            // tail
+            return '</ul>';
           },
           xml : function() {
-            if (head) {
-              return '<feed xmlns="http://www.w3.org/2005/Atom">'
-                +'<title>Test XML Feed</title>';
-            } else if (row) {
-              // Becase Safari can't stand to see that dastardly
-              // E4X outside of a string. Outside of tests you
-              // can just use E4X literals.
+            send('<feed xmlns="http://www.w3.org/2005/Atom">'
+              +'<title>Test XML Feed</title>');
+
+            while (row = getRow()) {
               var entry = new XML('<entry/>');
               entry.id = row.id;
               entry.title = row.key;
               entry.content = row.value;
-              // We'll also let you return just an E4X object
-              // if you aren't setting headers.
-              return entry;
-            } else {
-              return "</feed>";
+              send(entry);
             }
+            return "</feed>";
           }
-        })
+        });
       }),
-      qsParams: stringFun(function(head, row, req, row_info) {
-        if(head) return {body: req.query.foo};
-        else return {body: "\n"};
-      }),
-      stopIter: stringFun(function(head, row, req, row_info) {
-        if(head) {
-          return {body: "head"};
-        } else if(row) {
-          if(row_info.row_number > 2) return {stop: true};
-          return {body: " " + row_info.row_number};
-        } else {
-          return {body: " tail"};
-        }
+      qsParams: stringFun(function(req) {
+        return req.query.foo + "\n";
+      }),
+      stopIter: stringFun(function(req) {
+        send("head");
+        var row, row_number = 0;
+        while(row = getRow()) {
+          if(row_number > 2) break;
+          send(" " + row_number);
+          row_number += 1;
+        };
+        return " tail";
       }),
-      stopIter2: stringFun(function(head, row, req, row_info) {
-        return respondWith(req, {
+      stopIter2: stringFun(function(req) {
+        respondWith(req, {
           html: function() {
-            if(head) {
-              return "head";
-            } else if(row) {
-              if(row_info.row_number > 2) return {stop: true};
-              return " " + row_info.row_number;
-            } else {
-              return " tail";
-            }
+            send("head");
+            var row, row_number = 0;
+            while(row = getRow()) {
+              if(row_number > 2) break;
+              send(" " + row_number);
+              row_number += 1;
+            };
+            return " tail";
           }
         });
       }),
-      emptyList: stringFun(function(head, row, req, row_info) {
-        return { body: "" };
+      emptyList: stringFun(function() {
+        return "";
       }),
       rowError : stringFun(function(head, row, req, row_info) {
-        if (head) {
-          return "head";
-        } else if(row) {
-          return missingValue;
-        } else {
-          return "tail"
-        }
+        send("head");
+        var row = getRow();
+        send(fooBarBam); // intentional error
+        return "tail";
       })
     }
   };
@@ -152,26 +160,40 @@
   T(view.total_rows == 10);
   
   // standard get
-  var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView");
+  var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/basicBasic/basicView");
   T(xhr.status == 200, "standard get should be 200");
-  T(/Total Rows/.test(xhr.responseText));
-  T(/Key: 1/.test(xhr.responseText));
-  T(/LineNo: 0/.test(xhr.responseText));
-  T(/LineNo: 5/.test(xhr.responseText));
-  T(/FirstKey: 0/.test(xhr.responseText));
-  T(/LastKey: 9/.test(xhr.responseText));
-
-
-  var lines = xhr.responseText.split('\n');
-  T(/LineNo: 5/.test(lines[6]));
+  T(/head0123456789tail/.test(xhr.responseText));
 
   // test that etags are available
   var etag = xhr.getResponseHeader("etag");
-  xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView",
{
+  xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/basicBasic/basicView",
{
     headers: {"if-none-match": etag}
   });
   T(xhr.status == 304);
 
+  // test the richness of the arguments
+  xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/basicJSON/basicView");
+  T(xhr.status == 200, "standard get should be 200");
+  var resp = JSON.parse(xhr.responseText);
+  TEquals(resp.head, {total_rows:10, offset:0});
+  T(resp.rows.length == 10);
+  TEquals(resp.rows[0], {"id": "0","key": 0,"value": "0"});
+
+  TEquals(resp.req.info.db_name, "test_suite_db");
+  TEquals(resp.req.verb, "GET");
+  TEquals(resp.req.path, [
+      "test_suite_db",
+      "_design",
+      "lists",
+      "_list",
+      "basicJSON",
+      "basicView"
+  ]);
+  T(resp.req.headers.Accept);
+  T(resp.req.headers.Host);
+  T(resp.req.headers["User-Agent"]);
+  T(resp.req.cookie);
+
   // get with query params
   var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView?startkey=3");
   T(xhr.status == 200, "with query params");
@@ -179,13 +201,13 @@
   T(!(/Key: 1/.test(xhr.responseText)));
   T(/FirstKey: 3/.test(xhr.responseText));
   T(/LastKey: 9/.test(xhr.responseText));
-
   
   // with 0 rows
   var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/basicView?startkey=30");
   T(xhr.status == 200, "0 rows");
   T(/Total Rows/.test(xhr.responseText));
-  T(/Offset: null/.test(xhr.responseText));
+  return;
+
 
   // reduce with 0 rows
   var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/simpleForm/withReduce?startkey=30");
@@ -248,6 +270,7 @@
   
   // aborting iteration
   var xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter/basicView");
+  T("content type" == "text/plain");
   T(xhr.responseText.match(/^head 0 1 2 tail$/) && "basic stop");
   xhr = CouchDB.request("GET", "/test_suite_db/_design/lists/_list/stopIter2/basicView");
   T(xhr.responseText.match(/^head 0 1 2 tail$/) && "stop 2");

Modified: couchdb/branches/list-iterator/share/www/script/test/show_documents.js
URL: http://svn.apache.org/viewvc/couchdb/branches/list-iterator/share/www/script/test/show_documents.js?rev=782294&r1=782293&r2=782294&view=diff
==============================================================================
--- couchdb/branches/list-iterator/share/www/script/test/show_documents.js (original)
+++ couchdb/branches/list-iterator/share/www/script/test/show_documents.js Sat Jun  6 18:18:37
2009
@@ -106,9 +106,7 @@
         registerType("foo", "application/foo","application/x-foo");
         return respondWith(req, {
           html : function() {
-            return {
-              body:"Ha ha, you said \"" + doc.word + "\"."
-            };
+            return "Ha ha, you said \"" + doc.word + "\".";
           },
           xml : function() {
             var xml = new XML('<xml><node/></xml>');
@@ -145,10 +143,14 @@
   // hello template world
   xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/hello/"+docid);
   T(xhr.responseText == "Hello World");
-
-  // error stacktraces
-  xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/render-error/"+docid);
-  T(JSON.parse(xhr.responseText).error == "render_error");
+// 
+// };
+// 
+// function foo() {
+
+  // // error stacktraces
+  // xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/render-error/"+docid);
+  // T(JSON.parse(xhr.responseText).error == "render_error");
  
   // hello template world (no docid)
   xhr = CouchDB.request("GET", "/test_suite_db/_design/template/_show/hello");

Modified: couchdb/branches/list-iterator/src/couchdb/couch_httpd.erl
URL: http://svn.apache.org/viewvc/couchdb/branches/list-iterator/src/couchdb/couch_httpd.erl?rev=782294&r1=782293&r2=782294&view=diff
==============================================================================
--- couchdb/branches/list-iterator/src/couchdb/couch_httpd.erl (original)
+++ couchdb/branches/list-iterator/src/couchdb/couch_httpd.erl Sat Jun  6 18:18:37 2009
@@ -177,6 +177,14 @@
             % ?LOG_DEBUG("Minor error in HTTP request: ~p",[Error]),
             % ?LOG_DEBUG("Stacktrace: ~p",[erlang:get_stacktrace()]),
             send_error(HttpReq, Error);
+        error:badarg ->
+            ?LOG_ERROR("Badarg error in HTTP request",[]),
+            ?LOG_INFO("Stacktrace: ~p",[erlang:get_stacktrace()]),
+            send_error(HttpReq, badarg);
+        error:function_clause ->
+            ?LOG_ERROR("function_clause error in HTTP request",[]),
+            ?LOG_INFO("Stacktrace: ~p",[erlang:get_stacktrace()]),
+            send_error(HttpReq, function_clause);
         Tag:Error ->
             ?LOG_ERROR("Uncaught error in HTTP request: ~p",[{Tag, Error}]),
             ?LOG_INFO("Stacktrace: ~p",[erlang:get_stacktrace()]),

Modified: couchdb/branches/list-iterator/src/couchdb/couch_httpd_show.erl
URL: http://svn.apache.org/viewvc/couchdb/branches/list-iterator/src/couchdb/couch_httpd_show.erl?rev=782294&r1=782293&r2=782294&view=diff
==============================================================================
--- couchdb/branches/list-iterator/src/couchdb/couch_httpd_show.erl (original)
+++ couchdb/branches/list-iterator/src/couchdb/couch_httpd_show.erl Sat Jun  6 18:18:37 2009
@@ -119,37 +119,34 @@
 
 make_map_start_resp_fun(QueryServer, Db) ->
     fun(Req, CurrentEtag, TotalViewCount, Offset, _Acc) ->
-        ExternalResp = couch_query_servers:render_list_head(QueryServer, 
+        [_,Chunks,ExternalResp] = couch_query_servers:render_list_head(QueryServer, 
             Req, Db, TotalViewCount, Offset),
         JsonResp = apply_etag(ExternalResp, CurrentEtag),
         #extern_resp_args{
             code = Code,
-            data = BeginBody,
             ctype = CType,
             headers = ExtHeaders
         } = couch_httpd_external:parse_external_response(JsonResp),
         JsonHeaders = couch_httpd_external:default_or_content_type(CType, ExtHeaders),
         {ok, Resp} = start_chunked_response(Req, Code, JsonHeaders),
-        {ok, Resp, binary_to_list(BeginBody)}
+        {ok, Resp, ?b2l(?l2b(Chunks))}
     end.
 
 make_map_send_row_fun(QueryServer, Req) ->
     fun(Resp, Db2, {{Key, DocId}, Value}, _IncludeDocs, RowFront) ->
         try
-            JsonResp = couch_query_servers:render_list_row(QueryServer, 
+            [<<"chunks">>,Chunks] = couch_query_servers:render_list_row(QueryServer,

                 Req, Db2, {{Key, DocId}, Value}),
-            #extern_resp_args{
-                stop = StopIter,
-                data = RowBody
-            } = couch_httpd_external:parse_external_response(JsonResp),
-            case StopIter of
-            true -> {stop, ""};
+            case {false, 5} of
+            {_, 0} -> 
+                {stop, ""};
+            {true, _} -> 
+                Chunk = RowFront ++ ?b2l(?l2b(Chunks)),
+                send_non_empty_chunk(Resp, Chunk),
+                {stop, ""};
             _ ->
-                Chunk = RowFront ++ binary_to_list(RowBody),
-                case Chunk of
-                    [] -> ok;
-                    _ -> send_chunk(Resp, Chunk)
-                end,
+                Chunk = RowFront ++ ?b2l(?l2b(Chunks)),
+                send_non_empty_chunk(Resp, Chunk),
                 {ok, ""}
             end
         catch
@@ -159,6 +156,12 @@
         end
     end.
 
+send_non_empty_chunk(Resp, Chunk) ->
+    case Chunk of
+        [] -> ok;
+        _ -> send_chunk(Resp, Chunk)
+    end.
+
 output_map_list(#httpd{mochi_req=MReq}=Req, Lang, ListSrc, View, Group, Db, QueryArgs, nil)
->
     #view_query_args{
         limit = Limit,
@@ -350,19 +353,14 @@
         {_, _, Resp0, _} ->
             {Resp0, ""}
     end,
-    JsonTail = couch_query_servers:render_list_tail(QueryServer, Req, Db),
-    #extern_resp_args{
-        data = Tail
-    } = couch_httpd_external:parse_external_response(JsonTail),
-    Chunk = BeginBody ++ binary_to_list(Tail),
+    [<<"end">>, Chunks] = couch_query_servers:render_list_tail(QueryServer),
+    Chunk = BeginBody ++ ?b2l(?l2b(Chunks)),
     case Chunk of
         [] -> ok;
         _ -> send_chunk(Resp, Chunk)
     end,
     send_chunk(Resp, []).
 
-render_head_for_empty_list(StartListRespFun, Req, Etag, null) ->
-    StartListRespFun(Req, Etag, []);
 render_head_for_empty_list(StartListRespFun, Req, Etag, TotalRows) ->
     StartListRespFun(Req, Etag, TotalRows, null, []).
     
@@ -373,7 +371,7 @@
     Accept = proplists:get_value('Accept', Hlist),
     CurrentEtag = couch_httpd:make_etag({Lang, ShowSrc, nil, Accept}),
     couch_httpd:etag_respond(Req, CurrentEtag, fun() -> 
-        ExternalResp = couch_query_servers:render_doc_show(Lang, ShowSrc, 
+        [<<"resp">>, ExternalResp] = couch_query_servers:render_doc_show(Lang,
ShowSrc, 
             DocId, nil, Req, Db),
         JsonResp = apply_etag(ExternalResp, CurrentEtag),
         couch_httpd_external:send_external_response(Req, JsonResp)
@@ -387,7 +385,7 @@
     CurrentEtag = couch_httpd:make_etag({Lang, ShowSrc, Revs, Accept}),
     % We know our etag now    
     couch_httpd:etag_respond(Req, CurrentEtag, fun() -> 
-        ExternalResp = couch_query_servers:render_doc_show(Lang, ShowSrc, 
+        [<<"resp">>, ExternalResp] = couch_query_servers:render_doc_show(Lang,
ShowSrc, 
             DocId, Doc, Req, Db),
         JsonResp = apply_etag(ExternalResp, CurrentEtag),
         couch_httpd_external:send_external_response(Req, JsonResp)

Modified: couchdb/branches/list-iterator/src/couchdb/couch_js.c
URL: http://svn.apache.org/viewvc/couchdb/branches/list-iterator/src/couchdb/couch_js.c?rev=782294&r1=782293&r2=782294&view=diff
==============================================================================
--- couchdb/branches/list-iterator/src/couchdb/couch_js.c (original)
+++ couchdb/branches/list-iterator/src/couchdb/couch_js.c Sat Jun  6 18:18:37 2009
@@ -247,13 +247,13 @@
 
 static JSBool
 Print(JSContext *context, JSObject *obj, uintN argc, jsval *argv, jsval *rval) {
-    uintN i, n;
+    uintN i;
     size_t cl, bl;
     JSString *str;
     jschar *chars;
     char *bytes;
 
-    for (i = n = 0; i < argc; i++) {
+    for (i = 0; i < argc; i++) {
         str = JS_ValueToString(context, argv[i]);
         if (!str)
             return JS_FALSE;
@@ -270,9 +270,8 @@
         fprintf(stdout, "%s%s", i ? " " : "", bytes);
         JS_free(context, bytes);
     }
-    n++;
-    if (n)
-        fputc('\n', stdout);
+
+    fputc('\n', stdout);
     fflush(stdout);
     return JS_TRUE;
 }

Modified: couchdb/branches/list-iterator/src/couchdb/couch_os_process.erl
URL: http://svn.apache.org/viewvc/couchdb/branches/list-iterator/src/couchdb/couch_os_process.erl?rev=782294&r1=782293&r2=782294&view=diff
==============================================================================
--- couchdb/branches/list-iterator/src/couchdb/couch_os_process.erl (original)
+++ couchdb/branches/list-iterator/src/couchdb/couch_os_process.erl Sat Jun  6 18:18:37 2009
@@ -82,9 +82,9 @@
 readjson(OsProc) when is_record(OsProc, os_proc) ->
     Line = readline(OsProc),
     case ?JSON_DECODE(Line) of
-    {[{<<"log">>,Msg}]} when is_binary(Msg) ->
+    [<<"log">>, Msg] when is_binary(Msg) ->
         % we got a message to log. Log it and continue
-        ?LOG_INFO("OS Process Log Message: ~s", [Msg]),
+        ?LOG_INFO("OS Process :: ~s", [Msg]),
         readjson(OsProc);
     {[{<<"error">>, Id}, {<<"reason">>, Reason}]} ->
         throw({list_to_atom(binary_to_list(Id)),Reason});

Modified: couchdb/branches/list-iterator/src/couchdb/couch_query_servers.erl
URL: http://svn.apache.org/viewvc/couchdb/branches/list-iterator/src/couchdb/couch_query_servers.erl?rev=782294&r1=782293&r2=782294&view=diff
==============================================================================
--- couchdb/branches/list-iterator/src/couchdb/couch_query_servers.erl (original)
+++ couchdb/branches/list-iterator/src/couchdb/couch_query_servers.erl Sat Jun  6 18:18:37
2009
@@ -19,7 +19,7 @@
 -export([start_doc_map/2, map_docs/2, stop_doc_map/1]).
 -export([reduce/3, rereduce/3,validate_doc_update/5]).
 -export([render_doc_show/6,start_view_list/2,render_list_head/5, 
-        render_list_row/4, render_list_tail/3, render_reduce_head/3, 
+        render_list_row/4, render_list_tail/1, render_reduce_head/3, 
         render_reduce_row/4]).
 % -export([test/0]).
 
@@ -183,7 +183,7 @@
         _ -> {{append_docid(DocId, JsonReqIn)}, couch_doc:to_json_obj(Doc, [revs])}
     end,
     try couch_os_process:prompt(Pid, 
-        [<<"show_doc">>, ShowSrc, JsonDoc, JsonReq]) of
+        [<<"show">>, ShowSrc, JsonDoc, JsonReq]) of
     FormResp ->
         FormResp
     after
@@ -198,19 +198,17 @@
 render_list_head({_Lang, Pid}, Req, Db, TotalRows, Offset) ->
     Head = {[{<<"total_rows">>, TotalRows}, {<<"offset">>, Offset}]},
     JsonReq = couch_httpd_external:json_req_obj(Req, Db),
-    couch_os_process:prompt(Pid, [<<"list_begin">>, Head, JsonReq]).
+    couch_os_process:prompt(Pid, [<<"list">>, Head, JsonReq]).
 
 render_list_row({_Lang, Pid}, Req, Db, {{Key, DocId}, Value}) ->
     JsonRow = couch_httpd_view:view_row_obj(Db, {{Key, DocId}, Value}, false),
     JsonReq = couch_httpd_external:json_req_obj(Req, Db),
     couch_os_process:prompt(Pid, [<<"list_row">>, JsonRow, JsonReq]).
 
-render_list_tail({Lang, Pid}, Req, Db) ->
-    JsonReq = couch_httpd_external:json_req_obj(Req, Db),
-    JsonResp = couch_os_process:prompt(Pid, [<<"list_tail">>, JsonReq]),
+render_list_tail({Lang, Pid}) ->
+    JsonResp = couch_os_process:prompt(Pid, [<<"list_end">>]),
     ok = ret_os_process(Lang, Pid),
-    JsonResp.
-    
+    JsonResp.    
     
 render_reduce_head({_Lang, Pid}, Req, Db) ->
     Head = {[]},

Modified: couchdb/branches/list-iterator/test/query_server_spec.rb
URL: http://svn.apache.org/viewvc/couchdb/branches/list-iterator/test/query_server_spec.rb?rev=782294&r1=782293&r2=782294&view=diff
==============================================================================
--- couchdb/branches/list-iterator/test/query_server_spec.rb (original)
+++ couchdb/branches/list-iterator/test/query_server_spec.rb Sat Jun  6 18:18:37 2009
@@ -22,31 +22,6 @@
 require 'json'
 
 
-JSON_REQ = {
-  "body"=>"undefined", 
-  "verb"=>"GET", 
-  "info"=>{
-    "disk_format_version"=>2, 
-    "purge_seq"=>0, 
-    "doc_count"=>9082, 
-    "instance_start_time"=>"1243713611467271", 
-    "update_seq"=>9512, 
-    "disk_size"=>27541604, 
-    "compact_running"=>false, 
-    "db_name"=>"toast", 
-    "doc_del_count"=>1
-  }, 
-  "cookie"=>{}, 
-  "form"=>{}, 
-  "query"=>{"q"=>"stuff"}, 
-  "path"=>["toast", "_ext"], 
-  "headers"=>{
-    "User-Agent"=>"curl/7.18.1 (i386-apple-darwin9.2.2) libcurl/7.18.1 zlib/1.2.3", 
-    "Host"=>"localhost:5984", 
-    "Accept"=>"*/*"
-  }
-}
-
 class OSProcessRunner
   def self.run
     trace = false
@@ -78,9 +53,9 @@
   def add_fun(fun)
     run(["add_fun", fun])
   end
-  def get_chunk
+  def get_chunks
     resp = jsgets
-    raise "not a chunk" unless resp.first == "chunk"
+    raise "not a chunk" unless resp.first == "chunks"
     return resp[1]
   end
   def run json
@@ -103,10 +78,10 @@
     # puts "err: #{err}" if err
     if resp
       rj = JSON.parse("[#{resp.chomp}]")[0]
-      if rj.respond_to?(:[]) && !rj.is_a?(Array) && 
-        if rj["log"]
-          log = rj["log"]
-          puts "log: #{log}" #if @trace
+      if rj.respond_to?(:[]) && rj.is_a?(Array)
+        if rj[0] == "log"
+          log = rj[1]
+          puts "log: #{log}" if @trace
           rj = jsgets
         end
       end
@@ -177,131 +152,284 @@
       @qs.run(["rereduce", [@fun], vs]).should == [true, [45]]
     end
   end
+  
   # it "should validate"
+  describe "validation" do
+    before(:all) do
+      @fun = <<-JS
+        function(newDoc, oldDoc, userCtx) {
+          if (newDoc.bad) throw({forbidden:"bad doc"});
+          "foo bar";
+        }
+        JS
+      @qs.reset!
+    end
+    it "should allow good updates" do
+      @qs.run(["validate", @fun, {"good" => true}, {}, {}]).should == 1
+    end
+    it "should reject invalid updates" do
+      @qs.run(["validate", @fun, {"bad" => true}, {}, {}]).should == {"forbidden"=>"bad
doc"}
+    end
+  end
   
   describe "show" do
-     before(:all) do
-       @fun = <<-JS
-         function(doc, req) {
-           return [doc.title, doc.body].join(' - ')
-         }
-         JS
-       @qs.reset!
-     end
-     it "should show" do
-       @qs.rrun(["show_doc", @fun, 
-         {:title => "Best ever", :body => "Doc body"}, JSON_REQ])
-       @qs.jsgets.should == {"body"=>"Best ever - Doc body"}
-     end
-   end
-   
-   describe "show with headers" do
-     before(:all) do
-       @fun = <<-JS
-         function(doc, req) {
-           return {
-             headers : {"X-Plankton":"Rusty"},
-             body : [doc.title, doc.body].join(' - ')
-           }
-           
-         }
-         JS
-       @qs.reset!
-     end
-     it "should show" do
-       @qs.rrun(["show_doc", @fun, 
-         {:title => "Best ever", :body => "Doc body"}])
-       @qs.jsgets.should == {"headers"=>{"X-Plankton"=>"Rusty"}, "body"=>"Best ever
- Doc body"}
-     end
-   end
-     
-   describe "list with headers" do
-     before(:each) do
-       @fun = <<-JS
-         function(head, row, req) {
-           if (head) return {headers : {"Content-Type" : "text/plain"}, code : 200, "body"
: "foo"};
-           if (row) return 'some "text" here';
-           return "tail";
-         };
-         JS
-       @qs.reset!
-       @qs.add_fun(@fun).should == true
-     end
-     it "should send head, row, and tail" do
-       @qs.rrun(["list_begin", {"total_rows"=>1000}, {"q" => "ok"}])
-       @qs.jsgets.should == {"headers"=>{"Content-Type"=>"text/plain"}, "code"=>200,
"body"=>"foo"}
-       @qs.run(["list_row", {"foo"=>"bar"}, {"q" => "ok"}]).should == {"body"=>"some
\"text\" here"}
-       @qs.run(["list_tail", {"foo"=>"bar"}, {"q" => "ok"}]).should == {"body"=>"tail"}
-     end
-   end
-   
-   describe "list with headers and rows" do
-     before(:each) do
-       @fun = <<-JS
-         function(head, row, req) {
-           if (head) return {headers : {"Content-Type" : "text/plain"}, code : 200, "body"
: "foo"};
-           if (row) return 'row value '+row.value;
-           return "tail "+req.q;
-         };
-         JS
-       @qs.reset!
-       @qs.add_fun(@fun).should == true
-     end
-     it "should render rows" do
-       @qs.rrun(["list_begin", {"total_rows"=>1000}, {"q" => "ok"}])
-       @qs.jsgets.should == {"headers"=>{"Content-Type"=>"text/plain"}, "code"=>200,
"body"=>"foo"}
-       @qs.run(["list_row", {"value"=>"bar"}, {"q" => "ok"}]).should == {"body"=>"row
value bar"}
-       @qs.run(["list_row", {"value"=>"baz"}, {"q" => "ok"}]).should == {"body"=>"row
value baz"}
-       @qs.run(["list_row", {"value"=>"bam"}, {"q" => "ok"}]).should == {"body"=>"row
value bam"}
-       @qs.run(["list_tail", {"q" => "ok"}]).should == {"body"=>"tail ok"}
-     end
-   end
- end # query server normal case
+    before(:all) do
+      @fun = <<-JS
+        function(doc, req) {
+          log("ok");
+          return [doc.title, doc.body].join(' - ');
+        }
+        JS
+      @qs.reset!
+    end
+    it "should show" do
+      @qs.rrun(["show", @fun, 
+        {:title => "Best ever", :body => "Doc body"}])
+      @qs.jsgets.should == ["resp", {"body" => "Best ever - Doc body"}]
+    end
+  end
+  
+  describe "show with headers" do
+    before(:all) do
+      @fun = <<-JS
+        function(doc, req) {
+          var resp = {"code":200, "headers":{"X-Plankton":"Rusty"}};
+          resp.body = [doc.title, doc.body].join(' - ');
+          return resp;
+        }
+        JS
+      @qs.reset!
+    end
+    it "should show headers" do
+      @qs.rrun(["show", @fun, 
+        {:title => "Best ever", :body => "Doc body"}])
+      @qs.jsgets.should == ["resp", {"code"=>200,"headers" => {"X-Plankton"=>"Rusty"},
"body" => "Best ever - Doc body"}]
+    end
+  end
+    
+# end
+#                    LIST TESTS
+# __END__
+    
+  describe "raw list with headers" do
+    before(:each) do
+      @fun = <<-JS
+        function(head, req) {
+          start({headers:{"Content-Type" : "text/plain"}});
+          send("first chunk");
+          send('second "chunk"');
+          return "tail";
+        };
+        JS
+      @qs.reset!
+      @qs.add_fun(@fun).should == true
+    end
+    it "should do headers proper" do
+      @qs.rrun(["list", {"total_rows"=>1000}, {"q" => "ok"}])
+      @qs.jsgets.should == ["resp", ["first chunk", 'second "chunk"', "tail"], {"headers"=>{"Content-Type"=>"text/plain"}}]
+    end
+  end
+  
+  describe "list with rows" do
+    before(:each) do
+      @fun = <<-JS
+        function(head, req) {
+          send("first chunk");
+          send(req.q);
+          var row;
+          log("about to getRow " + typeof(getRow));
+          while(row = getRow()) {
+            send(row.key);        
+          };
+          return "tail";
+        };
+        JS
+      @qs.run(["reset"]).should == true    
+      @qs.add_fun(@fun).should == true
+    end
+    it "should should list em" do
+      @qs.rrun(["list", {"foo"=>"bar"}, {"q" => "ok"}])
+      @qs.jsgets.should == ["start", ["first chunk", "ok"], {}]
+      @qs.rrun(["list_row", {"key"=>"baz"}])
+      @qs.get_chunks.should == ["baz"]
+      @qs.rrun(["list_row", {"key"=>"bam"}])
+      @qs.get_chunks.should == ["bam"]
+      @qs.rrun(["list_end"])
+      @qs.jsgets.should == ["end", ["tail"]]
+    end
+    it "should work with zero rows" do
+      @qs.rrun(["list", {"foo"=>"bar"}, {"q" => "ok"}])
+      @qs.jsgets.should == ["start", ["first chunk", "ok"], {}]
+      @qs.rrun(["list_end"])
+      @qs.jsgets.should == ["end", ["tail"]]
+    end
+  end
+  
+  describe "should buffer multiple chunks sent for a single row." do
+    before(:all) do
+      @fun = <<-JS
+        function(head, req) {
+          send("bacon");
+          var row;
+          log("about to getRow " + typeof(getRow));
+          while(row = getRow()) {
+            send(row.key);        
+            send("eggs");        
+          };
+          return "tail";
+        };
+        JS
+      @qs.reset!
+      @qs.add_fun(@fun).should == true
+    end
+    it "should should buffer em" do
+      @qs.rrun(["list", {"foo"=>"bar"}, {"q" => "ok"}])
+      @qs.jsgets.should == ["start", ["bacon"], {}]
+      @qs.rrun(["list_row", {"key"=>"baz"}])
+      @qs.get_chunks.should == ["baz", "eggs"]
+      @qs.rrun(["list_row", {"key"=>"bam"}])
+      @qs.get_chunks.should == ["bam", "eggs"]
+      @qs.rrun(["list_end"])
+      @qs.jsgets.should == ["end", ["tail"]]
+    end
+  end
 
- describe "query server errors" do
-   before(:each) do
-     @qs = QueryServerRunner.run
-   end
-   after(:each) do
-     @qs.close
-   end
-   
-   describe "list" do
-     before(:each) do
-       @fun = <<-JS
-         function(head, row, req) {
-           if (head) return {headers : {"Content-Type" : "text/plain"}, code : 200, "body"
: "foo"};
-           if (row) return 'row value '+row.value;
-           return "tail "+req.q;
-         };
-         JS
-       @qs.run(["reset"]).should == true    
-       @qs.add_fun(@fun).should == true
-     end
-     it "should reset in the middle" do
-       @qs.rrun(["list_begin", {"total_rows"=>1000}, {"q" => "ok"}])
-       @qs.jsgets.should == {"headers"=>{"Content-Type"=>"text/plain"}, "code"=>200,
"body"=>"foo"}
-       @qs.run(["list_row", {"value"=>"bar"}, {"q" => "ok"}]).should == {"body"=>"row
value bar"}
-       @qs.run(["reset"]).should == true
-     end
-   end  
- end #query server that errors
+  describe "example list" do
+    before(:all) do
+      @fun = <<-JS
+        function(head, req) {
+          send("first chunk");
+          send(req.q);
+          var row;
+          while(row = getRow()) {
+            send(row.key);    
+          };
+          return "early";
+        };
+        JS
+      @qs.reset!
+      @qs.add_fun(@fun).should == true
+    end
+    it "should run normal" do
+      @qs.run(["list", {"foo"=>"bar"}, {"q" => "ok"}]).should == ["start", ["first
chunk", "ok"], {}]
+      @qs.run(["list_row", {"key"=>"baz"}]).should ==  ["chunks", ["baz"]]
+      @qs.run(["list_row", {"key"=>"bam"}]).should ==  ["chunks", ["bam"]]
+      @qs.run(["list_row", {"key"=>"foom"}]).should == ["chunks", ["foom"]]
+      @qs.run(["list_row", {"key"=>"fooz"}]).should == ["chunks", ["fooz"]]
+      @qs.run(["list_row", {"key"=>"foox"}]).should == ["chunks", ["foox"]]
+      @qs.run(["list_end"]).should == ["end" , ["early"]]
+    end
+  end
+  
+  describe "only goes to 2 list" do
+    before(:all) do
+      @fun = <<-JS
+        function(head, req) {
+          send("first chunk");
+          send(req.q);
+          var row, i=0;
+          while(row = getRow()) {
+            send(row.key);  
+            i += 1;
+            if (i > 2) {
+              return('early tail');
+            }  
+          };
+        };
+        JS
+      @qs.reset!
+      @qs.add_fun(@fun).should == true
+    end
+    it "should end early" do
+      @qs.run(["list", {"foo"=>"bar"}, {"q" => "ok"}]).
+        should == ["start", ["first chunk", "ok"], {}]
+      @qs.run(["list_row", {"key"=>"baz"}]).
+        should ==  ["chunks", ["baz"]]
 
-## tests for the generic "echo" external
- 
-# describe "running an external" do
-#   before(:all) do
-#     @ext = ExternalRunner.run
-#     
-#   end
-#   it "should respond to 'info'" do
-#     @ext.rrun(['info'])
-#     @ext.jsgets.should == ["info", "echo", "external server that prints its arguments as
JSON"]
-#   end
-#   it "should echo the request" do
+      @qs.run(["list_row", {"key"=>"bam"}]).
+        should ==  ["chunks", ["bam"]]
 
-#     @ext.rrun(['req', req_obj])
-#     @ext.jsgets.should == ["x"]
-#   end
-# end
-# 
+      @qs.run(["list_row", {"key"=>"foom"}]).
+        should == ["end", ["foom", "early tail"]]
+      # here's where js has to discard quit properly
+      @qs.run(["reset"]).
+        should == true
+    end
+  end
+end
+
+describe "query server that exits" do
+  before(:each) do
+    @qs = QueryServerRunner.run
+  end
+  after(:each) do
+    @qs.close
+  end
+  
+  describe "only goes to 2 list" do
+    before(:each) do
+      @fun = <<-JS
+        function(head, req) {
+          send("bacon")
+          var row, i = 0;
+          while(row = getRow()) {
+            send(row.key);        
+            i += 1;
+            if (i > 2) {
+              return('early');
+            }
+          };
+        }
+        JS
+      @qs.reset!
+      @qs.add_fun(@fun).should == true
+    end
+    it "should exit if erlang sends too many rows" do
+      @qs.run(["list", {"foo"=>"bar"}, {"q" => "ok"}]).should == ["start", ["bacon"],
{}]
+      @qs.run(["list_row", {"key"=>"baz"}]).should ==  ["chunks", ["baz"]]
+      @qs.run(["list_row", {"key"=>"foom"}]).should == ["chunks", ["foom"]]
+      @qs.run(["list_row", {"key"=>"fooz"}]).should == ["end", ["fooz", "early"]]
+      @qs.rrun(["list_row", {"key"=>"foox"}])
+      @qs.jsgets["error"].should == "query_server_error"
+      begin
+        @qs.run(["reset"])
+        "raise before this".should == true
+      rescue RuntimeError => e
+        e.message.should == "no response"
+      rescue Errno::EPIPE
+        true.should == true
+      end
+    end
+  end
+  
+  describe "raw list" do
+    before(:each) do
+      @fun = <<-JS
+        function(head, req) {
+          send("first chunk");
+          send(req.q);
+          var row;
+          while(row = getRow()) {
+            send(row.key);        
+          };
+          return "tail";
+        };
+        JS
+      @qs.run(["reset"]).should == true    
+      @qs.add_fun(@fun).should == true
+    end
+    it "should exit if it gets a non-row in the middle" do
+      @qs.rrun(["list", {"foo"=>"bar"}, {"q" => "ok"}])
+      @qs.jsgets.should == ["start", ["first chunk", "ok"], {}]
+      @qs.run(["reset"])["error"].should == "query_server_error"
+      begin
+        @qs.run(["reset"])
+        "raise before this".should == true
+      rescue RuntimeError => e
+        e.message.should == "no response"
+      rescue Errno::EPIPE
+        true.should == true
+      end
+    end
+  end  
+end



Mime
View raw message