How to get smart quotes in wok

Smart quotes (i.e., “curly quotes”) are a must-have for me. If I were designing a CMS, smart quotes would be included by default. Alas, wok does not include them. But that’s okay because the developer of wok intended for it to be customizable and provided us with hooks, which we can latch on to and add extra features.

To get smart quotes, we’re going to use the Typogrify package. We can install it with pip:

$ sudo pip install typogrify

Typogrify comes with SmartyPants, a utility that searches over text for straight quotes and turns them into curly quotes (among other things). Our content in wok is already being parsed by Markdown, so after that happens, we want to parse it with SmartyPants.

Wok hooks

Hooks live in their own folder within the wok file structure. But hooks aren’t required to run a wok website, so there’s a chance you don’t even have a hooks folder. If you don’t, go ahead and create a folder called hooks. In a default wok setup, it would go in the top level of your site structure, next to your content folder.

Next, create a file named __hooks__.py and put it inside the hooks folder. In this file, we will write some Python code that will latch on to the appropriate hook and tell wok to run SmartyPants.

Which hook? Wok’s documentation has a list of the available hooks. Remember that we want to run SmartyPants over our content after it has been rendered by Markdown. To do that, we’ll use the page.render.post hook.

Wok’s documentation describes page.render.post:

This hook will be called for each page right after the page is rendered by Markdown, reStructuredText, etc. The unrendered text will be in the variable page.original, and the rendered text will be in the meta variable page.meta['content']. Keep in mind that some pages won’t be run through this hook because they come from other sources, such as hooks, or pagination.

Writing our hook

I’ll go ahead and show you the code, then we’ll walk through it.

import typogrify.filters as typogrify

def smartypants_filters(config, page):
    if "title" in page.meta:
        page.meta["title"] = typogrify.smartypants(page.meta["title"])

    if page.meta['content']:
        page.meta['content'] = typogrify.smartypants(page.meta['content'])

hooks = {
    "page.render.post": [smartypants_filters],
}

First we import typogrify so that we have access to one of its filters, typogrify.filters.smartypants. I’m using the as keyword to make using typogrify a little more concise.

Next, we define a function. We can call it whatever we like, but it needs to accept the config and page variables that will be supplied to it by wok when the function is called.

I’m going to go out of order and talk about content first. We check to see if there is any content, and if there is we run smartypants over it and replace the existing content with the result.

We’re essentially doing the same thing with the title field. We first have to check to see if there is a title field defined, and if so we do the exact same maneuver we did with the content.

If your wok site has additional fields that need smart quote substitution, then you can treat them the same way we did our title. Check to see if a particular field exists, then replace it with the smartypants-enhanced version.

Up to this point, wok doesn’t know about our function. We need to specify that we want to attach our function to the page.render.post hook. Wok automatically looks for a variable called hooks within this file that contains a Python dictionary. Each key:value pair in that dictionary needs the name of the hook and a list of functions to fire. We just have the one function, so it goes in a list by itself.

If only there was…

I love wok for its simplictity and that you can use it with very little programming knowledge compared to other static site generators (which makes it a good candidate for designers). That said, it does take a little bit of pragramming to get smart quotes with wok. It’s certainly not a dealbreaker, though. And with this tutorial, you should be able to get there fairly easily.

We all have that one feature we feel is a must-have. I hope this tutorial gets your wheels turning on how you can use hooks in wok to get that feature you want.