Rails Tutorial Extension: I tried to create an RSS feed function

This is a continuation of Creating Extensions in Chapter 14 of the Rails Tutorial.

This time we will create an RSS feed function.

Confirmation of requirements

In the tutorial

Implement the ability to RSS feed microposts for each user. Next, implement the ability to RSS feed the status feed, and if you can afford it, try adding an authentication scheme to the feed to restrict access.

The requirements are written.

I've used RSS, but I'm not sure how it works. I will check on the net if there is a person who made it.

There seems to be a builder function that comes standard with feed.rss.builder.

Next is the authentication scheme in the feed, but I understood that it means that you can not follow unless you are logged on. Check online to see if there are any examples.

Check online to see if Twitter is authenticated. Twitter itself does not deliver RSS, and it seems that it is getting RSS from another site. Facebook used to provide RSS as an official function, but it has been discontinued.

Reference: How to get RSS on a certified site http: // username: password @ RSS feed URL

I found that it can be done with a fairly old net article, but it seems that there is a high risk of password leakage if it is described in the URL.

The requirements are summarized from the results of the investigation.

    1. Ability to output your own post as an RSS feed
    1. Add icons and links to subscribe to RSS feeds to your home
    1. A function to output the status feed, that is, the post including the followers as an RSS feed
  1. It seems difficult to authenticate RSS, so I will not do it this time

Design specifications

We will reduce the requirements to specific functions.

  1. Ability to output your own post as an RSS feed Accessing xxx/rss in the url returns an RSS file.

    1. Add icons and links to subscribe to RSS feeds to your home Add a link to your home.
    1. A function to output the status feed, that is, the post including the followers as an RSS feed Accessing xxx/rss in the url returns an RSS file.

RSS output of your own post: Add controller

Create a topic branch.

ubuntu:~/environment/sample_app (master) $ git checkout -b rss

http://miner.hatenablog.com/entry/2017/07/20/142532  I will refer to.

Add the routing.

config/routes.rb


  get :rss, to: 'rss#index', defaults: { format: :rss }

Generate a controller.

ubuntu:~/environment/sample_app (rss) $ rails generate controller Rsss
      create  app/controllers/rsss_controller.rb
      create    app/views/rsss
      create    test/controllers/rsss_controller_test.rb
      create    app/helpers/rsss_helper.rb
      create      app/assets/javascripts/rsss.coffee
      create      app/assets/stylesheets/rsss.scss

Add to the controller by referring to the user's show.

app/controllers/rsss_controller.rb


class RsssController < ApplicationController

  layout false
  def index
    @user = User.find(params[:id])
    @microposts = @user.microposts.limit(10) 
    
    respond_to do |format|
      format.rss
      format.atom
    end
  end
end

Create a view

Create a view.

Create an app/views/rsss/index.rss.builder file.

app/views/rsss/index.rss.builder


cache 'feed_cache_key', expires_in: 30.minutes do
  xml.instruct! :xml, :version => "1.0"
  xml.rss :version => "2.0" do
    xml.channel do
      xml.title "Rails Tutorial sample site"
      xml.description "Rails Tutorial sample site"
      xml.link root_url

      @microposts.each do |b|
        xml.item do
          xml.title b.title
          xml.description strip_tags(b.rich_text_body.to_s.gsub(/\r\n|\r|\n|\s|\t/, "")).truncate(120)
          xml.image image_url(url_for(b.image))
          xml.pubDate b.time.to_s(:rfc822)
          xml.link blog_url(b)
          xml.guid blog_url(b)
        end
      end
    end
  end
end

https://pgmg-rails.com/blogs/24  I will refer to.

The cloud9 syntax check is no longer colored. I didn't know if it was an extension or not.

ruby:app/views/rsss/index.rss.builder


         xml.title b.content
          xml.description b.content
          xml.image image_url(url_for(b.picture)) if b.picture?
          xml.pubDate b.updated_at.to_s(:rfc822)

Check if RSS is output

Let's check if RSS is output.

On the rails server, go to/rss. https://XXXXXXXXXXXXXXX.amazonaws.com/rss

An error was output.

ActionController::RoutingError (uninitialized constant RssController):

Check routes.

buntu:~/environment/sample_app (rss) $ rails routes
                 Prefix Verb   URI Pattern                             Controller#Action
                   root GET    /                                       static_pages#home
