Permalink
Browse files

Implementing memory strategy for the middleware

A memory strategy has been added to intercept files that have already
been uglified so that they do not have to be reprocessed on the server.
This is a very simple strategy that will eventually accumulate all
javascript files uglified into memory without limit.
  • Loading branch information...
1 parent f238060 commit 9a3fd743f11603c3e4ae92e4466a767210de2ddf @ethul committed Feb 12, 2012
Showing with 123 additions and 25 deletions.
  1. +12 −7 lib/memory-strategy.js
  2. +17 −16 lib/middleware.js
  3. +40 −0 spec/memory-strategy.spec.js
  4. +54 −2 spec/middleware.spec.js
View
@@ -1,11 +1,16 @@
-module.exports = function() {
- var memory = {};
+var curry = require("bassline").curry;
+
+module.exports = function(memory) {
+ memory = memory || {};
+
+ // Allows for a value to be placed into the memory at the given key if
+ // the caller so choses to do so.
+ function reserver(key,value) {memory[key] = value; return value;}
+
return Object.create({
- getOrElse: function(key,callback) {
- console.log("getting or elsing");
- },
- put: function(key,value) {
- console.log("putting");
+ get: function(onget,onelse,key) {
+ if (key in memory) onget(memory[key]);
+ else onelse(curry(reserver)(key),key);
}
});
};
View
@@ -11,29 +11,25 @@ var fs = require("fs")
, promise = bassline.promise
, memory = require("./memory-strategy");
-// When the extension of the file requested is not .js then go to next
-// <script src="/a.js"></script>
-// app.use(middleware(__dirname + "/assets/javascripts"));
-// app.use(middleware(__dirname + "/public/javascripts"));
-// <script src="/javascripts/a.js"></script>
-// app.use(middleware(__dirname + "/public"));
-// <script src="/assets/a.js"></script>
-// app.use("/assets",middleware(__dirname + "/assets/javascripts"));
-// app.use("/assets",middleware(__dirname + "/public/javascripts"));
+// Creates a new middleware which requires a non-empty root string
exports = module.exports = function(root,options) {
if (!validRoot(root)) throw new Error("Invalid root path: " + root);
+ var config = configure(options || {});
return function(request,response,next) {
- var config = configure(options || {});
isMethodKnown(request).
fmap(curry(requestedFilePath)(root)).
bind(curry(doesEscapeRoot)(root)).
bind(isFileKnown).
- fmap(function(file){
- doesFileExist(file,response).
- bind(curry(readFile)(config.encoding,next)).
- bind(uglifyFile).
- fold(curry(httpOk)(response,isHttpHead(request)));
- }).fold(next,function(){});
+ fmap(curry(config.strategy.get)(
+ curry(httpOk)(response,isHttpHead(request)),
+ function(reserver,key){
+ doesFileExist(key,response).
+ bind(curry(readFile)(config.encoding,next)).
+ bind(uglifyFile).
+ bind(curry(reserveFile)(reserver)).
+ fold(curry(httpOk)(response,isHttpHead(request)));
+ }
+ )).fold(next,function(){});
};
};
@@ -108,6 +104,11 @@ function uglifyFile(file) {
);
}
+// Completes the reserveration for the file in the caching strategy.
+function reserveFile(reserver,file) {
+ return promise(reserver(file));
+}
+
// Helper method to wrap a function with a promise
function withPromise(f) {var p = promise();f(p);return p;}
@@ -0,0 +1,40 @@
+var strategy = require("../lib/memory-strategy");
+
+describe("when the memory strategy is used", function() {
+ beforeEach(function() {
+ this.memory = {};
+ this.strategy = strategy(this.memory);
+ });
+
+ describe("when the key is not in the memory and the reserver is invoked", function() {
+ it("should update the memory with the value at the give key", function() {
+ var memory = this.memory
+ , key = "notthere"
+ , val = "value";
+ this.strategy.get(function(v){},function(r,k){
+ expect(r(val)).toEqual(val);
+ expect(k).toEqual(key);
+ },key);
+ expect(memory[key]).toEqual(val);
+ });
+ });
+
+ describe("when the key is not in the memory and the reserver is not invoked", function() {
+ it("should not update the memory", function() {
+ var memory = this.memory
+ , key = "notthere";
+ this.strategy.get(function(v){},function(r){},key);
+ expect(memory).toEqual({});
+ });
+ });
+
+ describe("when the key is in the memory", function() {
+ it("should be passed to the onget function", function() {
+ var key = "notthere", val = "value", that = this;
+ this.memory[key] = val;
+ this.strategy.get(function(v){
+ expect(v).toEqual(val);
+ },function(r){that.fail(new Error("should not be here"));},key);
+ });
+ });
+});
View
@@ -2,19 +2,25 @@ var fs = require("fs")
, path = require("path")
, request = require("request")
, connect = require("connect")
- , middleware = require("../lib/middleware");
+ , middleware = require("../lib/middleware")
+ , memory = require("../lib/memory-strategy");
describe("when the uglify-js middleware is used by connect", function() {
beforeEach(function() {
this.root = __dirname + "/assets/javascripts";
this.encoding = "utf8";
+ this.memory = {};
+ this.strategy = memory(this.memory);
});
beforeEach(function() {
var port = 12341;
this.uri = "http://localhost:" + port;
this.server = connect.createServer();
- this.server.use(middleware(this.root,{encoding: this.encoding}));
+ this.server.use(middleware(this.root,{
+ encoding: this.encoding,
+ strategy: this.strategy
+ }));
this.server.listen(port);
});
@@ -135,6 +141,52 @@ describe("when the uglify-js middleware is used by connect", function() {
});
});
+ describe("when an existing javascript file is requested that is memory with a GET", function() {
+ beforeEach(function() {
+ var javascript = this.javascript;
+ this.existsSpy = spyOn(path,"exists").andCallFake(function(file,callback){callback(true);});
+ this.readFileSpy = spyOn(fs,"readFile").andCallFake(function(file,encoding,callback){callback(false,javascript);});
+ });
+ beforeEach(function() {
+ this.pathname = "/abc/xyz/inmemory.js";
+ this.memory[this.root + this.pathname] = this.uglified;
+ });
+ it("should respond with an HTTP 200 status", function() {
+ get(this.uri + this.pathname, function(error,response,body) {
+ expect(response.statusCode).toEqual(200);
+ });
+ });
+ it("should respond with the text/javascript content type", function() {
+ get(this.uri + this.pathname, function(error,response,body) {
+ expect(response.headers["content-type"]).toEqual("application/javascript");
+ });
+ });
+ it("should respond with the length of the uglified javascript as content length", function() {
+ var expected = this.uglified.length.toString();
+ get(this.uri + this.pathname, function(error,response,body) {
+ expect(response.headers["content-length"]).toEqual(expected);
+ });
+ });
+ it("should respond with the uglified javascript file request", function() {
+ var expected = this.uglified;
+ get(this.uri + this.pathname, function(error,response,body) {
+ expect(body).toEqual(expected);
+ });
+ });
+ it("should not call exists on the path", function() {
+ var spy = this.existsSpy;
+ get(this.uri + this.pathname, function(error,response,body) {
+ expect(spy).not.toHaveBeenCalled();
+ });
+ });
+ it("should not call read on the fs", function() {
+ var spy = this.readFileSpy;
+ get(this.uri + this.pathname, function(error,response,body) {
+ expect(spy).not.toHaveBeenCalled();
+ });
+ });
+ });
+
describe("when a non-existing javascript file is requested", function() {
beforeEach(function() {
this.existsSpy = spyOn(path,"exists").andCallFake(function(file,callback){callback(false);});

0 comments on commit 9a3fd74

Please sign in to comment.