Andy from Webcrunch

Subscribe for email updates:

Portrait of Andy Leverenz
Andy Leverenz

February 12, 2023

Last updated November 5, 2023

How to create an RSS feed with Ruby on Rails

Many moons ago, RSS was all the hype. With fewer publication engines like Medium, Substack, and other newsletter-style engines, most people would create their websites to house their content.

I am one of those long-time personal blog lovers. It's a significant reason this blog exists; I've been contributing to it for over six years.

I recommend reading a private blog of an individual or small collective rather than a giant platform driven by clicks and impressions. The content is more genuine and provokes more thought and empathy. All this to say, my opinion isn't too important! Let's move toward the real reason you stumbled across this blog post.

Forming a basic app

I created a simple blog using this demo application's Post model. The model is four columns in total.

  • title
  • content
  • created_at
  • updated_at
rails new rss_rails -c tailwind -j esbuild
rails generate scaffold Post title:string content:text

Adding a discovery tag

To get RSS to work, you need a new type of data that is publically accessible. This is typically XML data in the blogging and podcasting space. Rails will default render HTML and JSON when we generate a scaffolded resource. You can see this in your controller after running the commands above.

# app/controllers/posts_controller.rb
class PostsController < ApplicationController
  before_action :set_post, only: %i[ show edit update destroy ]

  # GET /posts or /posts.json
  def index
    @posts = Post.all
  end

  # GET /posts/1 or /posts/1.json
  def show
  end

  # GET /posts/new
  def new
    @post = Post.new
  end

  # GET /posts/1/edit
  def edit
  end

  # POST /posts or /posts.json
  def create
    @post = Post.new(post_params)

    respond_to do |format|
      if @post.save
        format.html { redirect_to post_url(@post), notice: "Post was successfully created."}
        format.json { render :show, status: :created, location: @post }
      else
        format.html { render :new, status: :unprocessable_entity }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # PATCH/PUT /posts/1 or /posts/1.json
  def update
    respond_to do |format|
      if @post.update(post_params)
        format.html { redirect_to post_url(@post), notice: "Post was successfully updated."}
        format.json { render :show, status: :ok, location: @post }
      else
        format.html { render :edit, status: :unprocessable_entity }
        format.json { render json: @post.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /posts/1 or /posts/1.json
  def destroy
    @post.destroy

    respond_to do |format|
      format.html { redirect_to posts_url, notice: "Post was successfully destroyed."}
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_post
      @post = Post.find(params[:id])
    end

    # Only allow a list of trusted parameters through.
    def post_params
      params.require(:post).permit(:title, :content)
    end
end

Rails can serve several data types without any effort besides following conventions. You'll need to add a new view file to get data to render in a specific format. Those formats might be HTML, JSON, or XML, depending on your needs.

You can see this logic inside the controller's create and update actions. In these two cases, we are instructing the framework though a lot of the work is handled behind the scenes, which I'll demonstrate in this guide.

Allowing other sites and services to discover RSS

For RSS to work correctly in the app, we need to append a discovery URL to help feed readers know about the RSS feed behind the scenes. Think of this as a flag being raised, and on the bottom of the flag is a copy of all the data you need to know about.

Inside the app/views/posts/index.html.erb file, I added a content_for block.

<!-- app/views/posts/index.html.erb-->

<% content_for :head do %>
  <%= auto_discovery_link_tag(:rss, posts_url(format: "rss")) %>
<% end %>

This view helper will generate a link tag in the source HTML code that points directly to the posts_url (i.e., app.com/posts.xml).

To ensure the content_for block gets rendered, I added a corresponding yield statement to the application.html.erb layout file inside the <head></head> tags.

<!-- app/views/layouts/application.html.erb-->
<!DOCTYPE html>
<html>
  <head>
    <title>RssRails</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>

    <%= yield :head %> <!-- add this line -->
  </head>

Add some posts

We must create content to fill the RSS feed to test things out. Feel free to do this manually in the UI or using rails console.

Render XML

We're very close to already being done, but first, we need to format the data served from the index action of the posts_controller. We already have the instance variable @posts in our arsenal, so that the magic can happen thanks to how Rails responds to requests.

Create an index.xml.builder file and add it to the app/views/posts folder..builder` files are part of a separate gem that has since become a part of the Ruby on Rails framework. For more details, you can give the code a look on GitHub.

The builder file gives us an excellent way to write XML in Ruby. Following the conventions of what most blogs used to publish, I ended up with the following code.

# app/views/posts/index.xml.builder
xml.instruct! :xml, version: "1.0"
xml.rss version: "2.0" do
  xml.channel do
    xml.title "RSS Rails"
    xml.description "Some random description."
    xml.link posts_url

    @posts.each do |post|
      xml.item do
        xml.title post.title
        xml.description post.content
        xml.pubDate post.created_at.to_s(:rfc822)
        xml.link post_url(post)
        xml.guid post_url(post)
      end
    end
  end
end

With this file intact, you should now be able to boot your server locally and visit localhost:3000/posts.xml. In the browser, you might see some XML display which means everything is working!

Taking this a step further

I've only scratched the surface in this guide, but this should give you some foundational strategies for getting started. No doubt XML/RSS is older tech, but surprisingly, the podcast industry still swears by it. The subscription concept makes it very powerful and allows others to build feed readers to do amazing things.

I mentioned it in the video, but I may build a micro newsletter SaaS product for fun using Ruby on Rails soon. Think of it as a Substack competitor with some different twists.

Let me know if you'd like to see this and what kind of features you would add if you were taking this on!

Tags: rss xml
Link this article
Est. reading time: 5 minutes
Stats: 1,986 views

Categories

Collection

Part of the Ruby on Rails collection