File Database
04/11/15
Okay, so last time I printed out all the links on blog/index.html in chronological order, realized some links were still absolute paths, and made them relative. Now, I can get back to working towards some infrastructure that will let me construct index.html dynamically.
An important bit of infrastructure required is to have a proper list of the blogposts in the order of their placement on the page.
/*jslint node: true */
(function () {
"use strict";
var fs = require('fs'),
$ = require('cheerio'),
generateLinks;
generateLinks = function (readfile, savename) {
var saveLinks;
saveLinks = function (err, data) {
var links = [],
linkText;
if (err) {
return console.log(err);
}
$('.blogpost', data).each(function () {
var blogpost = $(this);
links.push(blogpost.find('h1 a').attr('href'));
});
linkText = links.reverse().join('\n');
fs.writeFile(savename, linkText, 'utf-8', function (err) {
if (err) {
return console.log(err);
}
});
};
fs.readFile(readfile, 'utf-8', saveLinks);
};
generateLinks('public/blog/index.html', 'database/blog.txt');
generateLinks('public/diary/index.html', 'database/diary.txt');
}());
All right, this even caught a mistake where I didn't link a plan document in index.html to its individual file. Hooray for scripts that clean and DRY.
Okay, from here I have what I need to generate an index.html. Let's generate one in a test area first, so I don't lose anything I care about.
/*jslint node: true */
(function () {
"use strict";
var indexGenerate;
indexGenerate = function (area) {
var fs = require('fs'),
path = require('path'),
database;
database = path.join('database', area + '.txt');
fs.readFile(database, 'utf-8', function (err, data) {
var $ = require('cheerio'),
filenames,
index,
blogDIV,
readBlogFile;
if (err) {
return console.log(err);
}
readBlogFile = function (filename) {
var blogHTML,
fullpath;
fullpath = path.join('public', area, filename);
blogHTML = fs.readFileSync(fullpath, 'utf-8');
return $('.blogpost', blogHTML);
};
// Order matters, do synchronously
filenames = data.split('\n');
for (index = filenames.length - 1; index >= 0; index -= 1) {
blogDIV = readBlogFile(filenames[index]);
console.log($(blogDIV).children('h1').text().trim());
}
});
};
indexGenerate('blog');
//indexGenerate('diary');
}());
Okay, so this file kind've mirrors the previous one, as well it should, as it basically does the same thing, but backwards. Anyways, this managed two things, it taught me that jslint flags the use of synchronous functions (I have an idea of how to do it asynchronously now), and also that two posts in index.html were missing their linked to files. The files just didn't exist. Two in a row.
Error: ENOENT, no such file or directory 'public/blog/of_people_and_systems.html'
Error: ENOENT, no such file or directory 'public/blog/free_to_play_customer_product_employee.html'
This happened in two separate runs. Luckily, all I needed to do to generate the posts was delete the link to the posts and run my generate links script through index.html again. Problem solved. Thanks past me for that tool!
All right, from here let's generate a index.html from the blogposts.
/*jslint node: true */
(function () {
"use strict";
var indexGenerate;
indexGenerate = function (area) {
var fs = require('fs'),
path = require('path'),
database;
database = path.join('database', area + '.txt');
fs.readFile(database, 'utf-8', function (err, data) {
var $ = require('cheerio'),
filenames,
index,
blogDIV,
readBlogFile,
indexHTML;
if (err) {
return console.log(err);
}
readBlogFile = function (filename) {
var blogHTML,
fullpath;
fullpath = path.join('public', area, filename);
blogHTML = fs.readFileSync(fullpath, 'utf-8');
return $('.blogpost', blogHTML);
};
// Order matters, do synchronously
// TODO: do async with index counting and building an array
filenames = data.split('\n');
indexHTML = '';
for (index = filenames.length - 1; index >= 0; index -= 1) {
blogDIV = readBlogFile(filenames[index]);
indexHTML += $(blogDIV).html();
}
fs.writeFile(path.join('public/test', area, 'index.html'), indexHTML, 'utf-8', function (err) {
if (err) {
console.log(err);
}
});
});
};
indexGenerate('blog');
//indexGenerate('diary');
}());
This generates a public/test/blog/index.html with all the appropriate blog content, but no other appropriate html. Let's make templates for the blog and diary index files such that the blog post html can be inserted into the content div and that'll be done.
/*jslint node: true */
(function () {
"use strict";
var indexGenerate;
indexGenerate = function (area) {
var fs = require('fs'),
path = require('path'),
database;
database = path.join('database', area + '.txt');
fs.readFile(database, 'utf-8', function (err, data) {
var $ = require('cheerio'),
filenames,
index,
blogDIV,
readBlogFile,
template;
if (err) {
return console.log(err);
}
readBlogFile = function (filename) {
var blogHTML,
fullpath;
fullpath = path.join('public', area, filename);
blogHTML = fs.readFileSync(fullpath, 'utf-8');
return $('#content', blogHTML).html();
};
// Order matters, do synchronously
// TODO: do async with index counting and building an array
filenames = data.split('\n');
blogDIV = '';
for (index = filenames.length - 1; index >= 0; index -= 1) {
blogDIV += readBlogFile(filenames[index]);
}
template = path.join('templates', area + '.html');
fs.readFile(template, 'utf-8', function (err, data) {
var indexHTML,
indexPath;
if (err) {
return console.log(err);
}
indexHTML = $(data);
$('#content', indexHTML).html(blogDIV);
indexPath = path.join('public/test', area, 'index.html');
fs.writeFile(indexPath, indexHTML, 'utf-8', function (err) {
if (err) {
console.log(err);
}
});
});
});
};
indexGenerate('blog');
//indexGenerate('diary');
}());
This works! Though, I don't have a style.css in that directory, so styling is a little funky. I really should just use a single master css, I think. Which revealed an interesting property: Second Try Starting Now had a link to itself in its file. Also, I still need to actually link to the individual posts in the generated index.html.
How did I do it in my createLinks.js script?
title = function (blogpost) {
return blogpost.children('h1').text().trim();
};
blogpost.children('h1').html(
'\n <a href="' + filename(blogpost) + '">\n'
+ ' ' + title(blogpost) + '</a>\n'
+ ' '
);
The title function will be useful. I already have the filename necessary, so the filename generator function not needed.
readBlogFile = function (filename) {
var blogHTML,
fullpath,
title;
fullpath = path.join('public', area, filename);
blogHTML = $(fs.readFileSync(fullpath, 'utf-8'));
title = blogHTML.find('h1').text().trim();
blogHTML.find('h1').html(
'\n <a href="' + filename + '">\n'
+ ' ' + title + '</a>\n'
+ ' '
);
return blogHTML.find('#content').html();
};
And now each post is linked to! Before I actually replace the actual index files in the blog and diary areas, though, let's commit my files as they exist right now.
Nope, git is revealing some problems with my script. My impress.js post got nullified. The impress.js has a whole bunch of special stuff I need to consider.... For right now let's remove all id attributes in blogpost divs (collisions could be a problem in the index.html file, removal is easier for now).
readBlogFile = function (filename) {
var blogHTML,
fullpath,
title,
idRemove;
fullpath = path.join('public', area, filename);
blogHTML = $(fs.readFileSync(fullpath, 'utf-8'));
title = blogHTML.find('h1').text().trim();
blogHTML.find('h1').html(
'\n <a href="' + filename + '">\n'
+ ' ' + title + '</a>\n'
+ ' '
);
idRemove = function () {
if ($(this).attr('id')) {
$(this).removeAttr('id');
}
$(this).children().each(idRemove);
};
blogHTML.find('.blogpost').each(idRemove);
return blogHTML.find('#content,#temp').html();
};
This works. I forget... why did I make that div temp for the impress.js post? Let's undo it and see what happens. That's right, it screws up the slide-show. This'll be something I need to fix when I start learning CSS proper.
Yep, this is functional. Let's clean it up a bit and make a proper blog post out of this setup. Right now it's:
/*jslint node: true */
(function () {
"use strict";
var indexGenerate;
indexGenerate = function (area) {
var fs = require('fs'),
path = require('path'),
database;
database = path.join('database', area + '.txt');
fs.readFile(database, 'utf-8', function (err, data) {
var $ = require('cheerio'),
filenames,
index,
blogDIV,
readBlogFile,
template;
if (err) {
return console.log(err);
}
readBlogFile = function (filename) {
var blogHTML,
fullpath,
title,
idRemove;
fullpath = path.join('public', area, filename);
blogHTML = $(fs.readFileSync(fullpath, 'utf-8'));
title = blogHTML.find('h1').text().trim();
blogHTML.find('h1').html(
'\n <a href="' + filename + '">\n'
+ ' ' + title + '</a>\n'
+ ' '
);
idRemove = function () {
if ($(this).attr('id')) {
$(this).removeAttr('id');
}
$(this).children().each(idRemove);
};
blogHTML.find('.blogpost').each(idRemove);
return blogHTML.find('#content,#temp').html();
};
// Order matters, do synchronously
// TODO: do async with index counting and building an array
filenames = data.split('\n');
blogDIV = '';
for (index = filenames.length - 1; index >= 0; index -= 1) {
blogDIV += readBlogFile(filenames[index]);
}
template = path.join('templates', area + '.html');
fs.readFile(template, 'utf-8', function (err, data) {
var indexHTML,
indexPath;
if (err) {
return console.log(err);
}
indexHTML = $(data);
$('#content', indexHTML).html(blogDIV);
indexPath = path.join('public', area, 'index.html');
fs.writeFile(indexPath, indexHTML, 'utf-8', function (err) {
if (err) {
console.log(err);
}
});
});
});
};
indexGenerate('blog');
indexGenerate('diary');
}());
Really, I want to make it so that readFileSync is just readFile, and I want to make it as elegant as I'm able while I'm at it.
/*jslint node: true */
(function () {
'use strict';
var blogpostRead,
readBlogFile,
indexGenerate,
$ = require('cheerio'),
path = require('path'),
fs = require('fs');
indexGenerate = function (template, blogDIV, indexPath) {
fs.readFile(template, 'utf-8', function (err, data) {
var indexHTML;
if (err) {
return console.log(err);
}
indexHTML = $(data);
$('#content', indexHTML).html(blogDIV);
fs.writeFile(indexPath, indexHTML, 'utf-8', function (err) {
if (err) {
console.log(err);
}
});
});
};
readBlogFile = function (fullpath, callback) {
fs.readFile(fullpath, 'utf-8', function (err, data) {
var blogHTML,
title,
idRemove;
if (err) {
return console.log(err);
}
blogHTML = $(data);
title = blogHTML.find('h1').text().trim();
blogHTML.find('h1').html(
'\n <a href="' + path.basename(fullpath) + '">\n'
+ ' ' + title + '</a>\n'
+ ' '
);
idRemove = function () {
if ($(this).attr('id')) {
$(this).removeAttr('id');
}
$(this).children().each(idRemove);
};
blogHTML.find('.blogpost').each(idRemove);
callback(blogHTML.find('#content,#temp').html());
});
};
blogpostRead = function (area) {
var database;
database = path.join('database', area + '.txt');
fs.readFile(database, 'utf-8', function (err, data) {
var filenames,
blogs,
count;
if (err) {
return console.log(err);
}
filenames = data.split('\n');
blogs = [];
count = 0;
filenames.forEach(function (filename, index) {
var fullpath,
template,
indexPath;
fullpath = path.join('public', area, filename);
readBlogFile(fullpath, function (blogHTML) {
blogs[filenames.length - index - 1] = blogHTML;
count += 1;
if (count === filenames.length) {
template = path.join('templates', area + '.html');
indexPath = path.join('public', area, 'index.html');
indexGenerate(template, blogs.join(''), indexPath);
}
});
});
});
};
blogpostRead('blog');
blogpostRead('diary');
}());
I like it! Each function is short enough to fit on a page, I kept it all asynchronous.
This is worthy of a blog post now, I think.