Typo sidebars: Recent Comments and Tagged Articles

Posted by Eric Kidd Sun, 13 Nov 2005 21:04:00 GMT

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? -%>
<h3><%=h @sb_config['title'] %></h3>
<ul class="tagged_articles">
<% for article in @articles -%>
  <li><%= link_to article.title,
                  article_url(article) %></li>
<% end -%>
</ul>
<% 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([<<END_SQL, @sb_config['count']])
SELECT article.*, comment.author AS comment_author, comment.id AS comment_id
  FROM contents AS comment
  LEFT JOIN contents AS article ON comment.article_id = article.id
  WHERE comment.type = 'Comment' AND article.type = 'Article'
  ORDER BY comment.created_at DESC LIMIT ?
END_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.

Tags , ,

Comments

  1. pknodle said 36 minutes later:

    Cool site. I need to look in to the layouts to make spiffy sites like this.

    BTW, the code text box messed with scrolling with the scrollwheel on FireFox on Linux.

  2. pknodle said 36 minutes later:

    Whoa! That fading effect was kewl! Let’s do that again!

  3. Eric said about 2 hours later:

    The fading effect is done with Script.aculo.us, a spiffy but rather undocumented Javascript library.

    You can find it in the public/javascripts directory of your Rails application.

  4. tony said 83 days later:

    Good news, Eric. Looks like the typo developers have changed their data model. Your original solution works with 2.6.0 (that’s the only one I’ve tested with); there’s no need for the hand-crafted query anymore!

Comments are disabled