...
                    rss GET    /rss(.:format)                          rss#index {:format=>:rss}

I wondered if the problem was that the plural form and the file name did not match. The file name static_pages_controller.erb corresponds to static_pages # home in routes. So I thought that the file name rss_controller.erb corresponds to rss # index. If so, routes should be rsss # index, so fix it.

config/routes.rb


  get :rss, to: 'rsss#index', defaults: { format: :rss }

A different error was output.

ActiveRecord::RecordNotFound (Couldn't find User with 'id'=):
app/controllers/rsss_controller.rb:5:in `index'

The previous error was resolved and I proceeded to the controller. Because you are not logged in, you do not have a user id.

I tried to log in and then access it, but the same error occurred.

The cause is why the id cannot be obtained from the parameter Since you are not logged in when you get RSS in the first place, refer to user # show.

GET    /users/:id(.:format)  users#show

It has become. When you access xxx/users/1 with url, 1 is passed for id. I thought it would be good to add /: id to routes, so I'll fix it.

Find out how to write routes on the net. https://www.sejuku.net/blog/13078

config/routes.rb


  get 'rss/:id', to: 'rsss#index', defaults: { format: :rss }
ubuntu:~/environment/sample_app (rss) $ rails routes
                 Prefix Verb   URI Pattern                             Controller#Action
                   root GET    /                                       static_pages#home

                        GET    /rss/:id(.:format)                      rsss#index {:format=>:rss}

I got a different error.

ActionView::Template::Error (undefined method `blog_url' for #<#<Class:0x000056130c6fb330>:0x000056130c6f9648>
Did you mean?  login_url):

I found that the article I referred to as an example does not work as it is. Find out what should be displayed in the link and guid.

          xml.link blog_url(b)
          xml.guid blog_url(b)

The article I referred to was an example of a blog, and I specified the URL of each article. Correspondingly, there is no URL for each micropost, so I will give the URL of the user. The difference between path and URL was ambiguous, so I checked it online. https://qiita.com/higeaaa/items/df8feaa5b6f12e13fb6f

ruby:app/views/rsss/index.rss.builder


          xml.link user_url(b.user)
          xml.guid user_url(b.user)

When I modified it and accessed the url, a dialog to open or save was displayed.

rss.1.png

When I opened the saved file with Notepad, xml was output.

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Rails Tutorial sample site</title>
    <description>Rails Tutorial sample site</description>
    <link>https://xxxxxxxxxxxxxxxxxxxxx.amazonaws.com/</link>
    <item>
      <title>@Elvis1 reply test</title>
      <description>@Elvis1 reply test</description>
      <pubDate>Wed, 11 Nov 2020 23:27:05 +0000</pubDate>
      <link>https://xxxxxxxxxxxxxxxxxxxxx.amazonaws.com/users/1</link>
      <guid>https://xxxxxxxxxxxxxxxxxxxxx.amazonaws.com/users/1</guid>
    </item>
    <item>
      <title>Est et placeat voluptates et alias.</title>
      <description>Est et placeat voluptates et alias.</description>
      <pubDate>Wed, 11 Nov 2020 23:27:04 +0000</pubDate>
      <link>https://xxxxxxxxxxxxxxxxxxxxx.amazonaws.com/users/1</link>
      <guid>https://xxxxxxxxxxxxxxxxxxxxx.amazonaws.com/users/1</guid>
    </item>
..
    <item>
      <title>Sint porro molestiae corporis quidem eligendi.</title>
      <description>Sint porro molestiae corporis quidem eligendi.</description>
      <pubDate>Wed, 11 Nov 2020 23:27:03 +0000</pubDate>
      <link>https://xxxxxxxxxxxxxxxxxxxxx.amazonaws.com/users/1</link>
      <guid>https://xxxxxxxxxxxxxxxxxxxxx.amazonaws.com/users/1</guid>
    </item>
  </channel>
</rss>

I tried adding this URL to Feedly, but it wasn't recognized as RSS.

We were not able to find an RSS feed for https://xxxxxxx.amazonaws.com/rss/1.

Please contact the website and ask them if they offer a valid RSS feed.

I searched online to see if it was an RSS problem, but I wasn't sure.

https://qiita.com/amymd/items/bb4a9e008af061c89ea9

https://pgmg-rails.com/blogs/24 Let's change how to write the controller with reference to.

app/controllers/rsss_controller.rb


    respond_to do |format|
      format.html
      format.rss { render :layout => false }
    end

