Tutorial 3: Blogging

This tutorial walks through how we can set up a blog on our website by following the Blog example found in the Pencil examples/ repo.

Let’s consider all the features we’d want for our blog:

That’s a pretty thorough feature list. But because Pencil aims to make life as easy for you as possible, it’s loaded with a bunch of these things. They’re not “raw” functionality built into Pencil, but more like user-land features that were so useful that it made it into the core library. And it’s all included when you import Pencil.

Pencil also ascribes to the philosophy of convention over configuration. And though you could configure these various tools to your liking or even write your own version (none of the functions are too complicated or very long), will follow Pencil’s convention for blogging.

So let’s get started!

Rendering blog posts

Unlike the first tutorial, we’re not going to walk through each file you need to create and so forth. Since we’re following the Blog example, I trust that you’ll be able to follow along in your own code base.

First, peruse the site/ folder (here) and get comfortable with a couple of things in there.

If you open 2018-01-30-code-related-stuff.markdown, you’ll see a preamble section at the beginning.

Note that date and postTitle are required. We also see that this one was tagged with “awesome”. We’ll get to tags further down.

Now, for our first stab:

With these first few lines of code, you should be able to see something working. As usual:

stack run
cd out && python -m SimpleHTTPServer 8000

And you should see our blog posts rendered! For example: http://localhost:8000/blog/code-related-stuff/. Of course it’s not listing any tags yet, and we don’t have a blog index or anything. But it’s a start.

Now, you should be familiar with the load function by now. But here we see loadPosts. This is a helper function provided by Pencil to do the following:

Don’t be shy about reading the source code. You’ll find that I sent more characters explaining it above than the actual source of loadPosts.

To render each post, we had this:

This converts a list of Pages into a list of Structures, pushing each page into the layout structure. We then render the list of Structure.

There’s an instance of the Render typeclass for list of Pages. That’s why you can call render on a list.

If you loaded one of the rendered pages in your browser, you’ll notice that the <title> tag still just says “My Blog”. But shouldn’t it contain the title of the blog post also?

Well, this ain’t the first time someone ran into this problem. Use injectTitle to fix it:

render $
  fmap ((layout <|| postLayout <|) . injectTitle websiteTitle)
    posts

injectTitle takes the postTitle variable in each post’s preamble and puts it in the front of the given website title. Rebuild the website, and the “Code related stuff” post now has the title “Code related stuff - My Blog”.

The dot operator (.) is function composition. (a . b . c) d translates to a (b (c d)).

Listing all blog posts

We’ll of course want to build a page that lists out all of our blog posts. To do that, let’s add a couple of lines to our website function:

index <- load "index.html"
render (layout <|| index <<| coll "posts" posts)

Ooo, some new stuff! Let’s talk about structures. As you know from the previous tutorials (and reference pages, and the Pencil docs) A structure is a nesting of pages. But sometimes we’ll want to nest not just a single page, but a collection of pages. In our case, we need our index page to have access to the list of post pages so that we can, in our template, loop through each post and display its postTitle along with its date and URL.

Well, that’s possible, as long as the collection element is (1) always the last element in the structure and (2) not the first. So you can’t have a structure with just a collection, for example.

This rule is there to keep things simple, and it makes sense too. For most use-cases, the collection is the “most important thing” for the page we’re trying to build.

Let’s talk about the contents of index.html too.

First, ignore the RSS stuff—we’ll get to it later. But look out that ${partial()} template directive. A partial is just a copy-paste of the contents in the partial. Nothing fancy is going on. So we’ll need to check out post-list.html:

Interesting! Here we loop over the posts variable. This comes from that collection structure that we built, when we did coll "posts" posts. For each post, we render the post-bullet.html partial:

What you’ll want to understand here is the fact that when we are inside the for-loop, the environment context has changed. We have access to the post’s variables like postTitle and date. And note that this.url is automatically set by Pencil during render.

So if we build our website now, we’ll see a sweet index page listing all of our blog posts!

