Jekyll installation
As I was saying, I moved my blog from WordPress to Jekyll.
It's mostly finished now. All posts and comments have been moved here, and adding comments is kind of possible too.
Jekyll is a blog-aware static site generator, so from template files it generates all the HTML pages of the website. The result is a static website.
There are several interesting points of having a static site including the fact that it's damn fast and it's much more secure. The main drawback being the static part. The obvious limitation on a static blog is that there are no comments. Lots of Jekyll users are including Disqus for managing comments but since I don't like centralized stuff I went to a fully static solution (comments work by sending me emails, so all comments are moderated before being published).
Workflow
Here is the workflow I use to write posts:
- Write post using a Markdown friendly editor (on Mac I use Mou).
- Run Jekyll on my Mac to see the end result.
- Commit the post in my blog's git repository.
- Push to git repository on my server (a script is launched at the end to regenerate the website).
Same for adding comments.
Installation on Debian (server side)
In this example I will assume that the blog is on blog.example.com
.
|
|
- Line 1: Jekyll is written in Ruby and installable as a rubygem.
- Line 2: Jekyll uses Pygments for syntax highlighting.
- Line 3: Install Jekyll.
- Line 4: Install rdiscount which is a faster (and less buggy) markdown parser than the default one (maruku) provided with Jekyll.
- Line 5: Create the directory that will hold the reference git repository.
- Line 6: Go into that directory.
- Line 7: Create a bare git repository in it.
- Line 8: Clone the repository, this clone will be used by Jekyll.
- Line 9: Give me ownership of the git clone.
When I commit into the main repository, I want the checkout to be updated and the website to be generated. We can use git hooks for that, just create file ~/blog.example.com.git/hooks/post-receive
:
#!/bin/sh
unset GIT_DIR
GIT_REPO=/srv/blog.example.com
cd $GIT_REPO
git pull
jekyll
Add it execution rights:
$ chmod +x /home/laurent/blog.desgrange.net.git/hooks/post-receive
Create a new virtual host in Apache with the file /etc/apache2/sites-available/blog
:
<VirtualHost *:80>
ServerName blog.example.com
ExpiresActive on
ExpiresByType image/jpeg "access plus 1 year"
ExpiresByType image/jpg "access plus 1 year"
ExpiresByType image/png "access plus 1 year"
ExpiresByType text/css "access plus 1 week"
ExpiresDefault "access plus 1 week"
DocumentRoot /srv/blog.example.com/_site
</VirtualHost>
In this file I'm using the expires
module to specify cache policy by content types.
Activate all that:
$ sudo a2enmod expires
$ sudo a2ensite blog
$ sudo service apache2 reload
Everything is ready, just need some content in this blog.
Installation on Mac OS (client side)
First, install ruby, There are several ways to do so, I choosed MacRuby wich is fairly easy to install. Then installing Jekyll is pretty much the same.
For Pygments:
$ sudo easy_install Pygments
I won't explain the basics of Jekyll, there are a lot of websites talking about that already, just some things specific to my case.
First of all, I'm using the HTML5 video
tag and Jekyll in server mode was not able to server video files properly because of unknown mime types. I had to add them in /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/lib/ruby/1.8/webrick/httputils.rb
:
DefaultMimeTypes = {
# (…)
"m4v" => "video/x-m4v",
"webm" => "video/webm",
"oga" => "audio/ogg",
}
As I was saying I wanted to keep comments on my blog. I came accross the jekyll-static-comments plugin but it requires to add a PHP script to send an email when the comment form is submitted. And I was to lazy for that. But Matt Palmer, the guy doing this plugin, points to an even-more-static-comments method. It's the same except emails are sent directly by the user. I like that the user can keep track of comments sent (since it's an email that is sent).
I was also interested by learning a little bit of ruby and I wanted comments to be written like posts (a YAML header followed by the content (which can be written using markdown syntax)).
So I rewrote most of the original plugin, still called static_comments.rb
:
class Jekyll::Site
attr_accessor :comments
alias :site_payload_without_comments :site_payload
def site_payload
if self.comments
payload = {
"site" => { "comments" => self.comments.values.flatten.sort.reverse },
}.deep_merge(site_payload_without_comments)
else
payload = site_payload_without_comments
end
payload
rescue Exception => e
puts "Site exception: " + e
end
end
class Jekyll::Post
alias :to_liquid_without_comments :to_liquid
def to_liquid
data = to_liquid_without_comments
data['comments'] = StaticComments::find_for_post(self)
data['comment_count'] = data['comments'].length
data
end
end
module StaticComments
class StaticComment
include Comparable
include Jekyll::Convertible
MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)-([0-9]+)(\.[^.]+)$/
attr_accessor :site
attr_accessor :id, :url, :post_title, :date, :author, :email, :link
attr_accessor :content, :data, :ext, :output
def initialize(post, comment_file)
@site = post.site
@post = post
self.process(comment_file)
self.read_yaml('', comment_file)
if self.data.has_key?('date')
self.date = Time.parse(self.data["date"])
end
if self.data.has_key?('name')
self.author = self.data["name"]
end
if self.data.has_key?('email')
self.email = self.data["email"]
end
if self.data.has_key?('link')
self.link = self.data["link"]
end
self.url = "#{post.url}#comment-#{id}"
self.post_title = post.slug.split('-').select {|w| w.capitalize! || w }.join(' ')
payload = {
"page" => self.to_liquid
}
do_layout(payload, {})
rescue Exception => e
puts "Exception: " + e
end
def process(file_name)
m, cats, date, slug, index, ext = *file_name.match(MATCHER)
self.date = Time.parse(date)
self.id = index
self.ext = ext
end
def <=>(other)
cmp = self.date <=> other.date
if 0 == cmp
cmp = self.post.slug <=> other.post.slug
end
return cmp
end
def to_liquid
self.data.deep_merge({
"id" => self.id,
"url" => self.url,
"post_title" => self.post_title,
"date" => self.date,
"author" => self.author,
"email" => self.email,
"link" => self.link,
"content" => self.content
})
end
end
def self.find_for_post(post)
post.site.comments ||= Hash.new()
post.site.comments[post.id] ||= read_comments(post)
end
def self.read_comments(post)
comments = Array.new
Dir["#{post.site.source}/_comments/#{post.date.strftime('%Y-%m-%d')}-#{post.slug}-*"].sort.each do |comment_file|
next unless File.file?(comment_file) and File.readable?(comment_file)
comment = StaticComment.new(post, comment_file)
comments << comment
end
comments
end
end
With this plugin, comments must be in _comments
directory and named like the post + a comment number (example: 2012-06-11-jekyll-installation-1.md
). The content is something like that:
---
date: 2011-12-29 17:11
name: A. Nonymous
email: anonymous@example.net
link: http://www.example.net
---
This is a `static` comment with **markdown** syntax.
The other benefit of doing it that way, is that I can do an atom feed for comments like that:
---
layout: nil
---
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<id>{{ site.url | xml_escape }}/</id>
<title>{{ site.name | xml_escape }} - Comments</title>
<link href="{{ site.url | xml_escape }}/atom-comments.xml" rel="self"/>
<link href="{{ site.url | xml_escape }}/"/>
<updated>{{ site.time | date_to_xmlschema }}</updated>
{% for comment in site.comments limit:site.paginate %}
<entry>
<id>{{ site.url | xml_escape }}{{ comment.url | xml_escape }}</id>
<title type="html">Comment on {{ comment.post_title | xml_escape }} by {{ comment.author | xml_escape }}</title>
<link href="{{ site.url | xml_escape }}{{ comment.url | xml_escape }}"/>
<updated>{{ comment.date | date_to_xmlschema }}</updated>
<author>
<name>{{ comment.author | xml_escape }}</name>
</author>
<content type="html">{{ comment.content | xml_escape }}</content>
</entry>
{% endfor %}
</feed>
For now the only other plugins I'm using are a sitemap generator plugin and a Pygments cache. But there is a plugin that I would like (I need to search for it or write it myself) that would allow me to write scheduled posts. It could be something like when the site is generated it writes in a file the next date the site should be generated again (if there are scheduled posts) and a cron task would regularly check this date and generate again the site if needed.
Links
- http://vitobotta.com/how-to-migrate-from-wordpress-to-jekyll/
- https://github.com/mojombo/jekyll/wiki/YAML-Front-Matter
- http://hezmatt.org/~mpalmer/blog/2011/07/19/static-comments-in-jekyll.html
- http://theshed.hezmatt.org/jekyll-static-comments/
- https://github.com/mpalmer/jekyll-static-comments
- http://www.sitemaps.org/protocol.html
Comments Add one by sending me an email.