February 2008

Building a Website with Webby

Unless you didn’t know, this blog is generated by Webby. That’s a pretty neat micro-CMS built by Tim Pease. In my opinion (and for this site) it’s absolutely genius! Let me show what I would like from a blog-engine:


You decide how you would write your site. Since everything is just plain text files you can use Vim, Emacs, TextMate or even Notepad. At the top of each page you can define meta-data. On this post it looks like this followed by the content in Textile-format:

 title:      Building a Website with Webby
 created_at: 2008-02-25 22:45:43.134435 +01:00
   - fn
   - uv
   - textile

The title and created_at is only used by me, while Webby uses the filters to do stuff with the content. uv and fn are created by me (more on those later), textile simply uses Textile to format my text.

Speed of Light

Since Webby simply generates static files, they will be served by Apache by the speed of light! By simply running rake deploy, my blog is getting rebuild and deployed to the server (using rsync). It’s simple and it’s working great!


The best thing about Webby is that it’s dead simple to hack away with. You can monkey patch everything thanks to Ruby and Webby includes all files in the lib-folder, so there is no need to edit the source directly.

Instead of putting my libraries in lib, I just have a file named lib/hacks.rb and it looks exactly like this:

require 'rss/rss'
require 'uv'

Webby::Filters.register :fn do |input|
  input.gsub!(/fn(\d+)\. ([^\n]+)/) do |s|
    "<li id=\"fn#{$1}\"><sup>#{$1}</sup> #{$2}</li>"
  li = '<li id="fn\d+.*?</li>'
  input.sub(/#{li}([\n\040]+#{li})*/, '<ul class="footnotes">\0</ul>')

Webby::Filters.register :uv do |input|
  # That closing tag below should be in lowercase, but
  # I can't write it without closing the real tag :(
  input.gsub(%r{<syntax( lang="(\w+)")?>(.*?)</SYNTAX>}m) do |s|
    if $2
     "<notextile>" + Uv.parse($3, "xhtml", $2, false, "sunburst") + "</notextile>"

First I’m requiring rss/rss (for formating the dates in my feed) and uv (for syntax-highlighting).

Then I’m defining a filter (fn) which looks for Textile-footnotes and puts them in a list. Normally each of the footnotes will be wrapped in a paragraph, but since I have a lot of space between them1 it looks weird. Now all I need to do is to prepend fn in the filter-list.

I prefer Ultraviolet over CodeRay, so making a syntax-filter was obligatory. The only thing that sucks a bit is that I can’t type </syntax> inside the tag and <SYNTAX> (in lowercase) outside, but I’ll survive…


After you’ve mastered the editing and creating custom filters, you might want to create an index page. You could insert everything manually, but that’s kinda not-so-cool and won’t work when you also want to show the content of another page.

The solution is right in front of you: @pages. It’s an instance of Webby::PagesDB (read the docs) and @pages.find is your friend.

   - erb
 <% pages = @pages.find(:limit => :all,
                        :draft => nil,
                        :in_directory => 'posts',
                        :sort_by => 'created_at').reverse
    first = pages.shift %>

 <h2 id="first"><a href="<%= first.url %>"><%= first.title %></a></h2>
 <%= first.render %>

 <% pages.each do |page| %>
   <h2><a href="<%= page.url %>"><%= page.title %></a></h2>
 <% end %> 

That’s my index.txt. I’m using @pages.find to get all my posts which not have any meta-data called draft, sorted by creation-time and reversed. Then I’m taking out the first post and using #render to render the first post, while the rest would just show up with a title.

In my layout (default.rhtml) I have this, so the title won’t appear on the index page:

<% unless @page.url == "/" %>
  <h2 id="first"><a href="#"><%= @page.title %></a></h2>
<% end %>

An annoying bug

The default behavior is to convert symbol to a string and search for that string in the resource’s meta-data. If found, the meta-data item is returned; otherwise, nil is returned.

method_missing in Webby::Resource

An important thing to remember is that if you try to run #inspect (or using p) on a page, will just give you nil. That’s because it’s got a very nifty #method_missing which looks for the meta-data. And when you don’t have defined inspect in the meta-data, it just returns nil.

I spent a lot of time on trying to get my test-page work, just to figure out that this code prints out lots of empty lines when it’s built:

 title: Testing
   - erb

 <% @pages.each{|x|p x} %> 


In these busy days, you can’t run a blog without providing a feed. That shouldn’t be too hard. You just have to look at some examples and dive into Mark to make proper id’s.

Atom uses the ISO 8601-standard to format dates. Luckily for us, that is already supported in Ruby. All you have to do is require 'rss/rss' and Time#iso8601 is at your service. If you’re lazy and don’t want to write it all by yourself, you can steal from mine.


The bottom line is that Webby is genius2! It generates static files that can be server at top speed by any web server. Tim have done a great job and due to Ruby’s openness it’s really easy to hack around with. I can rebuild it and tweak it on my machine and when I’m ready I just type rake deploy, which builds it and copies all files over (with rsync) to the server.

The only thing I miss is comments, but that’s not so important. We’re living in a world were we have dozens of way to communicate. If you have any feedback, please mail me at judofyr@gmail.com.