The behavior of the file open dialog did not change. I looked it up on the net, but I didn't understand it, so I'll leave it as it is.

Changed to output status feed

Now that I'm outputting what I posted, I'll change the status feed, that is, the post that includes the people I follow, to the ability to output as an RSS feed.

Refer to the place where the status feed is displayed on the screen. Since it was the home screen, I will add a static_pages controller.

app/controllers/static_pages_controller.rb


@feed_items = current_user.feed.paginate(page: params[:page])

And you can see that you can output it with the feed method. When I tried it on the console, it did return a micropost. Change the controller.

app/controllers/rsss_controller.rb


class RsssController < ApplicationController
  def index
    @user = User.find(params[:id])
    @microposts = @user.feed.limit(10) 
  
    respond_to do |format|
      format.html
      format.rss { render :layout => false }
    end
  end
end

Let's run it on rails server. A file will be output, so check the contents.

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>Rails Tutorial sample site</title>
    <description>Rails Tutorial sample site</description>
    <link>https://https://xxxxxxxxxxxxxxxxxxxxx.amazonaws.com.amazonaws.com/</link>
    <item>
      <title>@Elvis1 reply test</title>
      <description>@Elvis1 reply test</description>
      <pubDate>Wed, 11 Nov 2020 23:27:05 +0000</pubDate>
      <link>https://https://xxxxxxxxxxxxxxxxxxxxx.amazonaws.com.amazonaws.com/users/1</link>
      <guid>https://https://xxxxxxxxxxxxxxxxxxxxx.amazonaws.com.amazonaws.com/users/1</guid>
    </item>
    <item>
      <title>Est et placeat voluptates et alias.</title>
      <description>Est et placeat voluptates et alias.</description>
      <pubDate>Wed, 11 Nov 2020 23:27:04 +0000</pubDate>
      <link>https://https://xxxxxxxxxxxxxxxxxxxxx.amazonaws.com.amazonaws.com/users/6</link>
      <guid>https://https://xxxxxxxxxxxxxxxxxxxxx.amazonaws.com.amazonaws.com/users/6</guid>
    </item>

  </channel>
</rss>

When I compared it with the home screen, the second post was posted by another user and matched.

rss.2.png

It turns out that RSS doesn't tell who posted the micropost. I will leave it as it is this time and leave it as an improvement point in the future.

Create test

Now that it works, I'll write a test. I didn't know how to handle the RSS output result, so I searched online but couldn't find it. So I will write it in the same way as the screen.

test/integration/rss_test.rb


require 'test_helper'

class RssTest < ActionDispatch::IntegrationTest
  
  def setup
    @user = users(:michael)
  end
  
  test "rss show" do
    get rss_path(@user)
    assert_template 'rsss/index'
  end

end

I got an error.

