diff --git a/app/controllers/feed_controller.rb b/app/controllers/feed_controller.rb
new file mode 100644
index 00000000..e7a28ecc
--- /dev/null
+++ b/app/controllers/feed_controller.rb
@@ -0,0 +1,14 @@
+class FeedController < ApplicationController
+ skip_before_action :authenticate_user!
+
+ # GET /feed.rss
+ # GET /feed.atom
+ def index
+ @talks = Talk.all.order(created_at: :desc).includes(:speakers).limit(100)
+
+ respond_to do |format|
+ format.rss { render layout: false }
+ format.atom { render layout: false }
+ end
+ end
+end
diff --git a/app/views/feed/index.atom.builder b/app/views/feed/index.atom.builder
new file mode 100644
index 00000000..7fde818b
--- /dev/null
+++ b/app/views/feed/index.atom.builder
@@ -0,0 +1,16 @@
+atom_feed do |feed|
+ feed.title("Ruby Videos")
+ feed.updated(@talks.first.updated_at)
+
+ @talks.each do |talk|
+ feed.entry(talk) do |entry|
+ entry.title(talk.title)
+ entry.summary(talk.description) if talk.description.present?
+ entry.link(href: talk_url(talk.slug))
+ entry.author do |author|
+ author_names = talk.speakers.pluck(:name).join(", ")
+ author.name(author_names.present? ? author_names : "Unknown")
+ end
+ end
+ end
+end
diff --git a/app/views/feed/index.rss.builder b/app/views/feed/index.rss.builder
new file mode 100644
index 00000000..e5e3ef3e
--- /dev/null
+++ b/app/views/feed/index.rss.builder
@@ -0,0 +1,18 @@
+xml.instruct! :xml, version: "1.0"
+xml.rss version: "2.0" do
+ xml.channel do
+ xml.title "Ruby Videos"
+ xml.description "A collection of talks of Ruby conferences around the world"
+ xml.link root_url
+
+ @talks.each do |talk|
+ xml.item do
+ xml.title talk.title
+ xml.description talk.description
+ xml.pubDate DateTime.parse(talk.date.to_s).rfc822
+ xml.link talk_url(talk.slug)
+ xml.guid talk_url(talk.slug)
+ end
+ end
+ end
+end
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb
index b27d3578..c7dac1c8 100644
--- a/app/views/layouts/application.html.erb
+++ b/app/views/layouts/application.html.erb
@@ -10,6 +10,10 @@
<%= vite_client_tag %>
<%= vite_javascript_tag "application" %>
<%= vite_stylesheet_tag "application.css" %>
+
+ <%= auto_discovery_link_tag :rss, feed_url(format: :rss) %>
+ <%= auto_discovery_link_tag :atom, feed_url(format: :atom) %>
+
<%= yield :head %>
diff --git a/config/routes.rb b/config/routes.rb
index 721a7fab..387fcaf7 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -14,6 +14,9 @@
get "sign_up", to: "registrations#new"
post "sign_up", to: "registrations#create"
+ # rss feed
+ get "feed", to: "feed#index"
+
resources :sessions, only: [:index, :show, :destroy]
resource :password, only: [:edit, :update]
namespace :identity do
diff --git a/test/controllers/feed_controller_test.rb b/test/controllers/feed_controller_test.rb
new file mode 100644
index 00000000..72e17547
--- /dev/null
+++ b/test/controllers/feed_controller_test.rb
@@ -0,0 +1,29 @@
+require "test_helper"
+
+class FeedControllerTest < ActionDispatch::IntegrationTest
+ setup do
+ @talks = talks(:one, :two)
+ frozen_date_time = DateTime.new(2022, 1, 15, 10, 0, 0)
+ @talks.each do |talk|
+ talk.update!(created_at: frozen_date_time, updated_at: frozen_date_time)
+ end
+ end
+
+ test "should get rss format" do
+ get feed_url(format: :rss)
+
+ assert_response :success
+ assert_equal "/feed.rss", path
+ assert_equal "application/rss+xml; charset=utf-8", response.content_type
+ assert_equal File.read("test/fixtures/files/feed.rss"), response.parsed_body
+ end
+
+ test "should get atom format" do
+ get feed_url(format: :atom)
+
+ assert_response :success
+ assert_equal "/feed.atom", path
+ assert_equal "application/atom+xml; charset=utf-8", response.content_type
+ assert_equal File.read("test/fixtures/files/feed.atom"), response.parsed_body
+ end
+end
diff --git a/test/fixtures/files/feed.atom b/test/fixtures/files/feed.atom
new file mode 100644
index 00000000..15230e64
--- /dev/null
+++ b/test/fixtures/files/feed.atom
@@ -0,0 +1,32 @@
+
+
+ tag:www.example.com,2005:/feed
+
+
+ Ruby Videos
+ 2022-01-15T10:00:00Z
+
+ tag:www.example.com,2005:Talk/298486374
+ 2022-01-15T10:00:00Z
+ 2022-01-15T10:00:00Z
+
+ talk title 2
+ talk descritpion 2
+
+
+ Obie Fernandez
+
+
+
+ tag:www.example.com,2005:Talk/980190962
+ 2022-01-15T10:00:00Z
+ 2022-01-15T10:00:00Z
+
+ talk title 1
+ talk descritpion 1
+
+
+ Obie Fernandez
+
+
+
diff --git a/test/fixtures/files/feed.rss b/test/fixtures/files/feed.rss
new file mode 100644
index 00000000..4f8c7c40
--- /dev/null
+++ b/test/fixtures/files/feed.rss
@@ -0,0 +1,22 @@
+
+
+
+ Ruby Videos
+ A collection of talks of Ruby conferences around the world
+ http://www.example.com/
+ -
+ talk title 2
+ talk descritpion 2
+ Sat, 1 Jan 2022 00:00:00 +0000
+ http://www.example.com/talks/talk-title-2
+ http://www.example.com/talks/talk-title-2
+
+ -
+ talk title 1
+ talk descritpion 1
+ Sat, 1 Jan 2022 00:00:00 +0000
+ http://www.example.com/talks/talk-title-1
+ http://www.example.com/talks/talk-title-1
+
+
+
diff --git a/test/fixtures/talks.yml b/test/fixtures/talks.yml
index 9a0b59f1..f69b9633 100644
--- a/test/fixtures/talks.yml
+++ b/test/fixtures/talks.yml
@@ -24,9 +24,13 @@ one:
description: talk descritpion 1
slug: talk-title-1
year: 2022
+ speakers: one
+ date: 2022-01-01
two:
title: talk title 2
description: talk descritpion 2
slug: talk-title-2
year: 2022
+ speakers: one
+ date: 2022-01-01