Add build.lua now that all Prosody-specific things are out of it

Mon, 18 May 2009 15:20:52 +0100

author
Matthew Wild <mwild1@gmail.com>
date
Mon, 18 May 2009 15:20:52 +0100
changeset 1
74deddff1202
parent 0
b40ca010c49c
child 2
80eb1f7784c4

Add build.lua now that all Prosody-specific things are out of it

build.lua file | annotate | diff | comparison | revisions
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/build.lua	Mon May 18 15:20:52 2009 +0100
@@ -0,0 +1,196 @@
+require "LuaTemplates.templates"
+require "LuaTemplates.parsers"
+require "lfs"
+
+-- Config --
+
+dofile(arg[1] or ".blogrc")
+
+posts_dir = posts_dir or "_posts/"
+layouts_dir = layouts_dir or "_layouts/"
+-- - -- - -- 
+
+-- Logging --
+function make_msg(prefix, name) return function (fmt, ...) print(prefix, string.format(fmt, ...)); end end
+msg_info, msg_warn, msg_error = make_msg("II", "info"), make_msg("WW", "warn"), make_msg("EE", "error");
+-- -- - -- --
+
+-- Append '/' to end of posts_dir if necessary
+posts_dir = posts_dir:gsub("([^/])$", "%1/")
+
+-- Append '/' to end of base_url if necessary
+base_url = base_url:gsub("([^/])$", "%1/")
+
+posts = {};
+
+
+-- Read posts, interpret
+for post_filename in lfs.dir(posts_dir:gsub("/$", "")) do
+	-- Ignore dotfiles
+	if not post_filename:match("^%.") then
+		msg_info("Processing %s", post_filename);
+		local post_file, err = io.open(posts_dir..post_filename);
+		if post_file then
+			local post_format = post_filename:match("%.(%w+)") or "text";
+			local post = { filename = post_filename, path = posts_dir..post_filename, format = post_format };
+			if not parsers[post_format] then
+				msg_warn("Post format '%s' not supported", post_format);
+			else
+				local inside_header, line_num = nil, 0;
+				for line in post_file:lines() do
+					line_num = line_num + 1;
+					if  line:match("^%-%-%-") then
+						if not inside_header then
+							inside_header = true;
+						else
+							inside_header = false;
+							break;
+						end
+					elseif not inside_header then
+						break;
+					else
+						local k,v = line:match("^(%w+):%s?(.*)$");
+						if k and v then
+							post[k] = v;
+						else
+							msg_warn("Couldn't parse header at line %d", line_num);
+						end
+					end
+				end
+			end	
+		
+			local post_data, err = post_file:read("*a");
+
+			-- Parse post_data according to extension, add to post list
+			--msg_info("Parsing %s...", post_filename) --:gsub("%.[^%.]+$", ""):match("[^/]+"));
+			post.content = templates.load(parsers[post_format](post_data)):render{ page = post };
+			post.shorttitle = post.title:gsub("%W+", "-"):lower():sub(1,45)
+			if #post.title:gsub("%W+", "-") > 45 then
+				post.shorttitle = post.shorttitle:gsub("%-%w+$", "");
+			end
+			post.url = base_url..post.shorttitle.."/"
+			post.post = post
+			post.updated = post.updated or os.date("!%Y-%m-%dT%H:%M:%SZ", lfs.attributes(post.path).modification);
+			if post.published then
+				if not post.published:match("^%d%d%d%d%-%d%d%-%d%dT%d%d:%d%d%:%d%dZ$") then
+					msg_error("Post has invalid published date");
+				end
+			end
+			table.insert(posts, post);
+		else	
+			msg_error("Failed to open %s, %s", post_filename, err);
+		end
+	end
+end
+
+local layouts = setmetatable({ default = templates.init(layouts_dir.."default.html"); }, 
+	{ __index = function (t, k) rawset(t, k, templates.init(layouts_dir..k..".html")); return rawget(t, k); end });
+
+table.sort(posts, function (a, b)
+			do return (a.updated or "") < (b.updated or ""); end
+			if not a.updated then return true; end
+			if not b.updated then return false; end
+			local a_year, a_month, a_day = a.updated:match("(%d%d%d%d)%-(%d%d)%-(%d%d)");
+			local b_year, b_month, b_day = b.updated:match("(%d%d%d%d)%-(%d%d)%-(%d%d)");
+			if b_year >= a_year then return true; end
+			if b_month >= a_month then return true; end
+			if b_day >= a_day then return true; end
+			return false;
+		end);
+
+do
+	local shorttitles = {};
+	for _, post in ipairs(posts) do
+		while shorttitles[post.shorttitle] do
+			post.shorttitle = post.shorttitle:gsub("%~%d+$", "").."~"..((tonumber(post.shorttitle:match("%~(%d*)$")) or 0) + 1)
+			post.url = base_url..post.shorttitle.."/"
+		end
+		shorttitles[post.shorttitle] = post;
+	end
+end
+
+msg_info("%s post%s processed", #posts, #posts ~= 1 and "s" or "");
+
+msg_info(" - - - ");
+
+local unpublished = 0;
+for n, post in ipairs(posts) do
+	if post.content and (not post.layout or layouts[post.layout]) then
+		lfs.mkdir(post.shorttitle);
+		local outfile, err = io.open(post.shorttitle.."/index.html", "w+");
+		if outfile then
+			local layout = layouts[post.layout or "default"];
+			
+			-- Link to the previous and next published posts
+			local i = 1;
+			repeat
+				post.next = posts[n+i];
+				i = i + 1;
+			until (not post.next) or post.next.published;
+			i = 1;
+			repeat
+				post.previous = posts[n-i];
+				i = i + 1;
+			until (not post.previous) or post.previous.published;
+			
+			post.id = n;
+			outfile:write(layout:render(post));
+			outfile:close();
+			if not post.published then
+				-- Not published yet, hide it
+				--msg_info("Hiding unpublished post");
+				unpublished = unpublished + 1;
+				posts[n] = nil;
+			end
+		else
+			msg_error("Failed to write HTML: %s", err);
+		end
+		if post.published then
+			msg_info("Published: %s", post.shorttitle);
+		else
+			msg_info("Generated: %s", post.shorttitle);
+		end
+	end
+end
+
+-- Do main page
+local main_tpl = templates.init(layouts_dir.."/main.html");
+
+main_tpl:saverender("index.html", { title = blog_title, posts = posts });
+
+msg_info("%s post%s published, %d post%s unpublished", #posts, #posts ~= 1 and "s" or "", unpublished, unpublished ~= 1 and "s" or "");
+
+msg_info(" - - - ");
+
+-- Do feed
+local atom = io.open("feed/atom.xml.new", "w+");
+atom:write[[<?xml version="1.0" encoding="UTF-8"?>
+	<feed xmlns="http://www.w3.org/2005/Atom">
+]]
+atom:write("\t<id>", base_url, "</id>\n");
+atom:write("\t<title>", blog_title, "</title>\n");
+atom:write("\t<subtitle>", blog_subtitle, "</subtitle>\n");
+atom:write("\t<link href='", base_url, "feed/atom.xml' rel='self' />\n");
+atom:write("\t<link href='", base_url, "' />\n");
+
+atom:write("\t<updated>", os.date "!%Y-%m-%dT%H:%M:%SZ", "</updated>\n");
+
+for n, post in ipairs(posts) do
+	if n > 10 then break; end
+	if post.published then
+		atom:write("<entry>\n");
+		atom:write("\t<author>\n\t\t<name>", post.author or default_author or "Unknown Author", "</name>\n\t</author>\n");
+		atom:write("\t<id>", post.url, "</id>\n");
+		atom:write("\t<title>", post.title, "</title>\n");
+		atom:write("\t<published>", post.published, "</published>\n");
+		atom:write("\t<updated>", post.updated, "</updated>\n");
+		atom:write("\t<link href='", post.url, "' />\n");
+		atom:write("\t<content type='html'>\n", post.content:gsub("&", "&amp;"):gsub("<", "&lt;"), "\t</content>\n");
+		atom:write("</entry>\n");
+	end
+end
+atom:write("</feed>");
+atom:close();
+os.rename("feed/atom.xml.new", "feed/atom.xml");
+
+msg_info("ATOM feed written to feed/atom.xml");

mercurial