Exercise: Create a new blog post and re-build your website. Reference variables in your blog post, like ${postTitle} and Pencil Documentation (which is the website’s main title).

Tags, Tags, Tags

So we are now rendering each blog post, and we have a listing of blog posts. Time to deal with tags.

A bit of warning: there’s some hand-wavy stuff in this section. Not because it’s magical, but because it can be confusing to explain without actually looking at the implementation.

Add these lines to the website function:

buildTagPages takes tag-list.html and a list of post pages. It returns (hand-waving about to happen) a hash map of tags (which is just a Text) to a Page. That Page is a version of the page loaded from tag-list.html, but with the tag variable set to the tag in the key, and a posts array containing all posts that was tagged with the current tag. Oh, and the page’s URL is set to blog/tags/${tag}/.

Yeah, buildTagPages is complicated. If you want to control some of the configuration, use buildTagPagesWith.

So as an example, the value for the key "awesome" is a Page where the posts variable contain Pages containing the posts from 2018-01-30-code-related-stuff.markdown and 2018-02-01-more-ramblings.markdown, since they were both tagged with awesome. The URL for the page is blog/tags/awesome/.

We actually can now page for each tag, listing out all the posts tagged with the tag. This is what render $ fmap (layout <||) (H.elems tagPages) does. It takes each Page, which is a version of tag-list.html, and renders it.

Re-build your website, and check out localhost:8000/blog/tags/awesome/.

OK that’s pretty sweet. But we also want to list out the tags on each of the blog post page that we rendered. And not only that, but we want to link each tag to the tag listing page. This is common-enough and confusing-enough to warrant a built-in method. We can use injectTags during the blog post render, like this:

render $
  fmap ((layout <|| postLayout <|)
        . injectTags tagPages
        . injectTitle websiteTitle)
    posts

Basically we’re giving each post the entire mapping of tags to posts. Open post-layout.html and notice this:

injectTags filters out the tagPages you passed in only to the tags found in the current page. Thus we can loop through tags and link to the tag listing page, just like that.

There ya go. Tags. It’s complicated underneath the hood, but at least the method are easy to use, right? As always, if you’re interested in learning more, the source code is always available. If you want a more interesting tagging system (sub-tags?), the existing implementation would serve as a good starting point.

RSS

The final thing we’ll tackle is RSS. Let’s look at rss.xml:

This is our template. We loop over ever post and render the post’s URL, title, date and content. this.content is something we’ve never seen before. Any page’s content is always available through the special this.content variable. So to render each page’s content, all we need to do is type ${this.content}.

We use this.content instead of body because body refers to the inner page’s content. But inside the for-loop, the context is the current page being looped over. And our post page does not have an inner page to render, so body is not available.

Modifying website, we can add:

I think we understand load "rss.xml" by now, but what is that monstrosity in the next line? Examining it from right-to-left:

So now we have a structure containing all of the RSS posts we want to render. Can’t we call render rssFeedStruct? Well, almost.

The RSS specification expects the <pubDate> value to be in the RFC 822 date format (e.g. Wed, 31 Jan 2018 00:00:00 +0000). But Pencil’s default renderer will render a VDateTime in YYYY-MM-DD format. The transformation of Values to their final string outputs is defined in the config by configDisplayValue. As you can see in the Hackage documentation, the default value is toText. We need to replace toText with a different one, a Pencil-supplied one called toTextRss. The only difference between the two is how VDateTime values are rendered.

We could update our config value to:

But that would change how dates are rendered everywhere. What we need to do is temporarily set configDisplayValue too toTextRss.

This is where local comes in. local allows us to temporarily run some command in a modified Config environment.

local (setDisplayValue toTextRss) (render rssFeedStruct)

The above means that render rssFeedStructure runs with configDisplayValue set to toTextRss.

Let’s re-compile our website one more time and check out localhost:8000/rss.xml. We have an RSS feed indeed.

Conclusion

In this tutorial you learned: