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
haml
for writing html, since I don’t like closing tagsBootstrap
andsass
for writing cssMarkdown
for formatting large blogs of contentcoffeescript
for 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 cd
ing 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