Sam Heuck

Meet Jekyll the Static Site Generator - Part 2

Jekyll

Part II

In part 1 I discussed why I chose Jekyll to build this site and presented a high level overview of how Jekyll works. In this post, I'll dive into the depths of Jekyll and show off some of its features.

Liquid Templates

The template engine used by Jekyll is called Liquid. Liquid makes it easy to create a layout and pull in page elements dynamically.

Jekyll will compile content into a page using a layout like this:

// _layouts/default.html

<!DOCTYPE html>
<html lang="en">
  <head>
    {% include head.inc %}
  </head>

  <body>
    <header>
      {% include header.inc %}
    </header>

    <div id="main">
      {{ content }}
    </div>

    <aside>
      {% include sidebar.inc %}
    </aside>
  </body>
  {% include js.inc %}
</html>

Including partials

Jekyll will automatically find includes in the _includes directory.

{% include links.inc %}

Loops and conditionals

Any page that begins with a YAML Front matter block...

---
// predefined default variables
layout: default
category: funny
published: true
tags: cats

// custom variables
context: cats
title: Funny Cats
---

can declare variables which can be accessed in Liquid. This allows the conditional placement of content. For instance, you can place a sidebar on a page based on a context variable...

--snip--

<aside>
  {% case page.context %}
    {% when 'about' %}
      {% include sidebars/about-sidebar.inc %}
    {% when 'contact' %}
      {% include sidebars/contact-sidebar.inc %}
    {% when 'home' %}
      {% include sidebars/home-sidebar.inc %}
    {% when 'blog' %}
      {% include sidebars/blog-sidebar.inc %}
    {% else %}
  {% endcase %}
</aside>

It's also simple to create a recent posts block with a loop...

<h3>Recent Posts</h3>
<ul>
  {% for post in site.posts limit: 6 %}
  <li><a href="{{ post.url }}">{{ post.title }}</a></li>
  {% endfor %}
</ul>

Site-wide variables can be declared in _config.yml. This is a convenient place to put primary navigation…

// _config.yml
---
nav:
  - text: about
    url: /about/index.html
  - text: resume
    url: /resume/index.html
  - text: contact
    url: /contact/index.html
  - text: blog
    url: /blog/index.html

Which can be included in the layout

<nav>
  {% for link in site.nav %}
    {% assign active = nil %}
    {% if page.url == link.url or page.layout == link.layout %}
      {% assign active = 'class="active"' %}
    {% endif %}
    <li><a {{ active }} href="/{{ link.text }}/">{{ link.text }}</a>{{ indicator }}</li>
  {% endfor %}
</nav>

Plug-ins

Out of the box, Jekyll is well equipped to create a blog with a custom layout, and it has built in support for markdown. But augment Jekyll with plugins and you can start cooking with gas.

Jekyll Asset pipeline

This is an essential plug-in for Jekyll that will compile sass, and also aggregate and compress javascript and css.

~ gem install Jekyll-asset-pipeline

You have to install a compressor. I like the yui-compressor because it will minify both javascript and css.

~ gem install yui-compressor

Once these Ruby gems are installed, enabling the asset pipeline plug-in is as simple as putting a config file inside the _plugins directory. Here's the one I use for this site…

require 'Jekyll_asset_pipeline'

module JekyllAssetPipeline
  class SassConverter < JekyllAssetPipeline::Converter
    require 'sass'

    def self.filetype
      '.sass'
    end

    def convert
      return Sass::Engine.new(@content,
        :load_paths => ['.','../_assets','_assets/sass','../_assets/sass'],
        :syntax => :sass
      ).render
    end
  end

  class CssCompressor < JekyllAssetPipeline::Compressor
    require 'yui/compressor'

    def self.filetype
      '.css'
    end

    def compress
      return YUI::CssCompressor.new.compress(@content)
    end
  end

  class JavaScriptCompressor < JekyllAssetPipeline::Compressor
    require 'yui/compressor'

    def self.filetype
      '.js'
    end

    def compress
      return YUI::JavaScriptCompressor.new(:munge => true).compress(@content)
    end
  end
end

The asset pipeline plug-in hooks into Liquid and looks for assets to include inside the _assets folder.

Adding javascript looks like this:

{% javascript_asset_tag global %}
  - /_assets/js/jquery.js
  - /_assets/js/highlight.js
  - /_assets/js/init.js
{% endjavascript_asset_tag %}

And css:

{% css_asset_tag global %}
  - /_assets/css/normalize.css
  - /_assets/screen.sass
{% endcss_asset_tag %}

Including any file ending in .sass will cause asset pipeline to compile the sass into css on site build.

I also like to use a plug-in for sitemap generation. Just include it in the _plugins directory and a sitemap will be automatically generated on site build.

Helpers

Thor is an alternative to Rake. I like to use it as a helper for creating posts and deploying my site. Thor has some nifty features.

~ gem install thor

A Thor task for creating posts:

class Site < Thor
  include Thor::Actions

  desc "post", "create a new post"
  def post
    title = ask("Title:")
    @title = title
    @description = ask("Description:")
    @category = ask("Category:")
    @date = Time.now.strftime('%Y-%m-%d %k:%M:%S')

      filename = "#{Time.now.strftime('%Y-%m-%d')}-#{title.gsub(/\s/, '-').downcase}.md"
      template "_thor/templates/post", "_posts/#{filename}"

    if ENV['EDITOR']
        system("$EDITOR _posts/#{filename}")
    else
      system("vim _posts/#{filename}")
    end
  end
end
// Thor template for posts
// lives in _thor/templates
---
layout: post
published: false
date: <%= @date %>

title: <%= @title %>
description: <%= @description %>
category: <%= @category %>
image: <%= @image %>
---

From the command line, run thor site:post and it will prompt for info about the post - title, description, category and an image - and then create a file in _posts and open that file in your editor.

Thor could also be used to run Jekyll, and then rsync the contents of _site to a server for easy deployment.

You can also use Jenkins for deployment. For this site, I have the Jekyll stack and Thor installed on my host server. I then use Jenkins to run the thor deploy task which copies the contents of _site into the public html folder on my host. Updating my site is as simple as writing some content, saving that content into version control, and then running the Jenkins build.

Taking this one step further, you could have Jenkins watch a remote repository for changes, and automatically run a build and deploy whenever it sees a change in the code base. That way, the site would be updated every time you pushed changes to your repository.

So go start using Jekyll! Trust me, it's fun...