I got tired of bad Wordpress performance at Dreamhost so I started looking around for new options. I found one with Toto. Toto is a micro-blogging framework written in Ruby. It appealed to me on several fronts - it’s Ruby and I like Ruby, it’s extremely lightweight and I can easily understand and extend it.

Toto is a single Ruby file ‘toto.rb’ that sits on top of the Rack infrastructure. I played around with it and liked what I saw. From the outside it looks very similar to a Wordpress installation. URLs still look like ‘/2009/03’ and ‘/2010/04/18/my-article’. That was important to me since I wanted to seamlessly migrate from my existing Wordpress blog.

You can’t get much more lightweight then Toto. It doesn’t use a database. Comments are handled by Disqus. And each article is a single file consisting of a YAML header followed by the article text. You can read much more about it in an excellent overview article written by Dmitry Fadeyev.

Since I was converting an existing Wordpress blog I needed to extract the existing posts. I wrote a short Ruby program to do that for me:

require 'rubygems'
require 'fastercsv'
require 'xmlrpc/client'

USERID = '<wordpress userid>'
PASSWORD = '<wordpress password>'

def getPages(blog)
  blog.call("wp.getPages", 0, USERID, PASSWORD, 1000)

def getPosts(blog)
  blog.call("metaWeblog.getRecentPosts", 0, USERID, PASSWORD, 1000)

def slugify(title)
  title.downcase.gsub(/[ \._]/, '-')

blog = XMLRPC::Client.new3(:host => "<blog domain>", :path => "/xmlrpc.php")
  puts "Logging into blog and getting the list of posts..."
  posts = getPosts(blog)
  puts "  response received, found #{posts.size} posts"
  `mkdir articles`
  posts.each do |post|
    permaLink = post['permaLink']
    dateAndName = permaLink.gsub(/http:\/\/<blog name>\.com\//, '').split('/')
    postFilename = 'articles/' + dateAndName.join('-') + '.txt'
    postDate = dateAndName[0] + '/' + dateAndName[1] + '/' + dateAndName[2]
    escapedTitle = post['title'].gsub(/"/, '\\"')
    File.open(postFilename, "w") do |postFile|
      postFile.puts("title: \"#{escapedTitle}\"")
      postFile.puts("author: <your name>")
      postFile.puts("date: #{postDate}")
      postFile.puts("slug: #{dateAndName[3]}")
      puts "#{post['title']} - #{postDate}"
      postFile.puts("categories: #{post['categories']}")
      postFile.puts("keywords: #{post['mt_keywords']}")
      postFile.puts post['description']
rescue XMLRPC::FaultException => e
   puts "ERROR: Code: #{e.faultCode}"
   puts "ERROR: Msg.: #{e.faultString}"

That program pulls out all of the posts and generates Toto article files for each of them. It dumps them into a directory called ‘articles’.

I created some of the pages by hand and updated a script I used to generate Wordpress pages to generate Toto pages instead. Pages are simply ERB (rhtml) files.

Toto didn’t mesh exactly with what I wanted to do so I copied it into my blog directory and started modifying it. That included supporting the ‘/page/n’ Wordpress URLs and other features. I also added YAML files to specify all of the categories and tags.

The most annoying problem I ran into was dealing with Disqus. I had already switched my Wordpress blog to use it so my comments were already imported. I ended up embedding this code into my layout:

<script type="text/javascript">
var disqus_domain = 'disqus.com';
var disqus_shortname = '<%= @config[:disqus] %>';
var disqus_url = '<%= @config[:base_url] + "/" + @path %>/';

<div id="disqus_thread"></div>
<script type="text/javascript">
	(function() {
		var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
		dsq.src = 'http://<%= @config[:disqus] %>.disqus.com/embed.js';
		(document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
<noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript=<%= @config[:disqus] %>">comments powered by Disqus.</a></noscript>

But I still had a difficult time getting Disqus to work reliably. I kept getting the dreaded ‘This page cannot be reached by Disqus’ error on some of my blog posts but not on others. Turned out to be a bad error message from Disqus. Wordpress post URLs had a trailing ‘/’ and Toto post URLs didn’t. I modified the code to include a trailing slash and all of my old comments magically appeared.

There were other challenges as well, like creating a sitemap.xml file, changing the way archives were handled, etc. But it was a fun little process to convert the blog.