Ruby
Article

Building an RSS Reader in Rails Is Easy

By Vinoth

rss red hexagon 3d modern design icon on white background

This article was peer reviewed by Thom Parkin. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!

An RSS feed is a data format used by websites (mainly blogs) to deliver content to users. Most websites, including SitePoint, publish a feed as another means of content distribution. A feed is a stream where the updated content is published in one of the standardized formats and can be consumed by readers, like RSS clients.

Consuming an RSS feed lets you follow many of your favorite websites and gather updated information in a single place. There are social media and email newsletters that serves this purpose, but the flexibility is what makes feeds distinct. There are many feed readers that enables you to enter all the feed URLs you want to follow and to read them in a single place. Today, we are going to build one such reader to serve your reading pleasure. We will also cache the contents for offline reading.

Let’s get started.

Bootstrapping the App

We are going to build the application in Rails 4. Though it may work with older versions, it’s recommended to keep up with the latest version to avoid any dependency issues. Let’s create our Rails app by running the following command:

rails new feedreader -d postgresql

Note: This presumes you have PostgreSQL running on localhost. If not, feel free to omit the -d flag and use SQLite.

Once the Rails generators are finished, cd into the directory and create the database:

rake db:create

Let’s also add the gem dependencies we will use in this app. Since this is fairly minimal app, we will need only two gems, feedjira for feed processing and twitter-bootstrap-rails for a little bit of styling. The bootstrap part is completely optional and you can ignore it if you want to use your own design.

Add the following gems to the Gemfile:

gem 'feedjira'
gem 'twitter-bootstrap-rails'

and run bundle install.

Once the installation is complete, run the bootstrap generator:

rails generate bootstrap:install static

This will add the necessary bootstrap files and add required requires. With that, our bootstrapping is complete. If you’re using version control, which is recommended, now is the time to commit the changes.

Creating Models

It’s time to create the models for our app. The structure is simple, as we have only two models: Feed and Entry. Feed contains the list of added feeds and Entry contains the content of each feed. This will be the schema for our app:

qiPd9cv
Let’s create the Feed model first. It is simple enough to just scaffold. Run the following commands to scaffold and migrate:

rails g scaffold feed name url description:text
rake db:migrate

This creates the necessary model, controller, and view files related to Feeds. We don’t have to do any modification on any of the files, just add the root path in the config/routes.rb file:

root 'feeds#index'

That’s it. Start the server and add your favorite feeds. It’s probably a good idea to commit your changes at this point into git:

git add -A
git commit -m "Add Feed scaffold"

Next, let’s create the Entry model which will hold all the content. Since we’re going to store the entries via a rake task, there is no need for full fledged components. We can create the Entry model directly by running the following command:

rails g model entry title published:datetime content:text url author feed_id:integer
rake db:migrate

After the migration is done, add the relationship to both the models:

## models/feed.rb
class Feed < ActiveRecord::Base
    has_many :entries, dependent: :destroy
end

## models/entry.rb
class Entry < ActiveRecord::Base
    belongs_to :feed
end

With that, we’ve completely sett up the models. Now it’s time to create that rake task to fill the Entry model from the Feeds that have been added:

Updating Feed Content

As I’ve mentioned before, we’ll have a rake task as an entry point for all the data in the Entries table. This task could be scheduled to run in periodic intervals to sync the feed content with our database. Start by creating a rake task inside lib/tasks called sync.rake. Once the file is created, add the following content to it:

namespace :sync do
  task feeds: [:environment] do
    Feed.all.each do |feed|
      content = Feedjira::Feed.fetch_and_parse feed.url
      content.entries.each do |entry|
        local_entry = feed.entries.where(title: entry.title).first_or_initialize
        local_entry.update_attributes(content: entry.content, author: entry.author, url: entry.url, published: entry.published)
        p "Synced Entry - #{entry.title}"
      end
      p "Synced Feed - #{feed.name}"
    end
  end
end

The rake task loops through all the Feeds stored in the database and fetches the latest content for each one. From that, loop through the new Entries, creating or updating it in the database. We are updating every time to keep up with any change in the source content.

After you have added this, try running the rake task:

bundle exec rake sync:feeds

