地方でリモートワーク in Iwate

東京の受託開発会社でリモートワークしてます。

RailsでRSSリーダーを作るチュートリアル

スポンサーリンク

RailsでRSSリーダーを作るチュートリアルです。

Railsで簡易RSSリーダーを作ってみました。 参考サイトはこれです。

Building an RSS Reader in Rails Is Easy

プロジェクト開始

rails new feedreader

gem

使うgemはfeedjiraです。ゴジラとフィードをあわせたみたいです。

github.com

rubygemsを見てみてもrssパーサーで一番人気があるみたいです。

https://www.ruby-toolbox.com/categories/feed_parsing

f:id:ihatov08:20170104155139p:plain

実際のプロダクトのgemでも使われているみたいです!これは信頼できる。

# Gemfile
gem 'feedjira'
gem 'twitter-bootstrap-rails'

bootstrap導入

今回見た目をちょっとよくするためにcssフレームワークのbootstrapを使います。

rails generate bootstrap:install static

モデル作成

feedモデルとテーブルを作成します。 これにrssのurlなどを入れていきます。

rails g scaffold feed name url description:text
rake db:migrate

rootをfeedのindexにしましょう。

# config/routes.rb
root 'feeds#index

更新されていくentryモデルを作成します。 これはfeedに連なる形になりますね。

rails g model entry title published:datetime content:text url author feed_id:integer
rake db:migrate

アソシエーションを定義していきます。

## models/feed.rb
class Feed < ActiveRecord::Base
    has_many :entries, dependent: :destroy
end

## models/entry.rb
class Entry < ActiveRecord::Base
    belongs_to :feed
end

RSSを引っ張ってくるためのrakeタスク

RSSは自分から見に行かないと取得できません。そのため、rakeタスクにして、cronなどで定期的に取得する必要があります。 実際はwheneverとかgemを使って定期的にrakeタスクを、実行してあげればいいと思います。

# lib/tasks/sync.rake
namespace :sync do
  task feeds: [:environment] do
    Feed.all.each do |feed|
      content = Feedjira::Feed.fetch_and_parse feed.url
      content.entries.each do |entry|
        local_entry = feed.entries.where(title: entry.title).first_or_initialize
        local_entry.update_attributes(content: entry.content, author: entry.author, url: entry.url, published: entry.published)
        p "Synced Entry - #{entry.title}"
      end
      p "Synced Feed - #{feed.name}"
    end
  end
end

実行するには

rake sync:feeds

と入力します。

適当なサイトのrssのurlを登録して、タスクを走らせて見て下さい。 配信されている記事数とコンソールの結果が一緒になっていればOKです!

rails c
Entry.count

何回もrakeタスク走らせて記事被らないの?

xoyip.hatenablog.com

この記事でも言及されているのですが、rssフィードを取得する際に、更新頻度とタイミングによっては、記事がかぶってしまいます。 そこのところの処理どうするの?って思っていましたが、このrakeタスクはfirst_or_initializeでテーブルに同じ記事があるかどうか探して、なければ作成、あれば呼び出しという処理をしています。そのため記事が重複してレコードが作成されることはないし、記事が更新されていればupdateしてくれます。 こうやって処理を書くんだと参考になりました。

controllerとview

簡易的なviewを作成していきます。

rails g controller entries index show
# config/routes.rb
resources :feeds do
  member do
   resources :entries, only: [:index, :show]
  end
end
# app/controllers/entries_controller.rb
class EntriesController < ApplicationController
  before_action :set_feed, only: :index

  def index
    @entries = @feed.entries.order('published desc')
  end

  def show
    @entry = Entry.find(params[:id])
  end

  private
  def set_feed
    @feed = Feed.find(params[:id])
  end
end
# app/views/entries/index.html.erb
<div class="container">
  <% @entries.each do |entry| %>
    <div class="panel panel-default">
      <div class="panel-body">
        <%= link_to entry.title, entry %> - <i> published <%= time_ago_in_words(entry.published) %> ago.</i>
      </div>
    </div>
  <% end %>
</div>

# app/views/entries/show.html.erb
<div class="container">
  <h3><%= link_to @entry.title, @entry.url %></h3>
  <i>published on <%= @entry.published %> by <%= @entry.author %></i>
  <p>
    <%= @entry.content.html_safe %>
  </p>
</div>

http://localhost:3000/feeds/1

アクセスすると下記のように表示されるはずです。

f:id:ihatov08:20170104160604p:plain

rssの機能自体はかなり簡単に作れますね。