Subscribe now

Blogging with Middleman [07.19.2017]

So you have a site with Middleman, but now you need a blog. Worry not! In five minutes or less we'll add a blog, make a couple posts, and add a proper feed for that blog. Let's get started!

Middleman Blog Setup

First up we need to add the middleman-blog gem. It's officially supported, and well documented. We'll add it to our Gemfile and then run bundle.

# Gemfile
gem "middleman-blog", "~> 4.0.2"
bundle

With that done we can re-run the Middleman generator from inside our project with the blog flag, and generate our basic blog pages. It will ask us to overwrite a few things, but if we just answer no, our current files will be left alone. After that we'll have some new files to work with.

middleman init . --template=blog
  • source/layout.erb
  • source/calendar.html.erb
  • source/feed.xml.builder
  • source/tag.html.erb
  • source/2012-01-01-example-article.html.markdown

This gave us a blog layout, calendar, feed, and tag pages, as well as an example article. These are great starting points, but the generated location isn't great since we have another existing page as our home page. Lets move these into a /blog name space.

mkdir source/blog
mv source/layout.erb source/layouts/blog.erb
mv source/calendar.html.erb source/blog/
mv source/feed.xml.builder source/blog/
mv source/tag.html.erb source/blog/
mv source/2012-01-01-example-article.html.markdown source/blog/

After generating the new files, we'll activate the blog in our config.rb while setting some options. In these options, we are just telling Middleman where to find all our new files, and what templates to use for what page types. We'll also turn on directory indexes so we can serve all our files at /foo instead of /foo.html. In our config, note that activation of directory_indexes needs to be done after the blog activation.

# config.rb

activate :blog do |blog|
  blog.layout = 'blog'
  blog.prefix = 'blog'
  blog.tag_template = 'blog/tag.html'
  blog.calendar_template = 'blog/calendar.html'
  blog.year_template = 'blog/calendar.html'
  blog.month_template = 'blog/calendar.html'
  blog.day_template = 'blog/calendar.html'
end

# Serve from directories instead of html files.
# Must be done after blog activation
activate :directory_indexes

Since we chose not to overwrite our index page, we'll need to grab it from the Middleman example and place it in the blog directory also.

<!-- /source/blog/index.html.erb -->

---
pageable: true
per_page: 10
---
<% if paginate && num_pages > 1 %>
  <p>Page <%= page_number %> of <%= num_pages %></p>

  <% if prev_page %>
    <p><%= link_to 'Previous page', prev_page %></p>
  <% end %>
<% end %>

<% page_articles.each_with_index do |article, i| %>
  <h2><%= link_to article.title, article %> <span><%= article.date.strftime('%b %e') %></span></h2>
  <!-- use article.summary(250) if you have Nokogiri available to show just
       the first 250 characters -->
  <%= article.body %>
<% end %>

<% if paginate %>
  <% if next_page %>
    <p><%= link_to 'Next page', next_page %></p>
  <% end %>
<% end %>

Now let's fire up our local server with middleman so we can explore the new pages. First up lets compare /blog/index.html.erb to http://localhost:4567/blog/. Our page is working great, but it's using the wrong layout. When we set blog.layout above, it was was for the article pages and we'll need to override our default layout for that one page. Let's do that in the config now.

# config.rb

page '/blog/index.html', layout: 'blog'

With a quick manual refresh, we've got our correct layout. Live reload is nifty, but it doesn't hold up across config changes. You'll need to manually refresh after any of those.

Let's click into the example article next. We see the article rendered, as well as listings of recent articles, tags, and articles by year. We can even slice up the URL and try going to a month or a day. In all of these cases we have perfectly functional pages that just need a little styling to be a great blog. Best of all is the feed at http://localhost:4567/blog/feed.xml.

These lists, calendar pages, tag pages, and the feed are all updated and generated into static files every time you build. Let's try a clean build:

> rm -rf build/
> middleman build
== Blog Sources: /blog/{year}-{month}-{day}-{title}.html (:prefix + :sources)
      create  build/stylesheets/typebase.css
      create  build/stylesheets/site.css
      create  build/stylesheets/style.css
      create  build/stylesheets/normalize.css
      create  build/javascripts/site.js
      create  build/images/.keep
      create  build/CNAME
      create  build/blog/tags/example/index.html
      create  build/blog/2012/index.html
      create  build/index.html
      create  build/blog/2012/01/index.html
      create  build/blog/2012/01/01/index.html
      create  build/blog/2012/01/01/example-article/index.html
      create  build/blog/feed.xml
      create  build/blog/index.html
Project built successfully.

That's a lot of moving parts that you never have to worry about. Middleman does all the heavy lifting and we get static files that we can drop anywhere.

Creating a new article

Creating a new article is simple, and my favorite part is you can write them in Markdown. We call the middleman article generator, and pass a string to it for our title.

middleman article "Blogging with Middleman"

That gave us a new file at source/blog/2017-07-20-blogging-with-middleman.html.markdown. The date in the blog system is pulled from the front of the blog filename, and our title is made to be URL friendly. Next I'll drop some markdown in that file.

Our markdown goes below the frontmatter which is that block of page-specific variables at the top. Remember those work just like data files, only you use dashes for YAML and semicolons for JSON.

For our markdown, I'll just drop some Lorem in it and style it a little. I'll also add a new tag, just for kicks.

## Lorem ipsum dolor sit amet

![its a bear, scary!](https://placebear.com/g/200/300 "OMG a bear!")

Consectetur adipisicing elit, *sed do eiusmod tempor incididunt* ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. [Duis aute irure dolor](#foo) in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint `occaecat cupidatat` non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

_Lorem ipsum dolor sit amet_, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
\`\`\`
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
\`\`\`

Visiting our new blog page we can see it all fine, and its even got a sweet bear on it. Let's do one final build and see what all gets updated.

> middleman build
== Blog Sources: /blog/{year}-{month}-{day}-{title}.html (:prefix + :sources)
   identical  build/stylesheets/typebase.css
   identical  build/stylesheets/style.css
   identical  build/stylesheets/site.css
   identical  build/stylesheets/normalize.css
   identical  build/images/.keep
   identical  build/javascripts/site.js
   identical  build/CNAME
   identical  build/index.html
      create  build/blog/tags/example2/index.html
   identical  build/blog/tags/example/index.html
   identical  build/blog/2012/index.html
      create  build/blog/2017/07/index.html
      create  build/blog/2017/index.html
   identical  build/blog/2012/01/index.html
   identical  build/blog/2012/01/01/index.html
      create  build/blog/2017/07/20/index.html
      create  build/blog/2017/07/20/blogging-with-middleman/index.html
     updated  build/blog/feed.xml
     updated  build/blog/index.html
     updated  build/blog/2012/01/01/example-article/index.html
Project built successfully.

Most everything is the same, but we have new pages for our post, the new tag, this day, month, and year. Middleman also updated our blog index, XML feed and even linked the new post from our old post for us. This is all possible because these pages become objects in Ruby, like page_articles that Middleman can introspect and iterate across. All of those objects are exposed in the view so you can customize how they are displayed for your site.

Summary

And that's all! I said it was quick, right? Today we setup a blog in Middleman, name spaced it, turned on pretty URLs, overrode a layout for a single page, created a new post. Finally we looked into how Middleman generated the blog pages, and how that changes when new posts are added. See you next time!

Resources