Skip to content

Instantly share code, notes, and snippets.

@aseemk
Forked from laurie71/blocks.js
Created April 19, 2011 11:29
Show Gist options
  • Save aseemk/927172 to your computer and use it in GitHub Desktop.
Save aseemk/927172 to your computer and use it in GitHub Desktop.
Test app for Express blocks middleware
var express = require('express');
var app = express.createServer();
app.configure(function () {
app.use(require('./blocks')(app));
app.use(app.router);
});
app.set('views', __dirname);
app.set('view engine', 'html');
app.register('.html', require('ejs'));
app.get('/', function (req, res) {
res.render('test', { foo: 'bar' });
});
app.listen(8080);
console.log('listening on port 8080...');
var debug = require('util').debug;
// helper prototype for rendering block scripts, stylesheet links, etc
var blocktag = {
toString: function() {
var tag = this.tag,
html = '<'+tag,
that = this;
Object.getOwnPropertyNames(this).forEach(function(prop) {
if (prop !== 'tag') {
html += ' '+prop+'="'+that[prop]+'"';
}
});
if (tag === 'script') {
html += '></script>';
} else {
html += ' />';
}
return html;
}
}
module.exports = function(app) {
return function(req, res, next) {
// request-local hash of named blocks
var blocks = {};
// dynamic helper for defining/rendering blocks
function block(name) {
var block, html;
if (arguments.length === 0) return next(new Error('unnamed block'));
block = blocks[name];
if (arguments.length === 1) {
// render block contents
html = '';
if (block) {
block.forEach(function(item) {
html = String(item) + html;
});
}
debug('block: '+name+': '+JSON.stringify(block));
debug('html: '+html);
return html;
} else {
// define/append block contents
if (! block) block = blocks[name] = [];
block.push(Array.prototype.slice.call(arguments, 1));
debug('block: '+name+': '+JSON.stringify(block));
return '';
}
};
// utility method for adding a script to a block
block.script = function(src) {
return Object.create(blocktag, {
src : { enumerable: true, value: src },
tag : { enumerable: true, value: 'script' },
type: { enumerable: true, value: 'text/javascript' }
});
};
// utility method for adding a stylesheet to a block
block.stylesheet = function(href, media) {
var tag = Object.create(blocktag, {
href: { enumerable: true, value: href },
tag : { enumerable: true, value: 'link' },
type: { enumerable: true, value: 'text/css' },
rel : { enumerable: true, value: 'stylesheet' }
});
if (media) {
tag.media = media;
}
return tag;
};
debug('add helpers');
// res.local('block', block);
// req.app.dynamicHelpers(block);
// req.app.helpers(block);
app.helpers({block:block});
next();
};
};
<!-- use blocks.js to inject content into *parent* layout -->
<!-- use new layout() helper to select parent layout -->
<% layout('layout_site') %>
<% block('head', block.stylesheet('layout.css')) %>
<% block('foot', block.script('layout.js')) %>
<h1>Main Layout</h1>
<p>
This is the main page layout.
It is wrapped by the <t>layout_site</t> layout.
It adds the following blocks to the layout:
</p>
<ul><li>A layout.css stylesheet in the document head</li>
<li>A layout.js script at the document foot</li>
</ul>
<%- body %>
<!doctype html>
<head>
<meta charset=utf-8>
<title>Nested Layouts with Blocks Demo</title>
<!-- pull in the 'head' block content defined by our child layouts/pages -->
<%- block('head') %>
</head>
<body>
<h1>Site Layout</h1>
<p>
This is the master site layout.
It is not wrapped by a layout.
It includes the following dynamic blocks:
</p>
<ul><li><t>head</t>: a block whose content is rendered in the HTML 'head' section</li>
<li><t>foot</t>: a block whose content is rendered just before the closing 'body' tag</li>
</ul>
<%- body %>
<!-- pull in the 'foot' block content defined by our child layouts/pages -->
<%- block('foot') %>
</body>
</html>
<!-- use blocks.js to inject content into layout -->
<!-- use new layout() helper to get nested layouts -->
<% layout('layout_main') %>
<% block('head', block.stylesheet('page.css')) %>
<% block('foot', block.script('page.js')) %>
<h2>Index Page</h2>
<p>
This is the main page content.
It is wrapped by the <t>layout_main</t> layout.
It adds the following blocks to the layout:
</p>
<ul><li>A page.css stylesheet in the document head</li>
<li>A page.js script at the document foot</li>
</ul>
@aseemk
Copy link
Author

aseemk commented Apr 19, 2011

The three .html files should be in a views directory. Gist doesn't show full pathnames for files.

@aseemk
Copy link
Author

aseemk commented Apr 19, 2011

As the code is right now, navigating to / hangs. I haven't been able to figure out where the hang is coming from; none of the debug messages get printed.

If I swap the order of the two middlewares, app.router and blocks, requests no longer hang, but it seems to be the wrong order, because I get this error message:

ReferenceError: block is not defined
    at Object.anonymous (eval at <anonymous> (/usr/lib/node/.npm/ejs/0.3.1/package/lib/ejs.js:140:12))
    at Object.<anonymous> (/usr/lib/node/.npm/ejs/0.3.1/package/lib/ejs.js:142:15)
    at ServerResponse._render (/usr/lib/node/.npm/express/2.2.2/package/lib/view.js:377:21)
    at ServerResponse.render (/usr/lib/node/.npm/express/2.2.2/package/lib/view.js:242:17)
    at Object.<anonymous> (/Users/aseemk/Projects/Node/block-test/app.js:14:9)
    at param (/usr/lib/node/.npm/connect/1.3.0/package/lib/middleware/router.js:148:21)
    at pass (/usr/lib/node/.npm/connect/1.3.0/package/lib/middleware/router.js:164:10)
    at Object.router [as handle] (/usr/lib/node/.npm/connect/1.3.0/package/lib/middleware/router.js:170:6)
    at next (/usr/lib/node/.npm/connect/1.3.0/package/lib/http.js:204:15)
    at Object.handle (/usr/lib/node/.npm/express/2.2.2/package/lib/http.js:75:5)

@aseemk
Copy link
Author

aseemk commented Apr 20, 2011

@laurie71 found the bug: require('blocks') returns a function which needs to be called with app and which returns the actual middleware. Fixed, and now it works. Thanks Laurie!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment