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!
Categories
Collection
Part of the Ruby on Rails collection
Products and courses
-
Hello Hotwire
A course on Hotwire + Ruby on Rails.
-
Hello Rails
A course for newcomers to Ruby on Rails.
-
Rails UI
UI templates and components for Rails.