diff options
-rw-r--r-- | share/server/loop.js | 2 | ||||
-rw-r--r-- | share/server/render.js | 7 | ||||
-rw-r--r-- | share/server/util.js | 55 | ||||
-rw-r--r-- | share/www/script/test/design_docs.js | 18 |
4 files changed, 76 insertions, 6 deletions
diff --git a/share/server/loop.js b/share/server/loop.js index 800d4440..300151e9 100644 --- a/share/server/loop.js +++ b/share/server/loop.js @@ -74,7 +74,7 @@ var DDoc = (function() { if (i+1 == funPath.length) { fun = point[funPath[i]] if (typeof fun != "function") { - fun = Couch.compileFunction(fun); + fun = Couch.compileFunction(fun, ddoc); // cache the compiled fun on the ddoc point[funPath[i]] = fun }; diff --git a/share/server/render.js b/share/server/render.js index 1e341ec7..9dcfbcd6 100644 --- a/share/server/render.js +++ b/share/server/render.js @@ -210,6 +210,11 @@ var Render = (function() { return s; }; + function isDocRequestPath(info) { + var path = info.path; + return path.length > 5; + }; + function runShow(fun, ddoc, args) { try { resetList(); @@ -239,7 +244,7 @@ var Render = (function() { throw(["error", "render_error", "undefined response from show function"]); } } catch(e) { - if(args[0] === null) { + if (args[0] === null && isDocRequestPath(args[1])) { throw(["error", "not_found", "document not found"]); } else { renderError(e, fun.toSource()); diff --git a/share/server/util.js b/share/server/util.js index c2e6600f..d017b094 100644 --- a/share/server/util.js +++ b/share/server/util.js @@ -10,15 +10,66 @@ // License for the specific language governing permissions and limitations under // the License. +var resolveModule = function(names, parent, current) { + if (names.length == 0) { + if (typeof current != "string") { + throw ["error","invalid_require_path", + 'Must require a JavaScript string, not: '+(typeof current)]; + } + return [current, parent]; + } + // we need to traverse the path + var n = names.shift(); + if (n == '..') { + if (!(parent && parent.parent)) { + throw ["error", "invalid_require_path", 'Object has no parent '+JSON.stringify(current)]; + } + return resolveModule(names, parent.parent.parent, parent.parent); + } else if (n == '.') { + if (!parent) { + throw ["error", "invalid_require_path", 'Object has no parent '+JSON.stringify(current)]; + } + return resolveModule(names, parent.parent, parent); + } + if (!current[n]) { + throw ["error", "invalid_require_path", 'Object has no property "'+n+'". '+JSON.stringify(current)]; + } + var p = current + current = current[n]; + current.parent = p; + return resolveModule(names, p, current) +} + var Couch = { // moving this away from global so we can move to json2.js later toJSON : function (val) { return JSON.stringify(val); }, - compileFunction : function(source) { + compileFunction : function(source, ddoc) { if (!source) throw(["error","not_found","missing function"]); try { - var functionObject = sandbox ? evalcx(source, sandbox) : eval(source); + if (sandbox) { + if (ddoc) { + var require = function(name, parent) { + var exports = {}; + var resolved = resolveModule(name.split('/'), parent, ddoc); + var source = resolved[0]; + parent = resolved[1]; + var s = "function (exports, require) { " + source + " }"; + try { + var func = sandbox ? evalcx(s, sandbox) : eval(s); + func.apply(sandbox, [exports, function(name) {return require(name, parent, source)}]); + } catch(e) { + throw ["error","compilation_error","Module require('"+name+"') raised error "+e.toSource()]; + } + return exports; + } + sandbox.require = require; + } + var functionObject = evalcx(source, sandbox); + } else { + var functionObject = eval(source); + } } catch (err) { throw(["error", "compilation_error", err.toSource() + " (" + source + ")"]); }; diff --git a/share/www/script/test/design_docs.js b/share/www/script/test/design_docs.js index 9318d2bc..efc2e718 100644 --- a/share/www/script/test/design_docs.js +++ b/share/www/script/test/design_docs.js @@ -38,6 +38,13 @@ function() { var designDoc = { _id:"_design/test", // turn off couch.js id escaping? language: "javascript", + whatever : { + stringzone : "exports.string = 'plankton';", + commonjs : { + whynot : "exports.test = require('../stringzone')", + upper : "exports.testing = require('./whynot').test.string.toUpperCase()" + } + }, views: { all_docs_twice: {map: "function(doc) { emit(doc.integer, null); emit(doc.integer, null) }"}, no_docs: {map: "function(doc) {}"}, @@ -50,9 +57,11 @@ function() { reduce:"function (keys, values) { return \"" + makebigstring(16) + "\"; };"} }, shows: { - simple: "function() {return 'ok'};" + simple: "function() {return 'ok'};", + requirey : "function() { var lib = require('whatever/commonjs/upper'); return lib.testing; };" } - } + }; + var xhr = CouchDB.request("PUT", "/test_suite_db_a/_design/test", {body: JSON.stringify(designDoc)}); var resp = JSON.parse(xhr.responseText); @@ -74,6 +83,11 @@ function() { T(xhr.status == 200); TEquals("ok", xhr.responseText, 'query server used wrong ddoc'); + // test commonjs require + var xhr = CouchDB.request("GET", "/test_suite_db/_design/test/_show/requirey"); + T(xhr.status == 200); + TEquals("PLANKTON", xhr.responseText); + // test that we get design doc info back var dinfo = db.designInfo("_design/test"); TEquals("test", dinfo.name); |