Restarting Blog with Jekyll

21 Feb 2014

Jekyll

Since few days my blog is powered by Jeykyll. Comments are handled by Disqus. These have replaced WordPress which has served me well for several years. Nowadays Jekyll feels like natural replacement for such Markdown & Git junkie like me. Site is Retina friendly with images “retinized” on fly by retina.js.

Most of the migration work was copy & paste, however there were few caveats when migrating from WordPress. First of all categories are handled differently in Jekyll - there is no category hierarchy. There is no built-in category list, neither category pages. Luckily Jekyll supports plugins, notably Generate Categories plugin by Recursive Design.

Moreover Ruby monkey-patching gave me opportunity to tune Jekyll to my needs. Below 3 of my custom plugins.

redcarpet_class.rb

I perfectly understand that Markdown is simple by design, but I needed some way to give my CSS some hint about content elements such as images or special paragraph, while keeping texts uncluttered by HTML snippets.

module Jekyll
  module Converters
    class Markdown
      class RedcarpetParser
        CLASS_RE = /<(p|h[1-9]|ol|ul|li)>\.([a-zA-Z0-9\-_\.]+)\s*/i
        DIV_RE = /<h1([^>]*)><\/h1>/i
        alias_method :original_convert, :convert
        def convert(content)
          original_convert(content).gsub(CLASS_RE) {
            "<#{$1} class=\"#{$2.gsub(/\./, ' ')}\">"
          }.gsub(DIV_RE) {
            $1.length > 0 ? "<div#{$1}>" : '</div>'
          }
        end
      end
    end
  end
end

Now if I want assign the image right class (actually it does assign to <p> which is implicitly there) so it floats right, I just write:

.right
![Some Icon](/images/some-icon.png)

Similarly if I want to assign some list element custom class, i.e. so it appears with PDF icon I do:

* .pdf Download [My Article.pdf](/images/some-icon.png)

Obviously none starts a sentence with a dot, so it is safe.

The second regex DIV_RE brings another feature, ability to wrap parts of Markdown with div with given class, i.e.:

.left_column
============

Some stuff embedded with div.

===

Again as above it uses results of previous replacement to turn all empty h1 with class to divs and empty h1 without class to div closing.

category_placeholder.rb

Next I like to put several categories to some of my posts, but I don’t like their address to contain all of them, just the first one.

module Jekyll
  class Post
    alias_method :original_url_placeholders, :url_placeholders
    def url_placeholders
      {
        :category => URI.escape(((categories || []).first || "").to_s)
      }.merge(self.original_url_placeholders)
    end
  end
end

Now it can be done with permalink: '/:category/:title' in _config.yml. Note :category replaces here :categories built-in placeholder.

preserve_mtime.rb

During build Jekyll copies static assets to _site, but it does not preserve their timestamps, so browser will likely reload these files again every time you make an update. This isn’t really bandwith-wise.

Plugin below touches copied files back to their’s originals timestamp.

Also it does one extra thing, it adds 12h to post timestamp. Why? I am just too lazy to put exact timestamp in front matter, but I don’t like all my posts to appear with midnight timestamp. I do not work at midnight! At least I pretend.

module Jekyll
  module Convertible
    alias_method :original_write, :write
    def write(dest)
      original_write(dest)
      path = destination(dest)
      date = self.respond_to?(:date) ? self.date : nil
      FileUtils.touch path, mtime: date if date
    end
  end
  class StaticFile
    def write(dest)
      dest_path = destination(dest)
      dest_mtime = File.stat(dest_path).mtime if File.exist?(dest_path)
      mtime = self.mtime

      return false if File.exist?(dest_path) and !modified? or mtime.to_i == dest_mtime.to_i
      @@mtimes[path] = mtime

      FileUtils.mkdir_p(File.dirname(dest_path))
      FileUtils.cp(path, dest_path)
      FileUtils.touch dest_path, mtime: mtime

      true
    end
  end
  # extra: add 12h to post timestamp, since I don't like putting exact times
  class Post
    alias_method :original_process, :process
    def process(name)
      original_process(name)
      self.date = self.date + (12 * 60 * 60)
    end
  end
end

Disqus

It is worth to mention that Disqus migration was silk smooth. I just had to fix few links from my WordPress .xml export file, so they were pointing to existing URLs. That’s all.

Next time I will try to post on my blog about something else than how I did my blog ;P

Posted in CMS, Ruby, Tweaks