Create Multiple RSS Feeds For One Model In Ruby On Rails

Tressa Sanders
4 min readNov 25, 2020

--

Why? Why would you need multiple rss feeds for a single model in your Rails application?

I’m sure there are a ton of reasons but I’ll give you the reason I need multiple rss feeds for one model.

I have a podcast website.

Not only do I have a podcast website but that website is home to the multiple podcast shows that I write and produce. So, while I do have an rss feed that lists ALL of the podcast shows and all of the episodes associated with them, I still need separate rss feeds for each show because not every listener wants to be notified of every single episode for every single show. They only want to subscribe to their favorite show(s) and receive notification only for those shows.

The challenge I faced was trying to figure out how to create the feeds for each show so that the feed link looks like this “https://my.website.com/feed/podcast-show-name.rss”.

The “all-or-nothing” feed is already at “https://my.website.com/podcasts.rss”. And it already uses the index.rss.builder file in /views/podcasts/.

So in order to create rss feeds and links for the individual podcast shows without hard-coding the names, I had to set it up as follows:

In the /config/routes.rb file:

get ‘feed/:slug’ => ‘podcasts#feed’, :as => :feed

This creates a url for the “slug” of the individual podcast. If you are unfamiliar with “slug” it’s just the database field I use to store the friendly url for the podcast. I use the Stringex gem for every rails application to get friendly urls.

I didn’t want to use the “Title” field for the podcast because the titles are stored in the database with spaces. So for example one title is “Begin Transmission” but the friendly url or “slug” is “begin-transmission”.

I added the following action to /controllers/podcasts_controller.rb:

def feed
@podcasts = Podcast.where(:published => true).order(‘created_at DESC’)
@podcast = Podcast.find_by_slug(params[:slug])
respond_to do |format|
format.html
format.rss { render :layout => false }
end
end

The first line of this action is for the “all-or-nothing” feed. It allows me to iterate through all of the podcasts and their episodes for that feed. The view for the “all-or-nothing” feed is the /views/podcasts/index.rss.builder file.

The second line is for each individual podcast feed. It allows me to display data only for the one podcast who’s “slug” I’m using in the url. So you have to create a new builder view at /views/podcasts/feed.rss.builder.

Inside the /views/podcasts/index.rss.builder file, I have the following (my feed still needs improvement but this works all the same):

xml.instruct! :xml, version: “1.0”
xml.rss :version => ‘2.0’, ‘xmlns:atom’ => ‘http://www.w3.org/2005/Atom' do

xml.channel do
xml.title ‘Podcast Shows RSS Feed’
xml.description ‘RSS feed for my podcast shows.’
xml.link root_url + ‘podcasts’
xml.language ‘en’
xml.tag! ‘atom:link’, :rel => ‘self’, :type => ‘application/rss+xml’, :href => podcasts_url

for podcast in @podcasts
xml.item do
xml.title podcast.title
xml.category ‘fiction’
xml.pubDate podcast.created_at.to_date
xml.link podcast_url(podcast)
xml.guid podcast_url(podcast)
xml.description(h(podcast.description))
xml.image_url image_url(podcast.image)
xml.genre podcast.genre
xml.tagline podcast.tagline
xml.frequency podcast.frequency
xml.pubDate podcast.episodes.first.created_at.to_s(:rfc822)
xml.lastBuildDate podcast.episodes.first.created_at.to_s(:rfc822)
podcast.episodes.each do |episode|
xml.item do
xml.title episode.title
xml.summary episode.title
xml.pubDate episode.created_at.to_s(:rfc822)
xml.enclosure url: image_url(episode.file), length: episode.duration, type: ‘audio/mp3’
xml.link episode_url(episode)
xml.guid({isPermaLink: “false”}, episode_url(episode))
xml.itunes :author, ‘author’
xml.itunes :subtitle, truncate(episode.title, length: 150)
xml.itunes :summary, episode.title
xml.itunes :explicit, ‘no’
xml.itunes :duration, episode.duration
end
end
end
end

end

end

Inside the /views/podcasts/feed.rss.builder file for each podcast show, I have the following:

xml.instruct! :xml, version: “1.0”
xml.rss :version => ‘2.0’, ‘xmlns:atom’ => ‘http://www.w3.org/2005/Atom' do

xml.channel do
xml.title ‘Podcast Shows RSS Feed’
xml.description ‘RSS feed for my podcast shows.’
xml.link root_url + ‘podcasts’
xml.language ‘en’
xml.tag! ‘atom:link’, :rel => ‘self’, :type => ‘application/rss+xml’, :href => podcasts_url
xml.title @podcast.title
xml.category ‘fiction’
xml.pubDate @podcast.created_at.to_date
xml.link podcast_url(@podcast)
xml.guid podcast_url(@podcast)
xml.description(h(@podcast.description))
xml.image_url image_url(@podcast.image)
xml.genre @podcast.genre
xml.tagline @podcast.tagline
xml.frequency @podcast.frequency
xml.pubDate @podcast.episodes.first.created_at.to_s(:rfc822)
xml.lastBuildDate @podcast.episodes.first.created_at.to_s(:rfc822)
@podcast.episodes.each do |episode|
xml.item do
xml.title episode.title
xml.summary episode.title
xml.pubDate episode.created_at.to_s(:rfc822)
xml.enclosure url: image_url(episode.file), length: episode.duration, type: ‘audio/mp3’
xml.link episode_url(episode)
xml.guid({isPermaLink: “false”}, episode_url(episode))
xml.itunes :author, ‘author’
xml.itunes :subtitle, truncate(episode.title, length: 150)
xml.itunes :summary, episode.title
xml.itunes :explicit, ‘no’
xml.itunes :duration, episode.duration
end
end

end

end

To add a link to your website for the “all-or-nothing” rss feed, it’s as easy as doing the following:

Add this to the head of your layout file. Mine is at /views/layouts/podcast.html.erb.

This really isn’t required in order for you to add an RSS link but as the helper name suggests, it does help RSS readers automatically discover your RSS links.

<%= auto_discovery_link_tag :rss, podcasts_url(:format => :rss) %>

Then you can add a link like this, anywhere in your views (I added it on the index page for the podcasts and the “subscribe” button on the home page and footer.):

You don’t have to include the “target” or “id” if you don’t need them.

<%= link_to “RSS Feed”, podcasts_url(:rss), target: “_blank”, id: “podcasts_rss_link” %>

To add a link to your website for each individual podcast show, you can do as follows (or whatever you decide):

<%= @podcast.tagline %> | <%= link_to “Podcast RSS Feed”, feed_url(@podcast.slug, :rss), target: “_blank”, id: “podcast_rss_link” %>

I added this to the “show” page at /views/podcasts/show.html.erb.

It took me a bit to figure out how to use the auto discovery helper with these individual rss feeds… I ended up using the code below, however, I’m sure that this works because I used find_by_slug in the controller and this auto discovery link only exists in the podcast layout page. I have a feeling if I moved it to other layout pages, it wouldn’t work. I noticed on an error that it was using the “slug” as the “id” instead of the numerical “id”. That is why I used params[:id] instead of params[:slug].

<%= auto_discovery_link_tag :rss, feed_url(params[:id], :format => :rss) %>

That’s it!

It wasn’t really too hard to add this feature and I think it was worth getting it to work for the reasons I outlined above.

Do you have any improvements to add to this feature?

--

--

Tressa Sanders

Technical Writer, Senior English Trainer and Ruby on Rails Developer with over 20 years working in the Information Technology industry.