Blog

Writing a Static Site Generator in Nim (PART 2: Feeds)

Part 1

Now that content is written pretty nicely, the whole blog is represented by a json object internally, thus it's very easy to pass the templating engine a json list and it construct an index page. There needs to be FEEDS.

My first thought was that, since the blog's structure is represented as a json tree why not just write that to a file directly simply:

writeFile(fg.jsonPath, $ blog)

The $ operator I learnt was the 'toString' of nim, with it being pretty general to a good few types. I decided that instead of opening a config file for each module I would instead make an object that holds all of the information about what is to be generated like this:

type feedGenerator* = ref object of RootObj
  rssEnabled: bool
  rssPath: string
  atomEnabled: bool
  atomPath: string
  jsonEnabled: bool
  jsonPath: string

Then create a newFeedGenerator proc that would return this object, this allows me to set things up a little nicer in terms of default values and handling undefined config variables etc. I changed this similarly in the html generation module, and did some more error handling there.

I defined all the feed generation as async since they just write to a file and don't return anything, this seemed to speed up the program on the admittedly small test site I'd started.

RSS

I love RSS, it's only of my favourite things about the internet, I use Youtube through RSS (with some macros for newsboat) but had never really learnt about the structure of the data; w3c's specification for RSS is actually really well written and simple to understand with a validator tool being provided. Since the blog was represented as a json object I just had to create the xml nodes correctly.

RSS is composed of an <rss> tag with a <channel> and <item>s in the channel. The channel has information like 'what blog is this' (<title>), <link> etc. Each item is then an entry in the blog similarly with <title>, <link>, <pubDate> and so on. The date must be in RFC822 format which is a funny document to read since only 10 time zones are named, 8 of them being North American; UTC is also called 'UT' here ('GMT' is equivalent).

Nim xmltree macros for constructing xml nodes is nice with an example being:

<>title(newText(item["metadata"]["title"].getStr))

However this falls down when trying to create a namespace'd tag like the suggested <atom:link> as : is a special character. Usually you can use back ticks `` to escape something from the keywords (e.g. `template`) but here the : breaks it, making the node creation more like:

let atomLink = newElement("atom:link")
atomLink.attrs = {"href": blog["blog_url"].getStr & fg.atomPath,
                  "rel": "self", 
                  "type":"application/rss+xml"}.toXmlAttributes

When writing rss to a file, I found some frustration as the $ operator (notice how I introduced that earlier ;) ) printed xml 'pretty' where it hadn't with the json. Looking at what $ was doing I managed to figure out how to print with no whitespace or indentation.

var xmlString = "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
xmlString.add(feed, addNewLines=false, indWidth=0)
writeFile(fg.rssPath, xmlString)

Template Blog Entries

A small problem I have now is writing blog entries in pelican I always forget how the header line is supposed to look like and format etc. I decided to instead add a subcommand that makes a template to the correct specification, this is as simple as building and then pretty-printing a json object and writing to a file. I think this tool is worth it because it makes writing blog posts much nicer when you don't have to keep a template file around to copy and start writing just simply mmb template -t "title". It could also be useful in scripting, for example making automated blog posts with contents from a text file, as all you would have to do is something like.

mmb template -t "title"
cat "mytext.txt" >> content/title.md
mmb publish

Up next

The biggest thing to get this usable at the moment is being able to in-line static content in the content, I think I might be able to achieve this with the templating engine having escaped the html I pass it but perhaps there will need to be a pre-processing stage (slowing down the building :( ) to find something along the lines of {static} and replacing it with the directory.

I'd also be interested in some sort of implicit category system that uses the directory structure of the content directory to group posts into categories. Or simply defining the category in the header and creating category directories with their own indexes.

The code is now hosted here.

Back to Frontpage