Permalink
Browse files

Merge pull request #94 from nodejitsu/handbook-async

Provide async methods
  • Loading branch information...
2 parents fda2ae0 + c17fd3a commit c5cb455c8b9264df90150c5c76ad495430b714fb @3rd-Eden 3rd-Eden committed Mar 11, 2013
Showing with 103 additions and 21 deletions.
  1. +1 −2 content/index.md
  2. +102 −19 index.js
View
@@ -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.
---
View
121 index.js
@@ -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\((.+?)\)(?!\))/
@@ -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
@@ -52,7 +78,6 @@ function frequency(content) {
return words;
}
-
//
// ### function tags()
// #### @content {String} Document content
@@ -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
@@ -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.