Update (26/01): I’m using IDs on the feeds now, as I was getting duplicated feed entries. I followed this post written by Carlos Brando.

I had some trouble recently with atom feeds. I made it work with almost all the readers around there, but it took some time until I found why it wasn’t working with Google Reader.
So, I’ll try to save some people time here.

First, you need a action for creating the feeds (could be the index action too, you just need to add the format.atom line):

# app/controllers/stories_controller.rb
def feed
    @project = Project.find(params[:project_id])
    @type = @project.types.find(params[:type_id])
    @stories = @type.stories(:order => 'updated_at desc')
 
    respond_to do |format|
      format.atom
    end
  end

Now, you need a builder for that action:

# app/views/stories/feed.atom.builder
atom_feed do |feed|
  feed.title("#{@project.name}->#{@type.name}")
  feed.updated(@stories.first.updated_at.strftime("%Y-%m-%dT%H:%M:%SZ"))
 
  for story in @stories
    next if story.updated_at.blank?
    feed.entry([@project, @type, story]) do |entry|
      entry.title(story.headline)
      entry.content(story.text, :type => 'html')
      entry.updated(story.updated_at.strftime("%Y-%m-%dT%H:%M:%SZ")) # needed to work with Google Reader.
      entry.author do |author|
        author.name(story.author)
      end
    end
  end
end

And finally, a route to the feed:

map.connect '/projects/:project_id/types/:type_id/feed', :controller => 'stories', :action => 'feed', :format => 'atom'

Now you can check if it’s valid with the Feed Validator.

The problem why it wasn’t working with Google Reader was that I forgot the entry.updated line. Also, you need to format the time as it is above, so it’ll follow the rules for dates in the atom specification and will work with Google Reader.