Here's two new plugins for Typo, the cool Rails-based blogging software. The first shows a list of recent comments. The second shows articles with a specific tag. (I use it to implement the "Recommended Reading" list in my sidebar.)

To install the plugins, simply unzip them in your Typo root directory, restart Typo, and take a look at the "Sidebar" tab in the admin screen.

These plugins were unbelievably simple to write. If you'd like to see how they work, keep reading.

Writing sidebar plugins

Sidebars live in the components/plugins/sidebars directory. They consist of a controller named myplugin_controller.rb and two rhtml files in the myplugin directory.

Here's the controller for the Tagged Articles plugin:

class Plugins::Sidebars::TaggedController < Sidebars::Plugin
  def self.display_name
    "Tagged Articles"
  end

  def self.description
    "List of articles with a given tag"
  end
  
  def self.default_config
    {'title' => 'Recommended Reading',
     'tag' => 'Recommended',
     'count' => 5 }
  end

  def content
    limit = @sb_config['count']
    @articles =
      Article.find_published_by_tag_name(@sb_config['tag'],
                                         :limit => limit)
  end

  def configure
  end
end

The sidebar is rendered by content.rhtml:

<% unless @articles.blank? -%>

<%=h @sb_config['title'] %>

    <% for article in @articles -%>
  • <%= link_to article.title, article_url(article) %>
  • <% end -%>
<% end -%>

There's also a configure.rhtml file. It's quite short, and you can find a copy in the zip file.

Working around Typo's content table

In the "Recent Comments" plugin, I wanted to write:

Comment.find(:all,
             :order => 'created_at DESC',
             :limit => @sb_config['count'])

This works, but it will cause content.rhtml to perform extra SQL queries when we try to fetch the article title:

# Each time we do this, we have to go
# back to the database for the article.
comment.article.title

Fortunately, Rails offers us an easy workaround:

# Fetch articles along with the comments.
Comment.find(:all,
             :order => 'created_at DESC',
             :limit => @sb_config['count'],
             :include => [:article])

The :include parameter tells Rails to fetch each comment's article using a LEFT OUTER JOIN, so we won't have to look it up later.

Unfortunately, in this case, :include doesn't work. Typo stores articles and comments in the same table, which breaks :include very badly. Rails isn't smart enough to keep the two uses of the same table straight.

To work around this problem, we need to build a horrible database query by hand:

Article.find_by_sql([<

This returns a list of articles, one for each comment, with extra comment_author and comment_id fields. We then loop over the articles normally to render them.