The Stochastic Game

Ramblings of General Geekery

New features in PieCrust 2

Now that you know about PieCrust 2 and you’ve upgraded your website,
it’s time to look at the really new features. Today we’ll talk about the 2 ones
that I think are most important: the new content model, and the new pagination
model.

And yet another apple pie

(this post is going to be a bit long so here’s something to keep you hungry)

Sources, routes, and taxonomies

In PieCrust 1, like in most other static website generators, the way content was
defined was quite rigid: you could have pages, and you could have blog posts.
PieCrust did a few extra things, like letting you have multiple blogs, each with
its collection of posts, but that was it.

The only way to have a specific set of pages, different from other pages, was to
use page metadata and filtering (say, filter pages where type is recipe to
get all recipes), but that didn’t translate to a good file-system organization,
required remembering to tag things correctly, and required to use the inverse
filter to get the other pages. You also couldn’t have a different URL format for
all recipe pages, as compared to normal pages.

Enter PieCrust 2, where all the content is, under the hood, defined with
sources, routes, and taxonomies. Those are generated for you to
something equivalent to PieCrust 1 content if you don’t define them, but you can
override that for totally custom content.

Sources

Sources are where pages come from. Two source types you already know (if you
used PieCrust 1 before) are:

  • the “simple” page source, where pages are found recursively under a given
    directory, and their relative path translates to their relative URL.
  • one of the “blog” page sources, where pages are found in a closely structured
    directory, and both the date and “slug” of the post are defined by the
    filename. So in the case of, say, the flat post source, all posts are
    files named YYYY-MM-DD_foo-bar.md directly under the posts/ directory.

Because a site can have as many sources as you want, it already means you could
create a “recipes” source, and put all the recipes in a different directory than
the other pages, so that’s already nice.

But in the future there will also be more advanced sources. See, the “simple”
page source just gives one piece of information about a page: its relative URL.
But the blog post sources give more information, like the date of the article
(“simple” pages need to specify it as part of their config header).

You could therefore imagine, say, a page source where each folder applies a tag
to pages inside it. So if you created a page like
recipes/pies/fruits/apple-pie.md, it would automatically have tags pies and
fruits applied to it, as if you wrote tags: [pies, fruits] in its
configuration header. Another useful source would be one that applies a
hierarchical order to its pages, based on a filename prefix – this would be
well suited to things like documentations.

Routes

Now that PieCrust knows where to find your content, routes define how it’s
exposed – or parsed, if you were to run PieCrust as a lightweight CMS, or when
running chef serve.

A route defines the shape of the URL of a page. If you’ve used PieCrust 1, you
can think of it as a generalization of the post_url/tag_url/category_url
settings.

At the moment, it can only use the same information as the one provided by the
source (e.g. the year, month, day, and slug of a post for a blog post source),
but in the future you’ll get to use all the other page metadata too (so that you
can generate URLs that include categories or tags if you want).

Taxonomies

Another generalization from PieCrust 1 are the taxonomies. Before, only
categories and tags would have automatically generated listing pages. Now you
can have whatever you want. You just need to specify if a taxonomy can have
several terms applied to a page (like tags) or not, the name of the term listing
page (like _tag.html and _category.html), and a few other optional things.

Putting it all together

Let’s say we want to have a section in our website where visitors can browse our
favorites recipes. We want to put all recipe pages in a recipes/ directory
(next to pages/ and posts/), be able to tag them by ingredients, with
listings of recipes by ingredient being created automatically, and be able to
tweak the URLs for all of this.

We’ll specify appropriate sources, routes, and taxonomies in the site
configuration. Let’s start with just getting the recipes going:

site:
    sources:
        recipes: {}
    routes:
        - url: /recipe/%path%
          source: recipes

This will make PieCrust look for pages in the recipes/ directory, using the
default page source (since we didn’t specify anything), i.e. the same as the
one used for pages/. URLs that look like /recipe/foo/bar will match our new
route, and a file named foo/bar.md will be loaded from the recipes/
directory in that case.

Now’s the time to add the “ingredients” taxonomy. This gets more complicated
because we have several things to specify:

site:
    sources:
        recipes: {}
    taxonomies:
        ingredients:
            multiple: true
    routes:
        - url: /recipe/%path%
          source: recipes
        - url: /recipes/with/%ingredients%
          source: recipes
          taxonomy: ingredients

This does:

  • Add a new taxonomy named ingredients. It’s a multiple taxonomy, meaning
    that pages can have more than one ingredient assigned to them (this tells
    PieCrust it potentially has to generate listing pages for combinations of
    ingredients).
  • Add a new route for listing recipes by a given ingredient. Here, the
    %ingredients% token, along with the taxonomy: ingredients setting, let
    PieCrust know how to properly find and match content for this route.
  • When a listing page needs to be generated, PieCrust will look for a
    recipes/_ingredients.md page, passing whatever value was matched by
    %ingredients% to an ingredients template variable. This is analogous to
    how tag and category listing pages work in PieCrust 1.

Some other interesting facts:

  • You can list all recipes by starting with {% for recipe in recipes %}.... Sources have a page iterator exposed by default to a
    template variable of the same name as themselves.
  • You can create a new recipe page easily with chef prepare recipes foo-bar.

There are many advanced settings to change the behaviour of PieCrust, but
they’re outside the scope of this already quite long blog post.

Pagination

Another big change in PieCrust 2 is how pagination is handled. In PieCrust 1,
you could only paginate blog posts, but in PieCrust 2 you can paginate any list
of items – pages or otherwise.

The new paginate filter lets you do that, by returning a Paginator instance,
exactly like the existing pagination object. But where the pagination object
returns something that lists the posts in the default blog source, the
paginate filter will return something that lists whatever it was passed.

This is especially useful for galleries, as shown with my Meeting Notes
doodles. This was done more or less like so:

{% set thumbs = assets|paginate(9) %}

{% for thumb in thumbs.items %}
<img src="{{thumb}}" alt="Note" />
{% endfor %}

[Older entries]({{ thumbs.prev_page }})
[Newer entries]({{ thumbs.next_page }})

Of course in reality there’s more fluff (CSS classes, etc.) and tests around
using prev_page and next_page, but you get the idea. Just like in PieCrust
1, assets returns the list of page assets URLs (in this case, a whole bunch of
pictures), and the paginate filter makes sure only 9 of them will be shown on
a given page. It also tells PieCrust to generate sub-pages.

Obviously, you can’t use 2 pagination sources on the same page – PieCrust
wouldn’t know how to generate sub-pages that go in 2 different directions, so
you’ll get an error if you try that.

Call for feedback

Please get in touch with me, or post comments here, if you have some
constructive feedback about this new content model. PieCrust 2 is still in
alpha, so there’s time to change the design without messing up every other
PieCrust user.