Building Sites with Middleman
lean publishing
- tags
- middleman
- ruby
Contents
I make a lot of websites, and I have a certain toolkit that I use to build them. The most useful things I use are:
- layouts and partials so I only need to set things up once
hamlfor writing html, since I don’t like closing tagsBootstrapandsassfor writing cssMarkdownfor formatting large blogs of contentcoffeescriptfor JavaScript
Middleman is a static site generator, which means that it takes a bunch of source files, does some stuff with it, and produces static HTML, CSS, Images, and Javascript that can be hosted on a basic server somewhere, including hosting on S3 or Github Pages so you don’t need to consider a server.
Middleman is written in Ruby, so it’s our familiar toolset, and unlike Jekyll it uses Sprockets, which is the same asset pipelining system that Rails uses. So you get all the benefits of using a robust system that works for Rails apps without having to learn a bunch more things.
In the node world Yeoman does something similar, but I personally have had poor luck getting Grunt to work reliably in practice.
Setting up a simple static site with Middleman
One of the problems with starting with Middleman is that there are so many places to start. Lets look at how to setup a basic middleman site with bootstrap-sass, haml, and bower. First thing is to install middleman:
| |
And create an app:
| |
This creates 4 files in the main directory, Gemfile and Gemfile.lock, which we know and love, config.rb which configures how middleman generates the site, and source which are the sourcefiles of the site.
Gemfile
By default, middleman installs the middleman-livereload plugin, so in development mode any browers that have a page open with refresh when you save a file. This makes testing a lot easier. We can install other gems here to add different functionality. Let’s add a few of these now:
| |
The first two gems expand the functionality of middleman, one to add tasks to push the final site build to s3, gh-pages and a whole bunch more, and the second to make it easier to build bootstrap navbars.
The file two are including bootstrap-sass – the same that we use for rails sites – and jquery. These get included into the sprockets asset path, so you don’t need to maintain them in your project. (We’ll also see below how to integrate bower components into your project.)
Tweaking Middleman
config.rb is where we configure how middleman itself works.
| |
Lets go through this in detail.
page "CNAME", layout: false says to move the file called CNAME over without wrapping in the main layout. Since this file doesn’t have .html in the name, it would otherwise get ignored. This file is for having a custom domain on Github Pages, if that’s the sort of thing you are in to.
The set commands are there to configure different middleman settings, here to show where the various stylesheet, image, and css directories are.
activate :directory_indexes with enable pretty urls. Directory indexes means that files named about.html will actually get generated into a file called /about/index.html, and will rely on the underlying server to have /about actually show the directory “index” page, making the urls prettier. This doesn’t work on all servers but works on most of them. This plugin will actually rewrite the output of the link_to tags, so you don’t need to adjust your templates to work.
activate :bootstrap_navbar is an extension that we added in the Gemfile, which makes it easier to generate bootstrap navbars. Extensions generally work in three ways: they add helper methods, they change the way that the sitemap is processed, or they add different commands to the “middleman” command.
We see an example of that at the bottom of the config.rb file, where we configure the deploy extension:
| |
This also takes some configuration, and in this case it set to deploy to github pages, and to make sure that it generates the site before doing so. Here is the middleman-deploy github page with documentation.
There are two sections that each configure a different middleman environment. The first is used when you run middleman server to look at the site locally:
| |
The final section is configuring the build process, when you run middleman build and it creates the generated files in the build directory. The entry below ignores certain files for the build, and runs minifiers over the css and javascript, and turns on cache busting.
| |
Using bower
The basic way to use bower is to put this in config.rb:
| |
And then you can put things in your all.js file like
| |
To get all of the assets that are included in the packge, you may need to specify the package in the config.rb file, such as:
| |
More information on the Middleman Asset Pipline documentation
Using layouts and partials
The source directory is where the actual code for your site lives. Here is an example layouts/layout.haml file to give you a sense of how to use layouts and include partials:
!!! 5
%html.no-js.sticky
%head
%meta{ :charset => 'utf-8' }/
%meta{ 'http-equiv' => 'X-UA-Compatible', :content => 'IE=edge,chrome=1' }/
%title This site is amazing!
%meta{ :name => 'description', :content => '' }/
%meta{ :name => 'viewport', :content => 'width=device-width' }/
= stylesheet_link_tag 'application', "socicons", "animate"
%body
= partial "layouts/main_header"
~ yield
= partial "layouts/footer"
= partial "layouts/javascripts"
In this usage, this is similar to the way that rails layouts work. I’m using the ~ HAML operator inside of = here because I want to make sure that it doesn’t do anything wonky with the indention on <pre> blocks, btw.
Middleman has a concept of nested layouts, which lets you have wrap an a layout around another one. I think that this is confusing in practice, but as an example you could have a layouts/sidebar_layout.erb that looked like:
<% wrap_layout :layout do %>
<div class="sidebar">
<%= partial "layouts/sidebar" %>
</div>
<div class="content">
<%= yield %>
</div>
<% end %>
The YAML preable the preyaml
Metadata about the template is included in a block of text at the top of the file, which gets pulled off to set things that the templating system can use later. On the top of this file that I am editing right now, it looks like this
---
title: 'Building Sites with Middleman'
subtitle: 'lean publishing'
tags: middleman, ruby, howto
header_image: books.jpg
---
I make a lot of websites, and I have a certain toolkit that I use to build...
The title, subtitle, tags and header_image attributues are available in the templates as page data, so you can access them like:
%h1= current_article.title
%h2= current_article.data['subtitle']
title is built into middleman, subtitle and header image are just some random things I made up. The tags attribute is part of the middleman-blog extension which we will cover below.
How the build works
When you run middleman server or middleman build, middleman loads up the configuration file in config.rb. It creates a sitemap based upon the files in the source directory as well as other directives inside of the config.rb file.
By default it only includes files like .html.erb and .js, but you can set it manually include a non-template file (like our page "CNAME" above or create other proxy files. Proxy files are a way of seperating out the templates from the source data.
| |
This creates three entries into the sitemap called /about/tom.html, /about/dick.html and /about/harry.html that use a specific template.
This data doesn’t need to be hardcoded into the config.rb btw, you can also place json and yml files in the data/ directory which middleman will load automatically. For example, data/employees.json
| |
And then in your config.rb you could access this as:
| |
Then each entry in the sitemap the file is processed (based upon the extension, so scss -> js, haml -> html, etc.) into the build directory. Helper methods are available inside of the templates for things like javascript_include_tag, stylesheet_link_tag, link_to and image_tag and all of the rest.
middleman server
Starting the server in preview mode will start a local server on port 4567 that generates the files on demand. If you have livereload enabled this will automatically trigger a page refresh for any open browsers, so you can tweak and look at things as you go.
Inside of your templates, config.environment == :development when you are in preview mode. So, if there are some things that you don’t want to push to the live site but are useful for development, you can switch them on and off using that mechanism.
middleman build
This does basically the same thing as the server, but the templates are generally further processed. Cachebusting can be enabled, and you can include tracking code if config.environment == :build is true. This goes through all of the files in source that look like webfiles and places them in the build directory.
And easy way to check out what you have there is by cding into the build/ directory and running a simple webserver to serve the pages. Relative links don’t work when you open the file directly in the browser, so you need to use an actual webserver.
| |
And then open a new browser on port 8000.
middleman deploy
In the basic :git setting that we have above, middleman deploy will build the site into the build/ directory, switch that directory to the gh-pages branch, and push it to origin. Assuming that you are hosting your repo on github, this will publish the static content on github pages.
If you want to use a custom domain then you need to create a CNAME file in source/ with the domain name, and set up your DNS records to match.
One thing to note is that while changes to the pages seem to deploy quickly, it takes a long time for the first push to github pages to show up, on the order of 10-15 minutes.
More information on middleman-deploy.
Building a blog
There are two good extensions for building a blog with middleman. The default template for blog is sort of confusing in the way that it’s laid out, mainly because it gets rid of the layouts/ directory, but let’s go through it and see how it’s supposed to work:
| |
Now we have another middleman site, with a bunch of files. It also creates a new command:
| |
Let’s also include the middleman-blog-drafts gem into the Gemfile, activate :drafts in `config.rb, and that will give us a few more commands:
| |
This lets us keep drafts in git and doesn’t force us to commit to a date until we are ready to publish it. There are published and date attributes that the default blog extension knows about to turn it on and off, and is a good example of something that you can see in development but not production, but that still leaves to moving files around manually to adjust the date.
What does it add
In addition to the sitemap, we now have a blog, article, and tag concept inside of the middleman app. Articles are represented as pages (by default using markdown) but the tag and calendar templates are actually more like proxy templates than file templates, and when the site is generated middleman will iterate over then to produce many output files from one template.
Part of the index.html.haml of this site looks like:
- (drafts + page_articles).each do |article|
.post
.post-date
- unless article.is_a? ::Middleman::Blog::Drafts::DraftArticle
%p= article.date.strftime( '%b %e' )
- else
%p.draft Draft
I’m putting all of the articles in a list, both drafts and published ones, and only showing the date for articles which have already been published.
Here’s what gets added to the config.rb:
| |
This should be enough to get you started, and more documentation is here . At this point it really becomes a design and development challenge, not figuring out how to use the tool.
Go get started
Middleman gives you all of the front-end developer benefits of using a system like Rails, but outputs static content than can be served anywhere without any dependancies. Many sites don’t really require all that for them to run, and it’s crazy to me that something as read heavy as a blog often can’t perform well under load given that it’s just serving up the same old stuff over and over again. You want to have some tooling to make it easier, but it doesn’t need to be run time tooling.
Image Credit: Moyan Brenn
Previously
Next