Static MVC

Wednesday, August 06, 2008

Update Feb 4, 2009: I recently learned about Jekyll, which puts these ideas into a solid framework.

I know that web applications and content management systems are the hot thing right now (and maybe will be for some time), but sometimes you wonder whether a few static html pages wouldn’t be sufficient anyway. The advantages are pretty clear: No database back-end, no tweaking of apache configurations, and of course, nothing beats static html pages in terms of speed and memory requirements.

On the other hand, editing html pages by hand seems so “old-school”, and there are some real maintenance issues. Appearance can be nicely controlled by CSS, but if you actually want to move bits of information around, you have to do it yourself. And let’s just hope that you haven’t gotten into adding some nice Javascript effects to your pages… .

This approximately describes the situation I was in when I decided to restructure my own homepage.

It turns out that it is actually possible to write a page generator along the principles used in web frameworks such as rails and end up with something quite elegant. And it’s fun, too!

Some of the power of a framework like rails comes from a clear separation between data, business logic, and presentation. This kind of separation is also called the MVC-Pattern (Model-View-Controller). All the data is typically stored in a database like MySQL, and the presentation is encoded in template files.

So how can we duplicate this kind of separation with a static page-generator? Luckily, ruby comes with all the tools you need. Let us walk through a little example, the inevitable blog (without commenting functionality, of course).

Let’s start with the database. We will just store all the information into YAML files. YAML is a file format which puts special emphasis on being easy to read and maintain by a human. The format is pretty self-explanatory:

- date: August 4, 2008
  title: Same old, same old
  text: |
    Sometimes, you just wake up with that feeling that something
    great is happening today. I couldn't really lay my finger on
    it, but I was very sure that...

- date: August 6, 2008
  title: You wouldn't believe
  text: |
    You know when I said that something great would happen two
    days ago? And you know what...

This file contains two entries, encoded as hashes. The “|” indicates indented multi-line data.

Using the YAML library, you can easily load this data with

require 'yaml'

blog = YAML::load_file('blog.yaml')

And if you inspect blog you will find that is an array of two hashes.

Next, we need to render the result. We’ll use the erb library, which you might know from rails. Here is a possible template file

<html>
<head>
<title>My little static blog</title>
</head>
<body>
<% for b in blog %>
  <h1><%= b['title'] %></h1>

  <p><em><%= b['date'] %></em></p>

  <p><%= b['text'] %></p>
<% end %>
</body>
</html>

(Almost looks like were coding in rails, right? :))

Let’s finally put all things together. We store the YAML file in blog.yaml and the template file in blog.rhtml. Then, the following script will generate blog.html:

require 'yaml'
require 'erb'

blog = YAML::load_file('blog.yaml')

blog_template = open('blog.rhtml').read
html = ERB.new(blog_template).result

open('blog.html', 'w') {|o| o.write html}

And here is the result:

This way, you can easily add new posts by editing just the YAML file, and fiddle with the presentation in the template file. You’re not even restricted to a fixed number of pages, you could, for example, also generate a separate (static) page for each individual post (dynamically) by the script.

And it doesn’t stop here, adding a lightweight markup engine like BlueCloth you can even start using wiki-style markup in your texts.

So, it doesn’t always have to be a full fledged web application framework, you can have similar flexibility (minus the interactivity, of course), using a few readily available tools, and very few lines of code as well.

Posted by at 2008-08-06 16:22:00 +0000

blog comments powered by Disqus