9.3. Adding an RSS and Atom Feed

Responding to a request for a given resource in XML format is usually done without the need for a Builder template. For example, in the ArticlesController you had:

respond_to do |format|
  format.html # index.html.erb
  format.xml  { render :xml => @articles }
end

The highlighted line will invoke the to_xml method and render the collection of Article objects in the default XML representation. In this case, @articles will contain only one record because you set the pagination to one article per page. Reaching for /articles.xml will provide the following (depending on your content, of course):

<?xml version="1.0" encoding="UTF-8"?>
<articles type="array">
  <article>
    <body>Hi there!

If you don't know what %{color:red}Rails% is, you can read more about it on the
"official website":http://rubyonrails.org and then buy Antonio Cangiano's
book. It's *highly recommended*. ;-)

By the way, did you know that Ruby on Rails(TM) is a trademark of &quot;David
Heinemeier Hansson":http://loudthinking.com?</book>
    <created-at type="datetime">2008-07-16T18:56:33-04:00</created-at>
    <id type="integer">4</id>

    <published type="boolean">true</published>
    <published-at type="datetime">2008-07-19T23:52:00-04:00</published-at>
    <title>Oh hi!</title>
    <updated-at type="datetime">2008-07-17T12:21:48-04:00</updated-at>
  </article>
</articles>

A great example of when this approach is not sufficient is the publication of an RSS (from version 2.0 this stands for Really Simple Syndication) or an Atom (to be exact, Atom Syndication Format) feed. Every feed is supposed to be a valid XML document, but their format also differs from the standard representation of the data provided by the method to_xml.

What you need for both of these formats is a Builder template that specifies how the final XML data is supposed to be formulated. And to provide you with a couple of concrete examples, you are going to add an RSS and an Atom feed to the "The Rails Noob" blog.

9.3.1. format.rss and format.atom

Modify appcontrollersarticles_controller.rb, so that its index action looks like this:

def index
  @articles = Article.published.paginate(:page => params[:page],
                                         :order => "published_at DESC")

@syndicated_articles = Article.published.all(:limit => 10,
                                         :order => "published_at DESC")respond_to do |format|
    format.html # index.html.erb
    format.xml  { render :xml => @articles }
    format.rss  { render :rss => @syndicated_articles  }
    format.atom { render :atom => @syndicated_articles }
  end
end

When you want to syndicate, say, 10 articles from among the ones you've published and present them in reversed chronological order so that newer articles are at the top, what you need to do is to retrieve those articles with Article.published.all and assign them to @syndicated_articles. That will be your source of information for the feeds that are independent from the syndication format you'll be using.

In the last two highlighted lines, you added a format.rss and format.atom within the respond_to block, so that the application now knows how to handle application/atom+xml and application/rss+xml media types (for example, requests for /articles.rss and /articles.atom).

As a reminder, Rails already recognizes both formats. If you want to specify a format that Rails doesn't know, you will need to register its MIME type in configinitializersmime_types.rb, before you'll be able to use it within the respond_to block.

Within the respective inline blocks, you use render :rss and render :atom similarly to how you applied render :xml to the preceding line, except this time you are passing the @syndicated_articles variable instead of @articles. The real twist, however, lies in the view. You will in fact prepare two Builder templates: index.rss.builder and index.atom.builder.

9.3.2. index.rss.builder and index.atom.builder

When a request for /articles.atom comes in, Rails executes that last line of code corresponding to the Atom format and makes the @syndicated_articles's content available to the template. Rails expects to find an index.atom.builder template much like it would expect an index.html.erb for HTML requests. And the same is true for the RSS format as well.

Listing 9-1 is the Builder template for your RSS feed.

Example 9.1. appviewsarticlesindex.rss.builder
xml.instruct! :xml, :version => "1.0"
xml.rss :version => "2.0" do
  xml.channel do
    xml.title("The Rails Noob")
    xml.description("My latest articles")
    xml.link(formatted_articles_url(:rss))

    for article in @syndicated_articles
      xml.item do

xml.title(article.title)
        xml.description(textilize(article.body))
        xml.pubDate(article.published_at.to_s(:rfc822))
        xml.link(formatted_article_url(article, :rss))
        xml.guid(formatted_article_url(article, :rss))
      end
    end
  end
end

The basic idea is that you use the xml object (which was introduced earlier) and with it you define a series of elements that are required by the RSS specification before looping through all the articles that are going to show up in the feed. The :rfc822 symbol that was passed to article.published_at.to_s ensures that the timestamp appears in the expected format.

Figure 9-1 shows the resulting feed in Internet Explorer.

Figure 9.1. Figure 9-1

In Listing 9-2 you'll find the code for the Atom feed's template.

Example 9.2. appviewsarticlesindex.atom.builder
atom_feed do |feed|
  feed.title("The Rails Noob")
  last_article = @syndicated_articles.first
  feed.updated(last_article.published_at) if last_article

  for article in @syndicated_articles
    feed.entry(article) do |entry|
      entry.title(article.title)
      entry.content(textilize(article.body), :type => 'html')

      entry.author do |author|
        author.name("Antonio Cangiano")
      end
    end
  end
end

Note how the article.body is passed to the helper textilize, so that the body content is transformed from Textile to HTML.

Here you wrap all of the code in the block that was passed to the special method atom_feed, you define a few required elements like the feed title and the time of your last update, and loop through the collection of articles that are intended for syndication (including their titles, content, and author names). Elements for the publication and updates for each individual entry will be added automatically by feed.entry.

Figure 9-2 shows the output in Internet Explorer.

Feed Entry Order

