Skip to content
This repository has been archived by the owner on Jun 21, 2022. It is now read-only.

Bug 747160 memcache #9

Merged
merged 2 commits into from Apr 26, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
66 changes: 60 additions & 6 deletions lib/kumascript/api.js
Expand Up @@ -28,6 +28,7 @@ var util = require('util'),
Future = require('fibers/future'),
wait = Future.wait,
request = require('request'),
Memcached = require('memcached'),

ks_macros = require(__dirname + '/macros'),
ks_utils = require(__dirname + '/utils');
Expand Down Expand Up @@ -103,6 +104,24 @@ var PageAPI = ks_utils.Class(BaseAPI, {

});

// ### FakeMemcached
//
// A minimal stub replacement for Memcached, in case it's missing from the
// config. That way, kumascript can be used without memcache, even if that's
// not recommended.
var FakeMemcached = ks_utils.Class({
initialize: function (options) {
this._cache = {};
},
set: function (key, value, tm_out, next) {
this._cache[key] = value;
next(null, true);
},
get: function (key, next) {
next(null, this._cache[key]);
}
});

// ### APIContext
//
// Instances of this class manage instances of sub-APIs, supplying them with
Expand All @@ -111,6 +130,9 @@ var PageAPI = ks_utils.Class(BaseAPI, {
var APIContext = ks_utils.Class({

default_options: {
server_options: {},
env: {},
source: '',
apis: {
kuma: KumaAPI
}
Expand All @@ -123,8 +145,17 @@ var APIContext = ks_utils.Class({
_.each(this.options.apis, _.bind(this.installAPI, this));

// Make the env vars more easily used, if given
if (options && options.env) {
this.env = options.env;
if (this.options && this.options.env) {
this.env = this.options.env;
}

// Create a memcache instance, if necessary
if (this.options.server_options.memcache) {
var mo = this.options.server_options.memcache;
this.memcached = new Memcached(mo.server, mo.options || {});
} else {
// If the configuration is missing, use the fake stub cache
this.memcached = new FakeMemcached();
}

// Create a new cache for required templates.
Expand Down Expand Up @@ -167,18 +198,19 @@ var APIContext = ks_utils.Class({
// Auto-require some templates and install the exports as APIs. This is
// kind of a hack, but I wanted to use the require method.
performAutoRequire: function (next) {
var $this = this;
var $this = this,
server_options = $this.options.server_options;

// Skip this whole thing, if there are no autorequires
if (!this.options.autorequire) { return next(null); }
if (!server_options.autorequire) { return next(null); }

// Run this in a parallel forEach, to block less on network.
async.forEach(
_.keys($this.options.autorequire),
_.keys(server_options.autorequire),
function (install_name, fe_next) {
// require() expects to run inside a Fiber
Fiber(function () {
var tmpl_name = $this.options.autorequire[install_name],
var tmpl_name = server_options.autorequire[install_name],
exports = $this.require(tmpl_name);
setCaseVariantAliases($this, install_name, exports);
fe_next();
Expand All @@ -188,6 +220,7 @@ var APIContext = ks_utils.Class({
);
},

// #### setArguments
// Given a list of arguments, make them available to a template as $0..$n
// variables.
setArguments: function (args) {
Expand All @@ -205,6 +238,27 @@ var APIContext = ks_utils.Class({
return this;
},

// #### cacheFn
// Cache the results of a function
cacheFn: function (key, tm_out, to_cache) {
var result = null,
f = new Future(),
mc = this.memcached;
mc.get(key, function (err, c_result) {
if (c_result) {
result = c_result; f['return']();
} else {
to_cache(function (val) {
mc.set(key, val, tm_out, function (err, c_result) {
result = val; f['return']();
})
})
}
});
f.wait();
return result;
},

// #### template(name, arguments)
//
// Attempt to load and execute a template with the given name and
Expand Down
6 changes: 3 additions & 3 deletions lib/kumascript/server.js
Expand Up @@ -173,13 +173,13 @@ var Server = ks_utils.Class({
}
}).object().value();

// TODO: Build a proper API context based on the doc_resp and doc_body
// Build the API context
var api_ctx = new ks_api.APIContext({
server_options: $this.options,
env: env_vars,
request: req,
response: res,
source: src,
autorequire: $this.options.autorequire
source: src
});

try {
Expand Down
2 changes: 1 addition & 1 deletion node_modules/express/node_modules/connect/lib/connect.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions node_modules/express/node_modules/connect/lib/http.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion node_modules/express/node_modules/connect/package.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

52 changes: 0 additions & 52 deletions node_modules/express/node_modules/connect/test.js

This file was deleted.

1 change: 1 addition & 0 deletions package.json
Expand Up @@ -28,6 +28,7 @@
"underscore": "1.3.1",
"nconf": "0.5.1",
"winston": "0.5.9",
"memcached": "0.0.9",
"ejs": "0.6.1",
"up": "0.1.4",
"firelogger": "git://github.com/lmorchard/node-firelogger.git"
Expand Down
12 changes: 7 additions & 5 deletions run.js
Expand Up @@ -55,11 +55,13 @@ var cwd = process.cwd(),
process.env.KUMASCRIPT_CONFIG
];
_.each(conf_fns, function (conf_fn) {
try {
if (conf_fn && fs.statSync(conf_fn).isFile()) {
nconf.file({ file: conf_fn });
}
} catch (e) { }
if (!conf_fn) { return; }
// HACK: There's got to be a better way to detect non-existent files.
try { fs.statSync(conf_fn).isFile(); }
catch (e) { return; }
// Don't catch exceptions here, because it will reveal syntax errors in
// configuration JSON
nconf.file({ file: conf_fn });
});

// Include the fallback defaults.
Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/documents/memcache-expected.txt
@@ -0,0 +1,3 @@
This is a test of a template using Memcache:

* true
3 changes: 3 additions & 0 deletions tests/fixtures/documents/memcache.txt
@@ -0,0 +1,3 @@
This is a test of a template using Memcache:

* {{ memcache-user() }}
11 changes: 11 additions & 0 deletions tests/fixtures/templates/memcache-user.ejs
@@ -0,0 +1,11 @@
<%
var key = 'test-cache';
var value1 = cacheFn(key, 3600, function (next) {
next("Cached");
});
var value2 = cacheFn(key, 3600, function (next) {
next("THIS SHOULDN'T BE EXECUTED");
});
var used_cache = (value1 == value2);
%>
<%= used_cache %>
9 changes: 9 additions & 0 deletions tests/test-api.js
Expand Up @@ -117,6 +117,15 @@ module.exports = {
performTestRequest(test, expected_fn, result_url);
},

"The API offers access to a cache for work done in templates": function (test) {
// This is not an integration test for memcache. Instead, it just ensures
// that the FakeMemcached stub gets used. If that works, then the right
// calls should get made to memcached.
var expected_fn = __dirname + '/fixtures/documents/memcache-expected.txt',
result_url = 'http://localhost:9000/docs/memcache';
performTestRequest(test, expected_fn, result_url);
},

"A sub-API installed into APIContext should be usable in a template": function (test) {
var $this = this,
t_fn = 'api1.txt',
Expand Down