Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
base fork: twolfson/jojo
base: 9bbd168aa0
...
head fork: twolfson/jojo
compare: a9c718a4b3
  • 5 commits
  • 4 files changed
  • 0 commit comments
  • 1 contributor
View
1  app.js
@@ -6,6 +6,7 @@ app.use(jojo);
app.listen(8080);
app.set('view engine', 'ejs');
+app.set('jojo index view', 'pages/index');
app.set('jojo article view', 'pages/article');
app.get('/abba', function (req, res) {
View
8 node_modules/jojo/package.json
@@ -13,8 +13,12 @@
"node": ">= 0.4.0"
},
"dependencies": {
- "express": ">= 2.0.0"
+ "express": ">= 2.0.0",
+ "async": "*"
+ },
+ "devDependencies": {
+ "ejs": "*",
+ "showdown": "*"
},
- "devDependencies": {},
"optionalDependencies": {}
}
View
238 node_modules/jojo/src/jojo.js
@@ -1,14 +1,13 @@
var fs = require('fs'),
path = require('path'),
- url = require('url');
+ async = require('async');
-// TODO: What the fuck does this mean???
-// TODO: Restore config.callouts (config should not be a local variable?)
-
-// TODO: Switch over to a static file creator and file watching
+// TODO: Switch over to a static file creator and file watching (don't forget about creation of new files -- directory watching? or async setTimeout that watches dir)
// TODO: Implement RSS
+// TODO: Make separate methods for base page, article page, and rss which are tied back to jojo
+
/**
* jojo - 10 second blog engine for hackers (in javascript)
* @param {Object<ExpressServer>} [app] App to write to
@@ -29,7 +28,7 @@ function jojo(req, res, next) {
basepath = settings['jojo basepath'] || '/',
cwd = process.cwd(),
articleDir = settings['jojo articles'] || path.join(cwd, 'articles'),
- formatEngine = settings['jojo formatter'] || 'showdown',
+ yoyo = new Yoyo(app),
indexView = settings['jojo index view'],
articleView = settings['jojo article view'],
baseIndex;
@@ -38,102 +37,59 @@ function jojo(req, res, next) {
if (url === basepath) {
// and there is an index view
if (indexView) {
- // TODO: Index page with articles
- return res.render(indexView, 'Post summary');
+ // Generate an index page with articles
+ return yoyo.readArticles(articleDir, function (err, articles) {
+ // If there is an error, log it and move to next fn
+ if (err) {
+ console.error(err);
+ return next();
+ }
+
+ // Otherwise, render
+ // TODO: Figure out how to include view data that we don't know about
+ res.render(indexView, {'title': 'TESTING', 'articles': articles});
+ });
}
} else {
baseIndex = url.indexOf(basepath);
if (baseIndex !== -1) {
- // TODO: Abstract this into a jojo.readArticles (and readArticlesSync?) function
// Otherwise, attempt to find a matching article
- return fs.readdir(articleDir, function (err, fileNames) {
- var articleUrl = url.slice(baseIndex + 1);
-
- // If there is an error, log it and continue to the next method
+ return yoyo.readArticles(articleDir, function (err, articles) {
+ // If there is an error, log it and run the next function
if (err) {
- console.error('Article directory could not be read: ', articleDir);
console.error(err);
return next();
}
// Iterate the files
- var i = 0,
- len = fileNames.length,
- fileName,
- fileParts,
- articleName,
- fileFound = false;
+ var articleUrl = url.slice(baseIndex + 1),
+ i = 0,
+ len = articles.length,
+ article,
+ articleFound = false;
for (; i < len; i++) {
- fileName = fileNames[i];
-
- // Remove the extension from the file
- fileParts = fileName.split('.');
- if (fileParts.length > 1) {
- fileParts.pop();
- }
+ article = articles[i];
// If it matches our article url, save it
- articleName = fileParts.join('.');
- if (articleUrl.indexOf(articleName) !== -1) {
- fileFound = true;
+ if (articleUrl.indexOf(article.url) !== -1) {
+ articleFound = true;
break;
}
}
- // If the file was found, interpret it
- if (fileFound) {
- fs.readFile(path.join(articleDir, fileName), 'utf8', function (err, file) {
- // If there was an error, log it and call the next method
- if (err) {
- console.error('Article could not be read: ', fileName);
- console.error(err);
- return next();
- }
-
- var formatter = require(formatEngine);
-
- // If the engine is showdown, get the proper formatter
- if (formatEngine === 'showdown') {
- // This is so wrong... on so many levels... =_=
- var tempThis = {};
- formatter.Showdown.converter.call(tempThis);
- formatter = tempThis.makeHtml;
- }
-
- // TODO: This is what cloudhead uses but I am not too fond of it
- // Find where the JSON ends (denoted by a double line break)
- var dblLineBreakIndex = file.search(/\n\r?\n/g);
-
- // Fallback the dblLineBreakIndex
- if (dblLineBreakIndex === -1) {
- dblLineBreakIndex = file.length;
- }
-
- // Break up the properties and content
- var propsStr = file.slice(0, dblLineBreakIndex),
- props = new Function('return ' + propsStr + ';')(),
- rawContent = file.slice(dblLineBreakIndex);
-
- // Render the content via the formatter
- var content = formatter(rawContent),
- // TODO: Allow for non-json parser (e.g. yaml)
- renderObj = JSON.parse(propsStr);
-
- // Save the content to the renderObj
- renderObj.content = content;
-
- // TODO: Proper config item
- renderObj.config = app.settings;
-
- // If there is an articleView, render through it
- if (articleView !== undefined) {
- // TODO: Allow for alternative jojo render engine?
- res.render(articleView, renderObj);
- } else {
- // Otherwise, use res.send
- res.send(content);
- }
- });
+ // If the article was found, use it
+ if (articleFound) {
+ // TODO: Proper config item
+ article.config = app.settings;
+
+ // If there is an articleView, render through it
+ if (articleView !== undefined) {
+ // TODO: Allow for alternative jojo render engine?
+ res.render(articleView, article);
+ } else {
+ // Otherwise, send the content
+ res.send(article.content);
+ }
} else {
// Otherwise, call the next method
next();
@@ -141,6 +97,7 @@ function jojo(req, res, next) {
});
}
}
+
// Otherwise, call the next method
next();
}
@@ -160,5 +117,118 @@ jojo.createServer = function () {
// TODO: jojo.static which preloads views and does not dynamically fetch during each request
+// State (and sanity) preserver for jojo
+function Yoyo(app) {
+ // Fallback app
+ app = app || {'settings': {}};
+
+ // Save app to this
+ this.app = app;
+}
+Yoyo.prototype = {
+ 'readArticles': function (articleDir, callback) {
+ var that = this;
+ fs.readdir(articleDir, function (err, articles) {
+ // If there is an error, log and callback with it
+ if (err) {
+ console.error('Article directory could not be read: ', articleDir);
+ return callback(err);
+ }
+
+ // Otherwise, read in all of the articles
+ async.map(articles, function (article, callback) {
+ var articlePath = path.join(articleDir, article);
+ that.readArticle(articlePath, callback);
+ }, callback);
+ });
+ },
+ 'readArticle': function (articlePath, callback) {
+ var that = this;
+ fs.readFile(articlePath, 'utf8', function (err, article) {
+ // If there was an error, log it
+ if (err) {
+ console.error('An article could not be read: ', articlePath);
+ return callback(err);
+ }
+
+ // Otherwise, parse the article
+ var parsedArticle = that.parseArticle(article);
+
+ // Callback with the article
+ callback(null, parsedArticle);
+ });
+ },
+ 'parseArticle': function (article) {
+ var app = this.app,
+ settings = app.settings,
+ dataEngine = settings['jojo data parser'] || 'json',
+ formatEngine = settings['jojo formatter'] || 'showdown',
+ formatter = require(formatEngine),
+ dataParser = dataEngine === 'json' ? JSON.parse : require(dataEngine);
+
+ // If the engine is showdown, get the proper formatter
+ if (formatEngine === 'showdown') {
+ // This is so wrong... on so many levels... =_=
+ var tempThis = {};
+ formatter.Showdown.converter.call(tempThis);
+ formatter = tempThis.makeHtml;
+ }
+
+ // TODO: This is what cloudhead uses but I am not too fond of it
+ // Find where the JSON ends (denoted by a double line break)
+ var dblLineBreakIndex = article.search(/\n\r?\n/g);
+
+ // Fallback the dblLineBreakIndex
+ if (dblLineBreakIndex === -1) {
+ dblLineBreakIndex = article.length;
+ }
+
+ // Break up the properties and content
+ var propsStr = article.slice(0, dblLineBreakIndex),
+ props = new Function('return ' + propsStr + ';')(),
+ rawContent = article.slice(dblLineBreakIndex);
+
+ // Render the content via the formatter
+ var content = formatter(rawContent),
+ retObj = dataParser(propsStr);
+
+ // Save the content to the renderObj
+ retObj.content = content;
+
+ // Fallback the url
+ retObj.url = retObj.url || jojo.getUrl(retObj);
+
+ // Return the parsed object
+ return retObj;
+ }
+};
+
+// Expose Yoyo via jojo
+jojo.Yoyo = Yoyo;
+
+// Create a overwritable helper function for generating article URLs
+jojo.getUrl = function (article) {
+ var urlParts = [],
+ date = article.date;
+ if (date) {
+ urlParts.push(date.replace(/\//g, '-'));
+ }
+ urlParts.push(article.title.replace(/\s+/g, '-'));
+
+ return urlParts.join('-').toLowerCase();
+}
+
+// Create helper methods for reading and parsing articles
+var emptyYoyo = new Yoyo();
+jojo.readArticles = function (articleDir, callback) {
+ emptyYoyo.readArticles(articleDir, callback);
+};
+jojo.readArticle = function (articlePath, callback) {
+ emptyYoyo.readArticle(articlePath, callback);
+};
+jojo.parseArticle = function (article) {
+ emptyYoyo.parseArticle(article);
+};
+
// Export jojo
module.exports = jojo;
View
4 views/layout.ejs
@@ -1,8 +1,8 @@
<!doctype html>
<html>
<head>
- <link rel="stylesheet" type="text/css" href="/css/main.css">
- <link rel="alternate" type="application/atom+xml" title="<%= title %> - feed" href="/index.xml" />
+ <!-- <link rel="stylesheet" type="text/css" href="/css/main.css"> -->
+ <!-- <link rel="alternate" type="application/atom+xml" title="<%= title %> - feed" href="/index.xml" /> -->
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title><%= title %></title>
</head>

No commit comments for this range

Something went wrong with that request. Please try again.