Skip to content

Commit

Permalink
Merge pull request #94 from nodejitsu/handbook-async
Browse files Browse the repository at this point in the history
Provide async methods
  • Loading branch information
3rd-Eden committed Mar 11, 2013
2 parents fda2ae0 + c17fd3a commit c5cb455
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 21 deletions.
3 changes: 1 addition & 2 deletions content/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ where to get help when you need it.
This is a living document which you can submit patches to at
[http://github.com/nodejitsu/handbook](http://github.com/nodejitsu/handbook).
Note that this entire website is generated from the individual content files in
the `/content` folder, so any edits should be made to those source files, not
`/public/*.html`.
the `/content` folder, so any edits should be made to those files.

---

Expand Down
121 changes: 102 additions & 19 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ var path = require('path'),
fs = require('fs'),
natural = require('natural'),
tokenizer = new natural.WordTokenizer(),
loc = path.resolve(__dirname, 'content'),
scraper = {
title: /\[meta:title\]:\s<>\s\((.+?)\)(?!\))/,
description: /\[meta:description\]:\s<>\s\((.+?)\)(?!\))/
Expand All @@ -22,6 +23,31 @@ function scrape(content, key) {
return match ? match[1].trim() : '';
}

//
// ### function normalize()
// #### @file {String} file name
// Normalize the file name to resolve to a Markdown or index file.
//
function normalize(file) {
if (!file) file = 'index.md';

return ~file.indexOf('.md') ? file : file + '.md';
}

//
// ### function fileContent()
// #### @content {String} Document content
// Sugar content with additional properties from scraped content.
//
function fileContent(content) {
return {
content: content,
description: scrape(content, 'description'),
title: scrape(content, 'title'),
tags: tags(content, 10)
};
}

//
// ### function frequency()
// #### @content {String} Document content
Expand Down Expand Up @@ -52,7 +78,6 @@ function frequency(content) {
return words;
}


//
// ### function tags()
// #### @content {String} Document content
Expand All @@ -67,6 +92,65 @@ function tags(content, n) {
});
}

//
// ### function walk()
// #### @dir {String} Directory path to crawl
// #### @result {Object} Append content to current results
// #### @callback {Function} Callback for sub directory
// #### @sub {Boolean} Is directory subdirectory of dir
// Recusive walk of directory by using asynchronous functions, returns
// a collection of markdown files in each folder.
//
function walk(dir, callback, result, sub) {
var current = sub ? path.basename(dir) : 'index';

// Prepare containers.
result = result || {};
result[current] = {};

// Read the current directory
fs.readdir(dir, function readDir(error, list) {
if (error) return callback(error);

var pending = list.length;
if (!pending) return callback(null, result);

list.forEach(function loopFiles(file) {
file = dir + '/' + file;

fs.stat(file, function statFile(error, stat) {
var name, ref;

if (stat && stat.isDirectory()) {
walk(file, function dirDone() {
if (!--pending) callback(null, result);
}, result, true);
} else {
// Only get markdown files from the directory content.
if (path.extname(file) !== '.md') return;

ref = path.basename(file, '.md');
name = ['/' + path.basename(dir), ref];

// Only append the name of the file if not index
if (ref === 'index') name.pop();

// Get the tile of the file.
get(file, function getFile(err, file) {
result[current][ref] = {
href: sub ? name.join('/') : '',
title: file.title,
path: dir
};

if (!--pending) callback(null, result);
});
}
});
});
});
}

//
// ### function walkSync()
// #### @dir {String} Directory path to crawl
Expand Down Expand Up @@ -118,35 +202,34 @@ function walkSync(dir, result, sub) {
// ### function get()
// #### @file {String} Filename
// #### @callback {Function} Callback for file contents
// Returns file content by normalized path
// Returns file content by normalized path, if a callback is provided, content
// is returned asynchronously.
//
function get(file, callback) {
if (!!file) {
file = !!~file.indexOf('.md') ? file : file + '.md';
} else {
file = 'index.md';
if (!callback) {
return fileContent(fs.readFileSync(path.resolve(loc, normalize(file)), 'utf8'));
}

var content = fs.readFileSync(path.resolve(__dirname + '/content/', file), 'utf8');
file = path.resolve(loc, normalize(file));

return {
content: content,
description: scrape(content, 'description'),
title: scrape(content, 'title'),
tags: tags(content, 10)
};
fs.readFile(file, 'utf8', function read(error, content) {
callback.apply(this, [error, fileContent(content)]);
});
}

//
// ### function catalogSync()
// Returns a catalog by parsing the content directory. Titles are stripped from
// meta-title inside the .md file, defaults to filename.
function catalogSync() {
return walkSync(path.resolve(__dirname, 'content'));
// ### function catalog()
// #### @callback {Function} Callback for catalog/TOC
// Returns a catalog by parsing the content directory, if a callback is provided
// content is returned asynchronously.
function catalog(callback) {
if (!callback) return walkSync(loc);

walk(loc, callback);
}

// Expose public functions.
module.exports = {
get: get,
catalogSync: catalogSync
catalog: catalog
};

0 comments on commit c5cb455

Please sign in to comment.