LoginSignup
14
13

More than 5 years have passed since last update.

Rails Tutorial 第二版(Rails 4.0)の機能を拡張する1(返信機能)

Last updated at Posted at 2016-04-13

Ruby on Rails Tutorial の第二版11.4.1にある、「サンプルアプリケーションの機能を拡張する」を実践しました。今回は返信機能の拡張です。最新の第三版ではなく第二版なのは、自分の会社がRspecを用いているからです。

Ruby on Rails チュートリアル:11.4.1

仕様

あくまでも演習ということで、 あまり現実的とは言えませんが、 以下のような仕様としました。

  1. マイクロポスト入力中に@記号に続けてidとユーザー名を組み合わせたユニークなフレーズを入力すると、 そのマイクロポストは返信となる。例えば、idが1でユーザー名が"Example User"なら、@1-Example-Userとする(スペースはハイフンで置換)。
  2. 返信扱いのマイクロポストは、 マイクロポストの送信者(自分)と受信者(@1-Example-Userのように指定したユーザー)のフィードにのみ表示される。
  3. 返信先として指定できるのは、 一つのマイクロポストにつき一人のみ(@1-Example-Userのようなフレーズを複数入力しても、一つ目のユーザーにしか返信されない)。

実装

Micropostsへのカラムの追加

各マイクロポストに、 返信先のユーザーを指定するためのカラムが必要なので、追加する。"rails g migration AddInReplyToToMicropost"を実行。マイグレーションスクリプトを以下のように変更し、 "rake db:migrate"。

class AddInReplyToToMicroposts < ActiveRecord::Migration
  def change
    add_column :microposts, :in_reply_to, :string, default: ""
  end
end

カラム名は"in_reply_to"、defaultは""とした。

in_reply_toカラムへの保存

Micropostモデルにbefore_saveを設定し、 Micropostのcontentから返信先ユーザーを表すフレーズを抽出し、 in_reply_toカラムに入力する。

app/model/micropost.rb
class Micropost < ActiveRecord::Base
  
  
  before_save { in_reply_to = reply_user.to_s }
  
  
  
  def reply_user
    if user_unique_name = content.match(/(@[^\s]+)\s.*/)
      user_unique_name[1]
    end
  end
end

scopeの追加

任意のマイクロポストの"in_reply_to"カラムに返信先ユーザーを表すフレーズ(@1-Example-Userの形式)が入っている場合、 そのマイクロポストは返信先ユーザーおよび送信者(自分)からしか見えないようにする必要がある。例えば、@1-Example-Userというフレーズがcontentに入っていたら、 そのマイクロポストはid: 1、 name: "Example User"の属性を持つユーザーに対する返信となり、 自分と返信先ユーザーのフィードにのみ表示される。この機能を実現するため、 Micropostモデルに以下のようなscopeを追加する。

app/model/micropost.rb
class Micropost < ActiveRecord::Base
  
  
  scope :including_replies, ->(user){where("in_reply_to = ? OR in_reply_to = ? OR user_id = ?", "", "@#{user.id}\-#{user.name.sub(/\s/,'-')}", user.id)}

  
  
end

scopeによるフィードの絞り込み

上で実装したscopeをstatic_pages_controllerで使用する。それに伴って、 User.feedメソッドも以下のように変更。

app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
  def home
    if signed_in?
        @micropost = current_user.microposts.build

      # 元のコード
      # @feed_items = current_user.feed.paginate(page: params[:page])

      # 変更後のコード
      @feed_items = current_user.feed(current_user).paginate(page: params[:page])
      end
  end
  
  
  
  
end
app/model/user.rb

class User < ActiveRecord::Base
  
  
  
  
  
  def feed(user)
    Micropost.including_replies(user).from_users_followed_by(self)
  end
  
  
  
end

これで、簡易的な返信機能が実装できました。

おまけ

以下、 RSpecでの簡単なテストです。

spec/requests/static_pages_spec.rb

describe "Static pages" do

  subject { page }
  describe "Home page" do
  
  
  
    describe 'reply micropost' do
      # 送信者としてのユーザーを作成
      let(:user){FactoryGirl.create(:user)}
      # 返信先のユーザーを作成
      let(:reply_user){FactoryGirl.create(:user)}
      # 第三者のユーザーを作成
      let(:other_user){FactoryGirl.create(:user)}
      before do
        # 送信者から、返信先ユーザーに向けたマイクロポストを投稿
        FactoryGirl.create(:micropost, user: user, content: "@#{reply_user.id}\-#{reply_user.name.strip.sub(/\s/,'-')}\nhello world")
        reply_user.follow!(user)
      end
      #送信者からはマイクロポストがフィードに表示される
      describe 'at user' do
        before do
          sign_in user
          visit root_path
        end
        it { should have_content("hello world") }
      end
      #送信先ユーザーにもマイクロポストがフィードに表示される
      describe 'at reply_user' do
        before do
          sign_in reply_user
          visit root_path
        end
        it { should have_content("hello world") }
      end
      #第三者ユーザーにはマイクロポストがフィードに表示されない 
      describe 'at other_user' do
        before do
          sign_in other_user
          visit root_path
        end
        it { should_not have_content("hello world") }
      end
    end
    
    
    
14
13
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
13