Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Fixes to run on Node 0.4+, and better Readme #22

Open
wants to merge 8 commits into from

2 participants

@temsa

Hi, just made some small improvements over wheat to get it working on top of Node 0.4+.

I tried also to fix an issue which is "when viewing some article refering to a DOT file when Graphviz isn't installed, it crashes the blog..." but I didn't get how to catch the error, using :

  • running the child process in a try/catch.
  • listen to 'error' events on both the child process and main one.

The stack I have :

Error: EPIPE, Broken pipe
at Socket._writeImpl (net.js:138:14)
at Socket._writeOut (net.js:427:25)
at Socket.write (net.js:356:17)
at execPipe (/path/to/wheat/lib/wheat/renderers.js:41:18)
at Function.processFile (/path/to//wheat/renderers.js:281:11)
at next (/usr/local/lib/node/.npm/step/0.0.4/package/lib/step.js:51:23)
at /usr/local/lib/node/.npm/git-fs/0.0.5/package/lib/git-fs.js:93:23
at /usr/local/lib/node/.npm/git-fs/0.0.5/package/lib/git-fs.js:242:5
at [object Object].<anonymous> (fs.js:86:5)
at [object Object].emit (events.js:39:17)

Finally I used another solution for the moment, consisting in using cluster, which respawns the worker automagically when it crashes, but tht's not a pretty good solution !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on Dec 17, 2010
  1. @mklabs
Commits on Feb 26, 2011
  1. @temsa

    Fixes for Node 0.4+

    temsa authored
  2. @temsa
  3. @temsa
  4. @temsa

    Merge branch 'master' of github.com:temsa/wheat

    temsa authored
    Conflicts:
    	README.markdown
  5. @temsa

    Small fix

    temsa authored
  6. @temsa

    Fixed Markdown

    temsa authored
  7. @temsa

    Improved Readme !

    temsa authored
This page is out of date. Refresh to see the latest.
View
18 .gitmodules
@@ -1,18 +0,0 @@
-[submodule "lib/wheat/node-git"]
- path = lib/wheat/node-git
- url = git://github.com/creationix/node-git.git
-[submodule "lib/wheat/step"]
- path = lib/wheat/step
- url = git://github.com/creationix/step.git
-[submodule "lib/wheat/haml-js"]
- path = lib/wheat/haml-js
- url = git://github.com/creationix/haml-js.git
-[submodule "lib/wheat/node-router"]
- path = lib/wheat/node-router
- url = git://github.com/creationix/node-router.git
-[submodule "lib/wheat/proto"]
- path = lib/wheat/proto
- url = git://github.com/creationix/proto.git
-[submodule "lib/wheat/connectpkg"]
- path = lib/wheat/connectpkg
- url = git://github.com/extjs/Connect.git
View
41 README.markdown
@@ -2,14 +2,49 @@
Wheat is a blogging engine that reads a git repo full of markdown articles and presents them as a website.
+Wheat engine takes a local git repository path as a parameter
+
+ var wheat = require('wheat')("/path/to/my/repo");
+ // wheat is now a function which handles request and return response:
+ // wheat(req/*request*/, res/*response*/, next /*next handler to call for this request*/)
+
+Here's an example using Connect ( npm install connect ) to start a server, adapted from [howtonode.org repo app.js](https://github.com/creationix/howtonode.org/blob/master/app.js) :
+
+ var Connect = require('connect');
+
+ var repository = "/path/to/my/repo";
+
+ Connect.createServer(
+ Connect.logger(),
+ Connect.conditionalGet(),
+ Connect.favicon(),
+ Connect.cache(),
+ Connect.gzip(),
+ require('wheat')(repository)
+ ).listen(3000);
+
+You may also be interested in using [Cluster](http://learnboost.github.com/cluster/) ( npm install cluster )to run the blog in a reliable way (just don't listen to the port directly, pass the Connect server to Cluster, add the cluster plugin you want, and start !).
+
## How to Install
-Either manually install all the dependencies or use npm. It's packaged nicely now.
+Either manually install all the dependencies or use npm.
npm install wheat
For on the fly rendering of Graphviz graphs (DOT files), Graphviz will need to be [installed](http://www.graphviz.org/Download..php)
-That's it! Checkout the wheat branch of howtonode.org for an example of how to use the library.
-<http://github.com/creationix/howtonode.org>
+## Quick start using howtonode.org [repository](http://github.com/creationix/howtonode.org) for content :
+ $> npm install wheat
+ $> git clone https://github.com/creationix/howtonode.org.git
+ $> cd howtonode.org
+
+Then edit app.js and add ".listen(3000);" at the end of "Connect.createServer", see above example using connect.
+
+Now just run it
+
+ $> node app.js
+
+Open your browser on [your host, port 3000](http://127.0.0.1:3000). That's it!
+
+Note: Viewing any article using DOT files will crash the server if you don't have GraphViz (see above) installed
View
24 lib/wheat.js
@@ -19,7 +19,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
-
require.paths.unshift(__dirname + "/wheat");
require('proto');
@@ -46,20 +45,23 @@ function handleRoute(req, res, next, renderer, match) {
renderer.apply(null, match.concat([callback]));
}
-module.exports = function setup(repo) {
+module.exports = function setup(repo, o) {
+ // Set up configuration in necessary modules
+ var r = Renderers(o || {theme: ''});
+
// Initialize the Git Filesystem
Git(repo || process.cwd());
// Set up our routes
- addRoute(/^\/()$/, Renderers.index);
- addRoute(/^\/()feed.xml$/, Renderers.feed);
- addRoute(/^\/([a-f0-9]{40})\/([a-z0-9_-]+)$/, Renderers.article);
- addRoute(/^\/([a-f0-9]{40})\/(.+\.dot)$/, Renderers.dotFile);
- addRoute(/^\/([a-f0-9]{40})\/(.+\.[a-z]{2,4})$/, Renderers.staticFile);
- addRoute(/^\/()([a-z0-9_-]+)$/, Renderers.article);
- addRoute(/^\/()(.+\.dot)$/, Renderers.dotFile);
- addRoute(/^\/()(.+\.[a-z]{2,4})$/, Renderers.staticFile);
- addRoute(/^\/()category\/([\%\.a-z0-9_-]+)$/, Renderers.categoryIndex);
+ addRoute(/^\/()$/, r.index);
+ addRoute(/^\/()feed.xml$/, r.feed);
+ addRoute(/^\/([a-f0-9]{40})\/([a-z0-9_-]+)$/, r.article);
+ addRoute(/^\/([a-f0-9]{40})\/(.+\.dot)$/, r.dotFile);
+ addRoute(/^\/([a-f0-9]{40})\/(.+\.[a-z]{2,4})$/, r.staticFile);
+ addRoute(/^\/()([a-z0-9_-]+)$/, r.article);
+ addRoute(/^\/()(.+\.dot)$/, r.dotFile);
+ addRoute(/^\/()(.+\.[a-z]{2,4})$/, r.staticFile);
+ addRoute(/^\/()category\/([\%\.a-z0-9_-]+)$/, r.categoryIndex);
return function handle(req, res, next) {
View
6 lib/wheat/prettify.js
@@ -55,13 +55,15 @@
// JSLint declarations
/*global console, document, navigator, setTimeout, window */
+var window = window ||{};
+window.console = console||{log:function(){}, warn:function(){}, trace:function(){}};
-if (typeof window === 'undefined') {
+/*if (typeof window === 'undefined') {
window = GLOBAL;
if (typeof require === 'function') {
window.console = console;
}
-}
+}*/
if (typeof navigator === 'undefined') {
navigator = {};
}
View
458 lib/wheat/renderers.js
@@ -5,7 +5,7 @@ var Git = require('git-fs'),
Prettify = require('./prettify'),
MD5 = require('./md5'),
ChildProcess = require('child_process'),
- Mime = require('connect/utils').mime,
+ Mime = require('connect').utils.mime,
Step = require('step');
// Execute a child process, feed it a buffer and get a new buffer filtered.
@@ -19,6 +19,9 @@ function execPipe(command, args, data, callback) {
child.stderr.addListener('data', function onStderr(buffer) {
stderr[stderr.length] = buffer;
});
+ child.addListener('error', function onExit(err) {
+ callback(err);
+ });
child.addListener('exit', function onExit(code) {
if (code > 0) {
callback(new Error(stderr.join("")));
@@ -33,248 +36,259 @@ function execPipe(command, args, data, callback) {
callback(null, buffer);
}
});
- if (typeof data === 'string') {
- child.stdin.write(data, "binary");
- } else {
- child.stdin.write(data);
+ try {
+ if (typeof data === 'string') {
+ child.stdin.write(data, "binary");
+ } else {
+ child.stdin.write(data);
+ }
+ } catch(e) {
+ (errCallback||callback)(e);
}
child.stdin.end();
}
-// This writes proper headers for caching and conditional gets
-// Also gzips content if it's text based and stable.
-function postProcess(headers, buffer, version, path, callback) {
- Step(
- function buildHeaders() {
- if (!headers["Content-Type"]) {
- headers["Content-Type"] = "text/html; charset=utf-8";
- }
- var date = new Date().toUTCString();
- headers["Date"] = date;
- headers["Server"] = "Wheat (node.js)";
- if (version === 'fs') {
- delete headers["Cache-Control"];
- } else {
- headers["ETag"] = MD5.md5(version + ":" + path + ":" + date);
- }
+var Renderers = module.exports = (function(o) {
- if (/html/.test(headers["Content-Type"])) {
- buffer = Tools.stringToBuffer((buffer+"").replace(/<pre><code>[^<]+<\/code><\/pre>/g,
- function applyHighlight(code) {
- var code = code.match(/<code>([\s\S]+)<\/code>/)[1];
- code = Prettify.prettyPrintOne(code)
- return "<pre><code>" + code + "</code></pre>";
- }
- ));
- }
-
- headers["Content-Length"] = buffer.length;
+ var config = o || {theme: ''};
+
+ var tools = Tools(config);
+
+ // This writes proper headers for caching and conditional gets
+ // Also gzips content if it's text based and stable.
+ function postProcess(headers, buffer, version, path, callback) {
+ Step(
+ function buildHeaders() {
+ if (!headers["Content-Type"]) {
+ headers["Content-Type"] = "text/html; charset=utf-8";
+ }
+ var date = new Date().toUTCString();
+ headers["Date"] = date;
+ headers["Server"] = "Wheat (node.js)";
+ if (version === 'fs') {
+ delete headers["Cache-Control"];
+ } else {
+ headers["ETag"] = MD5.md5(version + ":" + path + ":" + date);
+ }
- return {
- headers: headers,
- buffer: buffer
- };
- },
- callback
- );
-}
+ if (/html/.test(headers["Content-Type"])) {
+ buffer = tools.stringToBuffer((buffer+"").replace(/<pre><code>[^<]+<\/code><\/pre>/g,
+ function applyHighlight(code) {
+ var code = code.match(/<code>([\s\S]+)<\/code>/)[1];
+ code = Prettify.prettyPrintOne(code)
+ return "<pre><code>" + code + "</code></pre>";
+ }
+ ));
+ }
-function insertSnippets(markdown, snippets, callback) {
- Step(
- function () {
- Tools.compileTemplate('snippet', this);
- },
- function (err, snippetTemplate) {
- if (err) { callback(err); return; }
- snippets.forEach(function (snippet) {
- var html = snippetTemplate({snippet: snippet});
- markdown = markdown.replace(snippet.original, html);
- });
- return markdown;
- },
- callback
- )
-}
+ headers["Content-Length"] = buffer.length;
-var Renderers = module.exports = {
- index: Git.safe(function index(version, callback) {
- Step(
- function getHead() {
- Git.getHead(this);
- },
- function loadData(err, head) {
- if (err) { callback(err); return; }
- Data.articles(version, this.parallel());
- Git.readFile(head, "description.markdown", this.parallel());
- Data.categories(version, this.parallel());
- },
- function applyTemplate(err, articles, description, categories) {
- if (err) { callback(err); return; }
- Tools.render("index", {
- articles: articles,
- description: description,
- categories: categories
- }, this);
- },
- function callPostProcess(err, buffer) {
- if (err) { callback(err); return; }
- postProcess({
- "Cache-Control": "public, max-age=3600"
- }, buffer, version, "index", this);
+ return {
+ headers: headers,
+ buffer: buffer
+ };
},
callback
);
- }),
+ };
- feed: Git.safe(function feed(version, callback) {
- var articles;
+ function insertSnippets(markdown, snippets, callback) {
Step(
- function loadData() {
- Data.fullArticles(version, this);
- },
- function (err, data) {
- if (err) { callback(err); return; }
- articles = data;
- var group = this.group();
- articles.forEach(function (article) {
- insertSnippets(article.markdown, article.snippets, group());
- });
+ function () {
+ tools.compileTemplate('snippet', this);
},
- function applyTemplate(err, markdowns) {
+ function (err, snippetTemplate) {
if (err) { callback(err); return; }
- markdowns.forEach(function (markdown, i) {
- articles[i].markdown = markdown;
+ snippets.forEach(function (snippet) {
+ var html = snippetTemplate({snippet: snippet});
+ markdown = markdown.replace(snippet.original, html);
});
- Tools.render("feed.xml", {
- articles: articles
- }, this, true);
- },
- function finish(err, buffer) {
- if (err) { callback(err); return; }
- postProcess({
- "Content-Type":"application/rss+xml",
- "Cache-Control": "public, max-age=3600"
- }, buffer, version, "feed.xml", this);
+ return markdown;
},
callback
- );
- }),
+ )
+ };
+
+ return {
+ index: Git.safe(function index(version, callback) {
+ Step(
+ function getHead() {
+ Git.getHead(this);
+ },
+ function loadData(err, head) {
+ if (err) { callback(err); return; }
+ Data.articles(version, this.parallel());
+ Git.readFile(head, "description.markdown", this.parallel());
+ Data.categories(version, this.parallel());
+ },
+ function applyTemplate(err, articles, description, categories) {
+ if (err) { callback(err); return; }
+ tools.render("index", {
+ articles: articles,
+ description: description,
+ categories: categories
+ }, this);
+ },
+ function callPostProcess(err, buffer) {
+ if (err) { callback(err); return; }
+ postProcess({
+ "Cache-Control": "public, max-age=3600"
+ }, buffer, version, "index", this);
+ },
+ callback
+ );
+ }),
- article: Git.safe(function renderArticle(version, name, callback) {
- var article, description;
- Step(
- function loadData() {
- Git.getHead(this.parallel());
- Data.fullArticle(version, name, this.parallel());
- },
- function (err, head, props) {
- if (err) { callback(err); return; }
- article = props;
- insertSnippets(article.markdown, article.snippets, this.parallel());
- Git.readFile(head, "description.markdown", this.parallel());
- },
- function applyTemplate(err, markdown, description) {
- if (err) { callback(err); return; }
- article.markdown = markdown;
- Tools.render("article", {
- title: article.title,
- article: article,
- author: article.author,
- description: description
- }, this);
- },
- function finish(err, buffer) {
- if (err) { callback(err); return; }
- postProcess({
- "Cache-Control": "public, max-age=3600"
- }, buffer, version, name, this);
- },
- callback
- );
- }),
+ feed: Git.safe(function feed(version, callback) {
+ var articles;
+ Step(
+ function loadData() {
+ Data.fullArticles(version, this);
+ },
+ function (err, data) {
+ if (err) { callback(err); return; }
+ articles = data;
+ var group = this.group();
+ articles.forEach(function (article) {
+ insertSnippets(article.markdown, article.snippets, group());
+ });
+ },
+ function applyTemplate(err, markdowns) {
+ if (err) { callback(err); return; }
+ markdowns.forEach(function (markdown, i) {
+ articles[i].markdown = markdown;
+ });
+ tools.render("feed.xml", {
+ articles: articles
+ }, this, true);
+ },
+ function finish(err, buffer) {
+ if (err) { callback(err); return; }
+ postProcess({
+ "Content-Type":"application/rss+xml",
+ "Cache-Control": "public, max-age=3600"
+ }, buffer, version, "feed.xml", this);
+ },
+ callback
+ );
+ }),
- categoryIndex: Git.safe(function index(version, category, callback) {
- Step(
- function getHead() {
- Git.getHead(this);
- },
- function loadData(err, head) {
- if (err) { callback(err); return; }
- Data.articles(version, this.parallel());
- Git.readFile(head, "description.markdown", this.parallel());
- Data.categories(version, this.parallel());
- },
- function applyTemplate(err, articles, description, categories) {
- if (err) { callback(err); return; }
+ article: Git.safe(function renderArticle(version, name, callback) {
+ var article, description;
+ Step(
+ function loadData() {
+ Git.getHead(this.parallel());
+ Data.fullArticle(version, name, this.parallel());
+ },
+ function (err, head, props) {
+ if (err) { callback(err); return; }
+ article = props;
+ insertSnippets(article.markdown, article.snippets, this.parallel());
+ Git.readFile(head, "description.markdown", this.parallel());
+ },
+ function applyTemplate(err, markdown, description) {
+ if (err) { callback(err); return; }
+ article.markdown = markdown;
+ tools.render("article", {
+ title: article.title,
+ article: article,
+ author: article.author,
+ description: description
+ }, this);
+ },
+ function finish(err, buffer) {
+ if (err) { callback(err); return; }
+ postProcess({
+ "Cache-Control": "public, max-age=3600"
+ }, buffer, version, name, this);
+ },
+ callback
+ );
+ }),
+
+ categoryIndex: Git.safe(function index(version, category, callback) {
+ Step(
+ function getHead() {
+ Git.getHead(this);
+ },
+ function loadData(err, head) {
+ if (err) { callback(err); return; }
+ Data.articles(version, this.parallel());
+ Git.readFile(head, "description.markdown", this.parallel());
+ Data.categories(version, this.parallel());
+ },
+ function applyTemplate(err, articles, description, categories) {
+ if (err) { callback(err); return; }
- var articlesForCategory = articles.reduce(function (start, element){
- return element.categories && element.categories.indexOf(category) >= 0 ? start.concat(element) : start;
- }, []);
+ var articlesForCategory = articles.reduce(function (start, element){
+ return element.categories && element.categories.indexOf(category) >= 0 ? start.concat(element) : start;
+ }, []);
- Tools.render("index", {
- articles: articlesForCategory,
- description: description,
- categories: categories
- }, this);
- },
- function callPostProcess(err, buffer) {
- if (err) { callback(err); return; }
- postProcess({
- "Cache-Control": "public, max-age=3600"
- }, buffer, version, "index", this);
- },
- callback
- );
- }),
+ tools.render("index", {
+ articles: articlesForCategory,
+ description: description,
+ categories: categories
+ }, this);
+ },
+ function callPostProcess(err, buffer) {
+ if (err) { callback(err); return; }
+ postProcess({
+ "Cache-Control": "public, max-age=3600"
+ }, buffer, version, "index", this);
+ },
+ callback
+ );
+ }),
- staticFile: Git.safe(function staticFile(version, path, callback) {
- Step(
- function loadPublicFiles() {
- Git.readFile(version, "skin/public/" + path, this);
- },
- function loadArticleFiles(err, data) {
- if (err) {
- Git.readFile(version, "articles/" + path, this);
- }
- return data;
- },
- function processFile(err, string) {
- if (err) { callback(err); return; }
- var headers = {
- "Content-Type": Mime.type(path),
- "Cache-Control": "public, max-age=32000000"
- };
- var buffer = new Buffer(string.length);
- buffer.write(string, 'binary');
- postProcess(headers, buffer, version, path, this);
- },
- callback
- );
- }),
+ staticFile: Git.safe(function staticFile(version, path, callback) {
+ Step(
+ function loadPublicFiles() {
+ Git.readFile(version, "skin/" + config.theme + "/public/" + path, this);
+ },
+ function loadArticleFiles(err, data) {
+ if (err) {
+ Git.readFile(version, "articles/" + path, this);
+ }
+ return data;
+ },
+ function processFile(err, string) {
+ if (err) { callback(err); return; }
+ var headers = {
+ "Content-Type": Mime.type(path),
+ "Cache-Control": "public, max-age=32000000"
+ };
+ var buffer = new Buffer(string.length);
+ buffer.write(string, 'binary');
+ postProcess(headers, buffer, version, path, this);
+ },
+ callback
+ );
+ }),
- dotFile: Git.safe(function dotFile(version, path, callback) {
- Step(
- function loadPublicFiles() {
- Git.readFile(version, "skin/public/" + path, this);
- },
- function loadArticleFiles(err, data) {
- if (err) {
- Git.readFile(version, "articles/" + path, this);
- }
- return data;
- },
- function processFile(err, data) {
- if (err) { callback(err); return; }
- execPipe("dot", ["-Tpng"], data, this);
- },
- function finish(err, buffer) {
- if (err) { callback(err); return; }
- postProcess({
- "Content-Type": "image/png",
- "Cache-Control": "public, max-age=32000000"
- }, buffer, version, path, this);
- },
- callback
- );
- })
-}
+ dotFile: Git.safe(function dotFile(version, path, callback) {
+ Step(
+ function loadPublicFiles() {
+ Git.readFile(version, "skin/" + config.theme + "/public/" + path, this);
+ },
+ function loadArticleFiles(err, data) {
+ if (err) {
+ Git.readFile(version, "articles/" + path, this);
+ }
+ return data;
+ },
+ function processFile(err, data) {
+ if (err) { callback(err); return; }
+ execPipe("dot", ["-Tpng"], data, this);
+ },
+ function finish(err, buffer) {
+ if (err) { errCallback(err); return; }
+ postProcess({
+ "Content-Type": "image/png",
+ "Cache-Control": "public, max-age=32000000"
+ }, buffer, version, path, this);
+ },
+ callback
+ );
+ })
+ };
+});
View
18 lib/wheat/tools.js
@@ -3,7 +3,8 @@ var Step = require('step'),
Markdown = require('./markdown'),
MD5 = require('./md5'),
Buffer = require('buffer').Buffer,
- Git = require('git-fs');
+ Git = require('git-fs'),
+ config = {};
function pad(num, count) {
count = count || 2;
@@ -124,7 +125,7 @@ function stringToBuffer(string) {
var loadTemplate = Git.safe(function loadTemplate(version, name, callback) {
Step(
function loadHaml() {
- Git.readFile(version, "skin/" + name + ".haml", this);
+ Git.readFile(version, "skin/" + config.theme + "/" + name + ".haml", this);
},
function compileTemplate(err, haml) {
if (err) { callback(err); return; }
@@ -183,8 +184,11 @@ function render(name, data, callback, partial) {
)
}
-module.exports = {
- stringToBuffer: stringToBuffer,
- compileTemplate: compileTemplate,
- render: render
-};
+module.exports = (function(o){
+ config = o || {theme: ''};
+ return {
+ stringToBuffer: stringToBuffer,
+ compileTemplate: compileTemplate,
+ render: render
+ };
+});
Something went wrong with that request. Please try again.