You will notice that the order of the first two posts is different in Figure 9-1 and Figure 9-2. The reason for this is that Internet Explorer uses the updated element instead of published to determine how to order the entries within the Atom feed. In my particular case, the "Lorem Ipsum" post was published before the "Oh hi" post, but I modified "Lorem Ipsum" last, and therefore its updated_at column is more recent in the database.

Upon opening the same feeds in Firefox, the entries will show up in the same order for both the RSS and Atom feeds. Depending on the feed reader used by the subscriber, when clicking "Subscribe to this feed," the entries will be displayed in the order of publication or their respective updates.


Figure 9.2. Figure 9-2

The output generated by the template for the RSS feed will be similar (depending on your data, of course) to the following output (truncated for clarity):

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>The Rails Noob</title>
    <description>My latest articles</description>
    <link>http://localhost:3000/articles.rss</link>
    <item>
      <title>Oh hi!</title>
      <description><p>Hi there!</p>

<p>If you don't know what <span
style="color:red;">Rails</span> is, you can read more about it
on the <a href="http://rubyonrails.org">official website</a>
and then buy Antonio Cangiano's book. It's <strong>highly
recommended</strong>. ;-)</p>

<p>By the way, did you know that Ruby on Rails]]>™<![CDATA[ is a trademark of
<a href="http://loudthinking.com">David Heinemeier
Hansson</a>?</p></description>

<pubDate>Sat, 19 Jul 2008 23:52:00 −0400</pubDate>
      <link>http://localhost:3000/articles/4.rss</link>
      <guid>http://localhost:3000/articles/4.rss</guid>
    </item>

    <item>
      <title>Lorem Ipsum</title>
      <description><p>Lorem ipsum dolor sit amet, consectetuer adipiscing
elit. Ut mi nisi, ullamcorper pharetra, imperdiet id, feugiat eu, justo. Class
aptent taciti sociosqu ad ...
      ...
      ...
      </description>
      <pubDate>Thu, 17 Jul 2008 02:36:00 -0400</pubDate>
      <link>http://localhost:3000/articles/2.rss</link>
      <guid>http://localhost:3000/articles/2.rss</guid>
    </item>
    ...
    ...
  </channel>
</rss>

For the Atom feed you'll have the following:

<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <id>tag:localhost,2005:/articles</id>
  <link type="text/html" rel="alternate" href="http://localhost:3000"/>
  <link type="application/atom+xml" rel="self" href="http://localhost:3000/
articles.atom"/>
  <title>The Rails Noob</title>
  <updated>2008-07-19T23:52:00-04:00</updated>
  <entry align="left" valign="bottom">
    <id>tag:localhost,2005:Article/4</id>
    <published>2008-07-16T18:56:33-04:00</published>
    <updated>2008-07-17T12:21:48-04:00</updated>
    <link type="text/html" rel="alternate"
href="http://localhost:3000/articles/4"/>
    <title>Oh hi!</title>
    <content type="html"><p>Hi there!</p>

<p>If you don't know what <span style="color:red;">
Rails</span> is, you can read more about it
on the <a href="http://rubyonrails.org">official website</a>
and then buy Antonio Cangiano's book. It's <strong>highly
recommended</strong>. ;-)</p>

<p>By the way, did you know that Ruby on Rails]]>™<![CDATA[ is a trademark of
 <a href="http://loudthinking.com">David Heinemeier
Hansson</a>?</p></content>
    <author>
      <name>Antonio Cangiano</name>
    </author>
  </entry>

<entry>
    <id>tag:localhost,2005:Article/2</id>
    <published>2008-07-16T10:31:33-04:00</published>
    <updated>2008-07-20T16:20:30-04:00</updated>
    <link type="text/html" rel="alternate" href="http://localhost:3000/articles/2"/
>
    <title>Lorem Ipsum</title>
    <content type="html"><p>Lorem ipsum dolor sit amet, consectetuer
adipiscing elit. Ut mi nisi, ullamcorper pharetra, imperdiet id, feugiat eu, justo.
  Class aptent taciti sociosqu ad ...
    ...
    ...
    </content>
    <author>
      <name>Antonio Cangiano</name>
    </author>
  </entry>
  ...
  ...
</feed>

Nowadays most developers favor Atom over RSS, but they are both extremely common.

9.3.3. Linking to the Feeds

At this point, all you need to do is provide your users with a link to at least one of your feeds, as well as allow browsers/clients to auto-discover it.

For the links, it's sufficient enough to use the two helpers, link_to and formatted_articles_path, within the articles.html.erb layout:

<li<>%= link_to 'Feed', formatted_articles_path(:atom) %></li>

This translates into:

<li><a href="/articles.atom">Feed</a></li>

If you want to advertise both versions of the feed, add a second link by passing :rss to the path helper. Alternatively, you may want to include an RSS feed icon to make the presence of a feed more prominent.

NOTE

In Rails 2.3 formatted_articles_path(:atom) will be written as articles_path(:format => "atom").

To add an auto-discovery link to the layout, use the auto_discovery_link_tag helper:

<head>
  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
  <title>The Rails Noob</title>
  <%= auto_discovery_link_tag :atom, formatted_articles_url(:atom) %>
  <%= stylesheet_link_tag 'site' %>
</head>

This facilitates the discovery of the feed URL starting from the site onward in feed readers and other automated services. It will also add the standard (typically orange) feed syndication icon to most browsers, as shown in Figure 9-3 and 9-4, respectively, for Internet Explorer and Mozilla Firefox.

You can check the validity of your feeds online at http://feedvalidator.org.

Figure 9.3. Figure 9-3

Figure 9.4. Figure 9-4

NOTE

As an exercise, feel free to create a feed for all the most recent comments and/or for the comments associated with a given article.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.16.137.38