52

I'm looking for a best practice/standard pattern for generating feeds in Rails 3. Is http://railscasts.com/episodes/87-generating-rss-feeds still valid?

shingara
  • 46,608
  • 11
  • 99
  • 105
Jeppe Liisberg
  • 3,734
  • 3
  • 25
  • 24

2 Answers2

115

First of all, nowadays I recommend using an ATOM feed instead of RSS.

The specification of ATOM feed offers more value than the RSS one with internationalization, content types and other things and every modern feed reader supports it.

More info about ATOM vs RSS can be found at:


On to the coding:

This example assumes:

  • a model called NewsItem with the following attributes:
  • title
  • content
  • author_name
  • a controller for that model (news_items_controller.rb), to which you'll add the feed action

We'll use a builder template for this and the Ruby on Rails atom_feed helper which is of great use.

1. Add the action to the controller

Go to app/controllers/news_items_controller.rb and add:

def feed
  # this will be the name of the feed displayed on the feed reader
  @title = "FEED title"

  # the news items
  @news_items = NewsItem.order("updated_at desc")

  # this will be our Feed's update timestamp
  @updated = @news_items.first.updated_at unless @news_items.empty?

  respond_to do |format|
    format.atom { render :layout => false }

    # we want the RSS feed to redirect permanently to the ATOM feed
    format.rss { redirect_to feed_path(:format => :atom), :status => :moved_permanently }
  end
end

2. Setup your builder template

Now let's add the template to build the feed.

Go to app/views/news_items/feed.atom.builder and add:

atom_feed :language => 'en-US' do |feed|
  feed.title @title
  feed.updated @updated

  @news_items.each do |item|
    next if item.updated_at.blank?

    feed.entry( item ) do |entry|
      entry.url news_item_url(item)
      entry.title item.title
      entry.content item.content, :type => 'html'

      # the strftime is needed to work with Google Reader.
      entry.updated(item.updated_at.strftime("%Y-%m-%dT%H:%M:%SZ"))

      entry.author do |author|
        author.name entry.author_name
      end
    end
  end
end

3. Wire it up with a route

Let's make the feeds available at http://domain.example/feed

This will call the action with the ATOM format by default and redirect /feed.rss to /feed.atom.

Go to config/routes.rb and add:

resources :news_items
match '/feed' => 'news_items#feed',
      :as => :feed,
      :defaults => { :format => 'atom' }

4. Add the link to ATOM and RSS feeds on the layout

Finally, all that is left is to add the feed to the layout.

Go to app/views/layouts/application.html.erb and add this your <head></head> section:

<%= auto_discovery_link_tag :atom, "/feed" %>
<%= auto_discovery_link_tag :rss, "/feed.rss" %>
Stephen Ostermiller
  • 23,933
  • 14
  • 88
  • 109
tomeduarte
  • 2,803
  • 1
  • 17
  • 10
  • 1
    To give credit where it's due, this is an implementation I did and use based off this blog post and its comments: http://lindsaar.net/2010/2/12/how-to-make-an-rss-feed-or-atom-feed-in-rails – tomeduarte Jan 28 '11 at 20:14
  • but why add a totally new action as opposed to simply a new format for say the index action? – holden Feb 05 '11 at 08:57
  • 2
    @holden - because I like to do code reuse and use this as a module on several projects. This way, if I want to change something it's a one-time change and does not impact the index action logic (e.g. disabling the pagination for the feed format(s) as you did in your answer). Personal preference, both are equally valid imho. – tomeduarte Feb 05 '11 at 18:57
  • 1
    @tomeduarte : best answer ever – Marcel Falliere Apr 30 '11 at 17:04
  • 2
    @tomeduarte nice answer but i think im right in saying that based on the routes you have suggested the auto_discovery_link_tag's you have provided are incorrect. they should indeed be "/feed" and "/feed.rss". I realise you have said there might be typos though so im sure this is just an oversight. :-) – Pete Jun 05 '11 at 11:25
  • @zoltarSpeaks You are correct, I missed that when posting! Thanks for the input. +1 – tomeduarte Jun 08 '11 at 15:11
  • 3
    what if i want to display an image for the article how do i pass the image entry. – Uchenna Sep 25 '11 at 09:30
10

I did something similar but without creating a new action.

index.atom.builder

atom_feed :language => 'en-US' do |feed|
  feed.title "Articles"
  feed.updated Time.now

  @articles.each do |item|
    next if item.published_at.blank?

    feed.entry( item ) do |entry|
      entry.url article_url(item)
      entry.title item.title
      entry.content item.content, :type => 'html'

      # the strftime is needed to work with Google Reader.
      entry.updated(item.published_at.strftime("%Y-%m-%dT%H:%M:%SZ")) 
      entry.author item.user.handle
    end
  end
end

You don't need to do anything special in the controller unless you have some special code like i did. For example I'm using the will_paginate gem and for the atom feed I don't want it to paginate so I did this to avoid that.

controller

  def index
    if current_user && current_user.admin?
      @articles = Article.paginate :page => params[:page], :order => 'created_at DESC'
    else
      respond_to do |format|
        format.html { @articles = Article.published.paginate :page => params[:page], :order => 'published_at DESC' }
        format.atom { @articles = Article.published }
      end
    end
  end
Community
  • 1
  • 1
holden
  • 13,471
  • 22
  • 98
  • 160
  • your example looks pretty straight forward - is it just these 2 steps to getting the atom feed working or are there more? – ubique May 25 '11 at 00:33
  • @ubique: I just played with this in one of my controllers out of curiosity. It uses the new Rail 3 format so all I had to do was add :atom to my respond_to hash - I didn't even have to touch respond_with. Rails automagically found the index.atom.builder with no further modifications to the controller. I'm thoroughly impressed coming from a PHP background! – Blu Dragon May 25 '11 at 06:15
  • what if i want to display an image for the article how do i pass the image entry. – Uchenna Sep 25 '11 at 09:29