NoMethodError:         NoMethodError: undefined method `rss_path' for #<RssTest:0x000055fc3d47d0f8>

ubuntu:~/environment/sample_app (rss) $ rails routes
                 Prefix Verb   URI Pattern                             Controller#Action
                 new_dm GET    /dms/new(.:format)                      dms#new
                     dm DELETE /dms/:id(.:format)                      dms#destroy
                        GET    /rss/:id(.:format)                      rsss#index {:format=>:rss}

In the routing of rss_path, there is no name in Prefix and there is no space, so let's give it a name. https://www.sejuku.net/blog/13078  I will refer to.

config/routes.rb


get 'rss/:id', to: 'rsss#index', as: 'rss', defaults: { format: :rss } 
ubuntu:~/environment/sample_app (rss) $ rails routes
                 Prefix Verb   URI Pattern                             Controller#Action
                     dm DELETE /dms/:id(.:format)                      dms#destroy
                    rss GET    /rss/:id(.:format)                      rsss#index {:format=>:rss}

It became GREEN.

Add to check if the content is displayed with assert_match. When I ran the test, it was GREEN. This completes it.

test/integration/rss_test.rb


  test "rss show" do
    get rss_path(@user)
    assert_template 'rsss/index'
    @user.feed.limit(10).each do |micropost|
      assert_match micropost.content, response.body
    end
  end

I will upload it to heroku as usual.

ubuntu:~/environment/sample_app (rss) $ git add -A
ubuntu:~/environment/sample_app (rss) $ git commit -m "Add RSS"       
ubuntu:~/environment/sample_app (rss) $ git checkout master
ubuntu:~/environment/sample_app (master) $ git merge rss
ubuntu:~/environment/sample_app (master) $ git push
ubuntu:~/environment/sample_app (master) $ git push heroku

I tried to access the RSS URL on Heroku. The behavior of the file open dialog was unchanged.

Time required

It is 8.0 hours of 13 working days from 12/21 to 1/11. It took me a while to look it up online. It takes less than half the time to write and test.

Summary of referenced articles

http://miner.hatenablog.com/entry/2017/07/20/142532

https://pgmg-rails.com/blogs/24

https://qiita.com/klriutsa/items/6662ef75e804c4323228

https://madogiwa0124.hatenablog.com/entry/2019/02/24/234610

http://maepachi.com/entries/94

Recommended Posts

Rails Tutorial Extension: I tried to create an RSS feed function
I tried to make a message function of Rails Tutorial extension (Part 1): Create a model
I tried to make a message function of Rails Tutorial extension (Part 2): Create a screen to display
I tried to make a reply function of Rails Tutorial extension (Part 3): Corrected a misunderstanding of specifications
[Ruby on Rails] Since I finished all Rails Tutorial, I tried to implement an additional "stock function"
Rails6 I tried to introduce Docker to an existing application
Rails Tutorial Extension: I created a follower notification function
[Rails] I tried to create a mini app with FullCalendar
[Rails] I tried to implement "Like function" using rails and js
I tried to implement Ajax processing of like function in Rails
I made a reply function for the Rails Tutorial extension (Part 1)
I tried to implement the image preview function with Rails / jQuery
I made a reply function for the Rails Tutorial extension (Part 5):
I tried to make a group function (bulletin board) with Rails
I tried to introduce CircleCI 2.0 to Rails app
I made a reply function for Rails Tutorial extension (Part 2): Change model
[Rails] I tried to raise the Rails version from 5.0 to 5.2
I tried to organize the session in Rails
I tried to create a LINE clone app
Rails6.0 ~ How to create an eco-friendly development environment
I tried to develop an application in 2 languages
I tried to create Alexa skill in Java
Create an EC site with Rails 5 ⑩ ~ Create an order function ~
[First environment construction] I tried to create a Rails 6 + MySQL 8.0 + Docker environment on Windows 10.
I tried to create a shopping site administrator function / screen with Java and Spring
[Rails] How to connect to an external API using HTTP Client (I tried connecting to Qiita API)
Create an EC site with Rails 5 ⑨ ~ Create a cart function ~
I tried to create a Clova skill in Java
I tried to make a login function in Java
I want to define a function in Rails Console
[Rails] Create an application
I can't deploy! Resolve an error that can't be pushed to heroku (Rails Tutorial Chapter 1)
I tried to build an environment using Docker (beginner)
A new employee tried to create an authentication / authorization function from scratch with Spring Security
[Rails 6.0, Docker] I tried to summarize the Docker environment construction and commands necessary to create a portfolio
I made a reply function for the Rails Tutorial extension (Part 4): A function that makes the user unique
I tried to make an application in 3 months from inexperienced
I tried to implement the like function by asynchronous communication
I tried to make an introduction to PHP + MySQL with Docker
I tried to create a java8 development environment with Chocolatey
I tried to introduce Bootstrap 4 to the Rails 6 app [for beginners]
Tutorial to create a blog with Rails for beginners Part 1
I want to push an app made with Rails 6 to GitHub
I tried to make Venn diagram an easy-to-understand GIF animation
[Swift] I tried to implement the function of the vending machine
I tried using Hotwire to make Rails 6.1 scaffold a SPA
I want to create a form to select the [Rails] category
[Rails] I tried to implement batch processing with Rake task
Tutorial to create a blog with Rails for beginners Part 2
I tried to create a padrino development environment with Docker
Tutorial to create a blog with Rails for beginners Part 0
How to create an application
I tried Rails beginner [Chapter 1]
I tried the Docker tutorial!
I tried the VueJS tutorial!
I tried Rails beginner [Chapter 2]
[Rails] Implementation of tutorial function
I tried to verify yum-cron
[Rails] Implementation of multi-layer category function using ancestry "I tried to make a window with Bootstrap 3"
I tried to make the "Select File" button of the sample application created in the Rails tutorial cool
After learning Progate, I tried to make an SNS application using Rails in the local environment