Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Medias support

Basic caching system: articles on disk, metas in RAM
Do not require a config file, pass a config module instead
Pass config.blogLang to templates
  • Loading branch information...
commit ad1a64e10a8768578d36e2a3a8aa4012fc3f341c 1 parent 8391584
@bpierre authored
View
9 README.md
@@ -7,10 +7,17 @@ blo is a simple blog system: no database required, just plain old HTML files.
blo works as a connect middleware.
It takes one parameter, which is your blog directory.
+Install it with:
+ $ npm install blo
+
+Create a demo blog:
+ $ node node_modules/blo/bin/blo skel
+ $ npm install .
+
Here is my app.js file:
var connect = require('connect'),
- blo = require('./blo/lib/blo');
+ blo = require('blo');
var server = connect(
connect.favicon(__dirname + '/public/favicon.ico'),
View
101 lib/blo.js
@@ -22,78 +22,141 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
-function blo(dir) {
+function blo(conf) {
var connect = require('connect'),
Step = require('step'),
+ fs = require('fs'),
Article = require('./blo/article'),
template = require('./blo/template'),
cache = require('./blo/cache'),
- conf = require(dir + '/config'),
articles = [];
return connect.router(function(app){
- // Conf
+ /* Conf */
cache.config(conf);
Article.config(conf);
+ /* Articles */
+ function getArticleTime(article, callback) {
+ fs.stat(conf.articlesDir + '/' + article.permalink + '.html' , function(err, stats) {
+ callback(stats.mtime);
+ });
+ }
+ function loadArticleMetas(article, callback) {
+ article.getMetas(function(err, article){
+ article.metas = metas;
+ callback();
+ });
+ }
+
+ function getArticleMetas(article, callback) {
+ //hasArticleChanged(article, function(){
+ //
+ //});
+ }
+
+ function updateArticleCache(article, callback) {
+ article.getMetas(function(err, metas) {
+ article.metas = metas;
+ article.getContent(function(err, content) {
+ cache.set(article.permalink, content, function(err) {
+ if (err) throw err;
+ callback(metas, content);
+ });
+ }, metas);
+ });
+ }
+
+ function getCachedArticleContent(article, callback) {
+ getArticleTime(article, function(mtime) {
+ if (!article.mtime || (article.mtime - mtime !== 0)) { // Article modified: update
+ console.log("Article modified: update cache.");
+ updateArticleCache(article, function(metas, content){
+ article.mtime = mtime;
+ callback(content);
+ });
+ } else {
+ console.log("Article cached.");
+ cache.get(article.permalink, function(err, content){
+ callback(content);
+ });
+ }
+ });
+ }
+
/* Routes */
function routes() {
// Home
app.get('/', function(req, res, next){
-
var context = {
blogName: conf.blogName,
+ blogLang: conf.lang,
articles: articles.sort(function(a, b){
return b.metas.date - a.metas.date;
}).slice(0, 10)
};
-
- template.render("home", context, conf.skinDir, function(err, body) {
+ template.render("base", context, conf.skinDir, function(err, body) {
if (err) throw err;
- res.writeHead(200, { 'Content-Type': 'text/html' });
- res.end(body);
+ template.render("home", context, conf.skinDir, function(err, body) {
+ if (err) throw err;
+ res.writeHead(200, { 'Content-Type': 'text/html' });
+ res.end(body);
+ });
});
});
// Article
app.get('/:title', function(req, res, next){
-
function showArticle(article) {
- article.getContent(function(err, content) {
- if (err) throw err;
+ getCachedArticleContent(article, function(content){
var context = {
blogName: conf.blogName,
+ blogLang: conf.lang,
article: {
metas: article.metas,
content: content,
}
};
- template.render("article", context, conf.skinDir, function(err, body) {
+ template.render("base", context, conf.skinDir, function(err, body) {
if (err) throw err;
- res.writeHead(200, { 'Content-Type': 'text/html' });
- res.end(body);
+ template.render("article", context, conf.skinDir, function(err, body) {
+ if (err) throw err;
+ res.writeHead(200, { 'Content-Type': 'text/html' });
+ res.end(body);
+ });
});
});
}
-
for (var i = 0; i < articles.length; i++) {
- console.log(articles[i].permalink, req.params.title);
if (articles[i].permalink === req.params.title) {
return showArticle(articles[i]);
}
}
return next();
});
+
+ app.get('/:title/:media', function(req, res, next){
+ connect.static.send(req, res, next, {
+ root: conf.articlesDir,
+ path: req.params.title + '/' + req.params.media,
+ callback: function(err){
+ if (err && err.code === 'ENOENT') {
+ return next();
+ }
+ }
+ });
+ });
}
// Load articles metas
- Article.getPermalinks(function(err, permalinks){
- function articleLoaded(err, article){
- article.getMetas(function(err, metas){
+ Article.getPermalinks(function(err, permalinks) {
+ function articleLoaded(err, article) {
+ article.getMetas(function(err, metas) {
article.metas = metas;
+ // article.mtime = mtime;
articles.push(article);
if (articles.length === permalinks.length) {
routes();
View
4 lib/blo/article.js
@@ -21,12 +21,12 @@ Article.prototype.getMetas = function(callback) {
});
};
-Article.prototype.getContent = function(callback) {
+Article.prototype.getContent = function(callback, metas) {
this.domExec(function(err, window) {
conf.readArticle(window, function(err, content) {
if (err) throw err;
return callback(err, content);
- });
+ }, metas);
});
};
View
30 lib/blo/cache.js
@@ -0,0 +1,30 @@
+var fs = require('fs'),
+ conf = null;
+
+function config(c) {
+ conf = c;
+}
+
+function set(key, val, callback) {
+ if (!conf) {
+ return callback(new Error("You must call config() before"));
+ }
+ fs.writeFile(conf.cacheDir + '/' + key, val, 'utf8', function (err) {
+ return callback(err);
+ });
+}
+
+function get(key, callback) {
+ if (!conf) {
+ return callback(new Error("You must call config() before"));
+ }
+ fs.readFile(conf.cacheDir + '/' + key, 'utf8', function (err, data) {
+ return callback(err, data);
+ });
+}
+
+module.exports = {
+ set: set,
+ get: get,
+ config: config
+};
View
8 lib/blo/template.js
@@ -1,6 +1,6 @@
var fs = require("fs"),
dust = require('dust'),
- compiled = {};
+ compiledTpls = {};
// Disable whitespace compression
dust.optimizers.format = function(ctx, node) { return node; };
@@ -10,8 +10,10 @@ function compileTemplate(name, dir, callback) {
if (err) {
throw err;
}
+
var compiled = dust.compile(data, name);
dust.loadSource(compiled);
+
return callback(compiled);
});
}
@@ -23,9 +25,9 @@ function renderCached(name, context, callback) {
}
function render(name, context, dir, callback) {
- if (!compiled[name]) {
+ if (!compiledTpls[name]) {
compileTemplate(name, dir, function(dustTpl){
- compiled[name] = dustTpl;
+ compiledTpls[name] = dustTpl;
renderCached(name, context, callback);
});
} else {
View
15 package.json
@@ -3,18 +3,19 @@
"description": "HTML blog system",
"tags": ["node","blog"],
"version": "0.1.0",
- "author": "Pierre Bertet <bonjour@pierrebertet.net>",
+ "author": "Pierre Bertet <bonjour@pierrebertet.net> (http://pierrebertet.net)",
"repository": {
"type": "git",
"url": "http://github.com/bpierre/blo.git"
},
"bugs": "http://github.com/bpierre/blo/issues",
- "engines": ["node >= 0.4.0"],
+ "engines": { "node": ">= 0.4.1 < 0.5.0" },
"main": "lib/blo.js",
"dependencies": {
"connect": ">= 1.4.0 < 2.0.0",
- "dust": ">= 0.3.0",
- "jsdom": ">= 0.2.0",
- "step": ">= 0.0.4"
- }
-}
+ "dust": ">= 0.3.0 < 1.0.0",
+ "jsdom": ">= 0.2.0 < 1.0.0",
+ "step": ">= 0.0.4 < 1.0.0"
+ },
+ "bin": { "blo": "./bin/blo" }
+}
Please sign in to comment.
Something went wrong with that request. Please try again.