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

# 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 フィードを初めて実装する

```ruby
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`が用意されている。

```ruby: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のどのテストが失敗するでしょうか?

```ruby: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
```

```ruby: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のどのテストが失敗するでしょうか?

```ruby:app/models/user.rb
def feed
  # Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
  Micropost.where("user_id IN (?) ", id)
end
```

```ruby: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のどのテストが失敗するでしょうか? ヒント: 自分自身とフォローしているユーザー、そしてそれ以外という集合は、いったいどういった集合を表すのか考えてみてください。

```ruby:app/models/user.rb
def feed
  # Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
  Micropost.all
end
```

```ruby: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はそのテンプレートです。

```ruby: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」を探すと原因の究明に役立つはずです。

```ruby
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をコンソールで実行してみましょう）

```ruby

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アプリを作成していこうと思います。
ここまでご覧いただきありがとうございました！