A custom wiki with Gollum
I've had a personal wiki for some time. Until recently I went with
Mediawiki, simply because I've been too lazy to evaluate any
alternatives. But keeping it uptodate, upgrading on a regular basis to
cope with emerging vulnerabilities wasn't really worth it. I quess I
was hardly using 5% of Mediawiki's features anyway.
So when I heard about Gollum (the wiki which is also at work on
github.com) I decided to give it a shot. I very much liked the idea
that I would never again have to migrate or setup a database just for
my wiki, since Gollum stores its content on disk in a git
respository. Which in turn gives you additional benefits, like being
able to edit any file on the disk with your favorite editor.
Even extending Gollum's functionality with some custom JavaScript,
which I had hacked for Mediawiki, turned out to be a surisingly
pleasant experience, due to Rack's concept of middlwares, but more
about that shortly.
So here is how I set my new wiki up:
Step 2: Create two files. A file called `Gemfile` will hold the
dependencies of our project. For now this is only Gollum itself. But
we might add to that later as we customize our wiki. The Gemfile is
consumed by `bundler`, a Ruby dependency management tool, which we
will install and run in the next step.
A file called `config.ru` contains the setup to run our
wiki. config.ru is consumed by any application server which adheres to
the principles introduced by `Rack`. Rack can be seen as an interface
to web applications, although this is more of a simplification than
a good description. Although Gollum ships with its own command to fire
up a webserver, on a server you might want to take a more robust
approach and run Gollum within an Apache/Nginx webserver via passenger
(aka. mod_rails), luckily these know what to do with a config.ru, so
deployment is pretty straight forward.
Step 3: Install and run Bundler. Bundler will install Gollum and its
dependencies. After bundler has done its job we should be able to
start our wiki with the command `gollum`. This will fire up Sinatra
which is the web application framework Gollum is built on. Sinatra by
default starts a webserver on port 4567. So if you direct you browser
to http://localhost:4567/ you should see your newly created wiki.
Step 4: Customize the wiki with injecting a custom JavaScript via
Rack. We'll create two more files. The file `rack-injector.rb` will
hold a Rack middleware which will inject some markup into every html
which is served by gollum. The file `amazon.js` will hold the payload,
which will be injected by our middleware. Finally we have to add our
middleware to the Rack setup in config.ru.
With a restart of gollum, every tag on your pages with the class
`amazon` will be transformed into nicely looking bookshelves. Within
the tag you'll only need to list the ISBNs, one per line. For the
content of the iframes I used Amazon affiliate links. So be aware if
you copy and paste these examples you'll support me in writing this
blog. Thanks man.
A step by step guide
Step 1: Create a directory, change into it and initialize a git repository. This is not only the repo our project will be stored in, but at the same time the repo Gollum will store its content. (In a more sophisticated setup you might want to seperate these, but for my personal wiki it comes quite handy.)mkdir wiki
cd wiki
git init .
# Gemfile
source 'http://rubygems.org'
gem 'gollum'
# config.ru
#!/usr/bin/env ruby
require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../Gemfile", Pathname.new(__FILE__).realpath)
require 'rubygems'
require 'bundler/setup'
require 'gollum/frontend/app'
# setup of gollum
gollum_path = File.expand_path(File.dirname(__FILE__))
Precious::App.set(:gollum_path, gollum_path)
Precious::App.set(:default_markup, :markdown)
Precious::App.set(:wiki_options, {:universal_toc => false})
run Precious::App
gem install bundler
bundle
gollum
# rack-injector.rb
module Rack
class Injector < Struct.new :app, :options
def call(env)
status, headers, response = app.call(env)
if headers["Content-Type"] =~ /text\/html/
body = ""
response.each { |part| body << part }
index = body.rindex "</body>"
if index
body.insert index, options[:body]
headers["Content-Length"] = body.length.to_s
response = [ body ]
end
end
[ status, headers, response ]
end
end
end
// amazon.js
(function() {
var tmpl = '<iframe src="http://rcm-de.amazon.de/e/cm?lt1=_blank&bc1=FFFFFF&IS2=1&bg1=FFFFFF&fc1=000000&lc1=0000FF&t=branch14org-21&o=3&p=8&l=as1&m=amazon&f=ifr&ref=qf_sp_asin_til&asins=:asins" style="width:120px;height:240px;" scrolling="no" marginwidth="0" marginheight="0" frameborder="0"></iframe>';
var amazon = $(".amazon");
amazon.each(function(i, e) {
e = $(e);
var ids = e.html().split("\n");
e.empty();
$(ids).each(function (index, id) {
if(id != "") {
e.append(tmpl.replace(':asins', id));
}
});
});
})();
# in config.ru, before the setup of gollum
require File.expand_path('../rack-injector', __FILE__)
custom = File.read(File.expand_path('../amazon.js', __FILE__))
use Rack::Injector, :body => "<script type='text/javascript'>#{custom}</script>"