You can see the contents have been added to the Entry table. This is easily verified by logging into the Rails console:

$ rails console
> Entry.count

Alright, now for the final step. Let’s create the controller for Entries.

Displaying the Feed

Start by generating the controller for our Entries model. We need only two actions: index and show, which can be specified it to the generator itself:

rails g controller entries index show

After the above command is executed, the controller and view files are created. Head over to routes.rb and change the following lines and add it under the feeds resources:

get 'entries/index'
get 'entries/show'

to

resources :feeds do
  member do
   resources :entries, only: [:index, :show]
  end
end

We have nested Entries under a specific feed here. Head over to the Entries controller and add the following:

# app/controllers/entries_controller.rb
class EntriesController < ApplicationController
  before_action :set_feed, only: :index

  def index
    @entries = @feed.entries.order('published desc')
  end

  def show
    @entry = Entry.find(params[:id])
  end

  private
  def set_feed
    @feed = Feed.find(params[:id])
  end
end

We are fetching respective feed by ID for the Entries index page to display the list of entries from that feed. Let’s also make the necessary changes to the views:

# app/views/entries/index.html.erb
<div class="container">
  <% @entries.each do |entry| %>
    <div class="panel panel-default">
      <div class="panel-body">
        <%= link_to entry.title, entry %> - <i> published <%= time_ago_in_words(entry.published) %> ago.</i>
      </div>
    </div>
  <% end %>
</div>

# app/views/entries/show.html.erb
<div class="container">
  <h3><%= link_to @entry.title, @entry.url %></h3>
  <i>published on <%= @entry.published %> by <%= @entry.author %></i>
  <p>
    <%= @entry.content.html_safe %>
  </p>
</div>

The code above is trivial. We’re displaying the Entry customized as per our needs. One thing to note is, we have used html_safe in the Entries show view since the feed will be HTML formatted. This allows us to render the HTML in the Entry. With that change, we have finished our tiny little feed reader that just works. Save the changes, and start the server.

Our finalized app looks something like this after adding the URL and running the rake task:

gVMou

gVMot

I know this is not the best looking feed reader, but it lets you to build one. We’ve got the functionality, now use your imagination for the design.

Conclusion

All the sample code used in this tutorial is available in Github feel free to fork and poke.

Thank you for taking time reading this tutorial and I hope it served your purposes. Until next time.

  • http://cashpath20.com JudyJMena

    .❝my neighbor’s mother is making $98 HOURLY on the
    internet❞….

    A few days ago new McLaren F1 subsequent after earning 18,512$,,,this was my
    previous month’s paycheck ,and-a little over, $17k Last month ..3-5 h/r of work a day ..with extra
    open doors & weekly paychecks.. it’s realy the
    easiest work I have ever Do.. I Joined This 7 months ago and now making over
    $87, p/h.

    Learn More right Here….website on my PrroFile
    =fsdgg

  • GOODLOOKS

    Where do you enter the feed URL? I think that’s missing from your steps

    • andrekibbe

      Right after the step of changing the root route, start the Rails server, go to the index page, and click “New Feed”. This assumes you’ve already added the sync Rake task, which you’ll have to run for the database to save the feed.

  • http://www.dare-studios.com Dan Rempel

    After following your instructions I’m unable to show the feed I was able to sync using the rake task… My root page and /feeds shows the rails default CRUD instead of the views we styled.

    Can you help me understand how it is we styled views in /entries but our root is set to /feeds? And why am I unable to see the feeds?

    • http://www.dare-studios.com Dan Rempel

      Nevermind, I figured it out. After reading the routes closely I realized you have to navigate to /feeds/1/entries.

      That wasn’t clear right away.

  • hkdeven

    Hmm, seems like there is an error when I attempt the *bundle exec rake sync:feeds* command. Says it doesn’t know how to build task ‘sync:feeds’

    • ReidasaurusRex

      Did you copy the code exactly? I had the same issue because I chose to namespace differently to feed_sync

  • Mike Gruszka

    How to you update the view when new items are added to the database without have to reload the browser?

Recommended

Learn Coding Online
Learn Web Development

Start learning web development and design for free with SitePoint Premium!

Get the latest in Ruby, once a week, for free.