0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Railsチュートリアル】第14章 ユーザーをフォローする③

Posted at

14.3 ステータスフィード

  • ステータスフィードを実装する

14.3.1 動機と計画

テストから書いていく。
このテストで重要なことは以下の3つ。

  1. フォローしているユーザーのマイクロポストがフィードに含まれていること(Lana)
  2. 自分自身のマイクロポストもフィードに含まれていること(Michael)
  3. フォローしていないユーザーのマイクロポストがフィードに含まれていないこと(Archer)

演習 1

マイクロポストのidが正しく並んでいると仮定して(すなわち若いidの投稿ほど古くなる前提で)、図 14.22のデータセットでuser.feed.map(&:id)を実行すると、どのような結果が表示されるでしょうか? 考えてみてください。ヒント: 13.1.4で実装したdefault_scopeを思い出してください。

user.feedを配列にして取り出す。若いidの投稿ほど古くなるので、もっとも古い投稿がトップに来る。
[1, 2, 4, 5, 7, 9, 10]

14.3.2 フィードを初めて実装する

SELECT * FROM microposts
  # micropostsからすべて(*)を取り出してください
WHERE user_id IN (<list of ids>) OR user_id = <user id>
  # 取り出す条件は
  # user_idがフォローしているユーザーidと一致している場合
  # または自分自身のユーザーidと一致している場合

(<list of ids>): フォローしているユーザーidの集合がほしい
Active Recordでfollowing_idsが用意されている。

app/models/user.rb
# ユーザーのステータスフィードを返す
def feed
  Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
    # ?には2つ目の引数「following_ids」が入る
end

演習 1

リスト 14.44において、現在のユーザー自身の投稿を含めないようにするにはどうすれば良いでしょうか? また、そのような変更を加えると、リスト 14.42のどのテストが失敗するでしょうか?

app/models/user.rb
def feed
  # Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
  Micropost.where("user_id IN (?) ", following_ids)
end
test/models/user_test.rb
# 自分自身の投稿を確認
michael.microposts.each do |post_self|
  assert michael.feed.include?(post_self)
end


FAIL["test_feed_should_have_the_right_posts", #<Minitest::Reporters::Suite:0x00007f5a9fdf4110 @name="UserTest">, 2.079927581999982]
 test_feed_should_have_the_right_posts#UserTest (2.08s)
        Expected false to be truthy.
        test/models/user_test.rb:101:in `block (2 levels) in <class:UserTest>'
        test/models/user_test.rb:100:in `block in <class:UserTest>'

演習 2

リスト 14.44において、フォローしているユーザーの投稿を含めないようにするにはどうすれば良いでしょうか? また、そのような変更を加えると、リスト 14.42のどのテストが失敗するでしょうか?

app/models/user.rb
def feed
  # Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
  Micropost.where("user_id IN (?) ", id)
end
app/models/user.rb
# フォローしているユーザーの投稿を確認
lana.microposts.each do |post_following|
  assert michael.feed.include?(post_following)
end


FAIL["test_feed_should_have_the_right_posts", #<Minitest::Reporters::Suite:0x00007f5a9e0d11f0 @name="UserTest">, 4.210437281999475]
 test_feed_should_have_the_right_posts#UserTest (4.21s)
        Expected false to be truthy.
        test/models/user_test.rb:97:in `block (2 levels) in <class:UserTest>'
        test/models/user_test.rb:96:in `block in <class:UserTest>'

演習 3

リスト 14.44において、フォローしていないユーザーの投稿を含めるためにはどうすれば良いでしょうか? また、そのような変更を加えると、リスト 14.42のどのテストが失敗するでしょうか? ヒント: 自分自身とフォローしているユーザー、そしてそれ以外という集合は、いったいどういった集合を表すのか考えてみてください。

app/models/user.rb
def feed
  # Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
  Micropost.all
end
test/models/user_test.rb
# フォローしていないユーザーの投稿を確認
archer.microposts.each do |post_unfollowed|
  assert_not michael.feed.include?(post_unfollowed)
end


FAIL["test_feed_should_have_the_right_posts", #<Minitest::Reporters::Suite:0x00007f5a9c39c3f0 @name="UserTest">, 3.210739447999913]
 test_feed_should_have_the_right_posts#UserTest (3.21s)
        Expected true to be nil or false
        test/models/user_test.rb:105:in `block (2 levels) in <class:UserTest>'
        test/models/user_test.rb:104:in `block in <class:UserTest>'

14.3.3 サブセレクト

  • フォローしているユーザー数に応じてスケールできるように、ステータスフィードを改善する。

演習 1

Homeページで表示される1ページ目のフィードに対して、統合テストを書いてみましょう。リスト 14.49はそのテンプレートです。

test/integration/following_test.rb
require 'test_helper'

class FollowingTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:michael)
    log_in_as(@user)
  end
  .
  .
  .
  test "feed on Home page" do
    get root_path
      # root_pathにGETリクエスト
    @user.feed.paginate(page: 1).each do |micropost|
      # @userのフィードのページネート1ページ目から1つずつ取り出してmicropostに代入
      assert_match CGI.escapeHTML(micropost.content), response.body
    end
  end
end

演習 2

リスト 14.49のコードでは、期待されるHTMLをCGI.escapeHTMLメソッドでエスケープしています(このメソッドは11.2.3で扱ったCGI.escapeと同じ用途です)。このコードでは、なぜHTMLをエスケープさせる必要があったのでしょうか? 考えてみてください。ヒント: 試しにエスケープ処理を外して、得られるHTMLの内容を注意深く調べてください。マイクロポストの内容が何かおかしいはずです。また、ターミナルの検索機能(Cmd-FもしくはCtrl-F)を使って「sorry」を探すと原因の究明に役立つはずです。

Expected /I'm\ sorry\.\ Your\ words\ made\ sense,\ but\ your\ sarcastic\ tone\ did\ not\./ to match " ...

記号が特殊文字になってしまう。

演習 3

リスト 14.47のコードは、いわゆるSQLのINNER JOIN、すなわちjoinメソッドを使えばRailsで直接表現できます。このテストを実行したときにリスト 14.50のコードが正しいフィードを返すことを確認してください12 。このコードではどのようなSQLクエリが生成されますか?(ヒント: User.first.feedをコンソールで実行してみましょう)


following_ids = "SELECT followed_id FROM relationships
                     WHERE follower_id = :user_id"
    Micropost.where("user_id IN (#{following_ids})
                     OR user_id = :user_id", user_id: id)


>> User.first.feed
   (4.8ms)  SELECT sqlite_version(*)

  User Load (4.3ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]

  Micropost Load (14.2ms)  SELECT "microposts".* FROM "microposts" INNER JOIN "users" ON "users"."id" = "microposts"."user_id" INNER JOIN "relationships" ON "relationships"."followed_id" = "users"."id" INNER JOIN "users" "followers_users" ON "followers_users"."id" = "relationships"."follower_id" WHERE (relationships.follower_id = 1 or microposts.user_id = 1) ORDER BY "microposts"."created_at" DESC LIMIT ?  [["LIMIT", 11]]

さいごに

ついにRailsチュートリアルを完走しました!
途中で挫折しそうになったこともありましたが、粘り強く続けて良かったです。
これまではインプットの作業なので、次からは自分でWebアプリを作成していこうと思います。
ここまでご覧いただきありがとうございました!

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?