emphatic solutions : ephemeral musings in the ether...
Brian Doll, Software/Systems Architect, San Francisco Bay Area, California

Quick and Easy - Cache RSS feeds in your Rails app

November 2008

Displaying a feed within your Rails app is pretty easy. It turns out caching that RSS feed is pretty easy too. I recently added cached RSS feeds to an app and had a few tiny hoops to jump through, so I thought I’d document them here for anyone else looking to solve the same problem.

Caching is easy. Cache invalidation is hard. For an RSS feed, we’d have to ping the feed to determine if any data had changed. Why bother? Instead of fetching the RSS feed on every single page request, let’s cache it for a fixed period of time.

The timed_fragment_cache plugin adds a duration option to the Rails cache method, allowing you to specify how long to cache a fragment of code. So, get it!

> ./script/plugin install git://github.com/GeorgePalmer/timed_fragment_cache.git

To integrate that cache method into a page, it might look like this:

<%- cache "statulous_blog", (Time.now + 60.minutes) do -%>
  <%= render_rss_feed("http://example.com/rss.xml") %>
<%- end -%>

The render_rss_feed helper method looks like this:

require 'rss/1.0'
require 'rss/2.0'
require 'open-uri'
require 'socket'
module ApplicationHelper
 
  def render_rss_feed(url)
    content = ""
    open(url, 0) do |s| content = s.read end
    feed = RSS::Parser.parse(content, false)
    @link = feed.channel.link
    @title = feed.channel.title
    @items = feed.channel.items[0..4] # just use the first five items
    render :partial => 'home/rss_view'
  end
 
end

And from there, pretty up that feed however you like. Here I’m just using the link, title and date:

<h3><a href='<%= @link %>'><%= @title %>:</a></h3>
<small>The most recent 5 entries:</small><br/>
<% @items.each do |item| %>
  <li><strong><a href='<%= item.link %>'><%= item.title %></a>
	</strong> - <small>(<%= item.date.strftime('%m/%d/%Y') %>)</small>
<% end %>

private method `gsub’ called for #

If you are using Rails 2+ and you try to get fancy with that duration specification, using something like 30.minutes.from_now, you’ll end up getting that gsub error. Here is why:

%ruby script/console
Loading development environment (Rails 2.1.0)
>> (Time.now + 30.minutes).class
=> Time
>> 30.minutes.from_now.class
=> ActiveSupport::TimeWithZone

Note: 30.minutes.from_now.time will result in a Time class, and will work equally as well as (Time.now + 30.minutes). It’s up to you which of those two are more readable and intention revealing.

Caution: Feed parsing is hard

The best part about feed standards is that there are so many to choose from! The code above is trivial, and will only handle feeds that fit that particular convention. Since I’m consuming feeds that I am also responsible for creating, I can opt for this simplest thing that could possibly work approach.

As soon as you start consuming several feeds, you’ll start to see just how diverse these feeds can be. Empty channels, posts without titles, non-unique identifiers… these are just the beginning. I’d love to hear what robust feed parsers folks are using in Ruby. The best feed parser I know of, and that some Rubists even use, is Universal Feed Parser, a Python library with 3k unit tests. Good luck!


Thoughts from Twitter


About the author:

Brian Doll is a business-focused technologist who has been building things on the web for over 13 years. He has extensive experience in retail, media and financial service industries in both start-up and large enterprise environments.

He enjoys speaking on lean engineering, web application performance and systems architecture. Having been inspired by Ruby and reinvigorated by Rails, Brian has been an avid contributor in the Ruby/Rails community since early 2007.

Additionally, he is a husband, father, thought worker, tree-hugging, music-loving, punk, atheist, non-conformist, optimist, Quality seeker. Phew! Here you'll find a mix of thoughts on fitness (Crossfit, Paleo foods), philosophy and programming (Ruby, Rails and other goodies).



Generated by Jekyll
Copyright © 2007-2010 Brian Doll