Rails チュートリアル14章まとめ

#この章でやること

- ユーザーをフォローする、される部分を実装する
- Ajaxも紹介
- この章は最も難易度が高い


ページ操作の全体的な流れは

1. あるユーザー（John Calvin）は自分のプロフィールページを最初に表示し、
2. フォローするユーザーを選択するためにUsersページ（図 14.2）に移動
3. Calvinは2番目のユーザーThomas Hobbesを表示し、［Follow］ボタンを押してフォローする
４. これにより、［Follow］ボタンが［Unfollow］に変わり、Hobbes の［followers］カウントが1人増える。
5. CalvinがHomeページに戻ると、［following］カウントが1人増え、Hobbesのマイクロポストがステータスフィードに表示される

##14.1 Relationshipモデル
ユーザーをフォローする機能を実装するためにまず、データモデルを構成する。
ただし簡単ではない。
素朴に考えれば、has_many（1対多）の関連付けを用いて「1人のユーザーが複数のユーザーをhas_manyとしてフォローし、1人のユーザーに複数のフォロワーがいることをhas_manyで表す」といった方法でも実装できそう。
しかし後ほどこの方法ではたちまち壁に突き当たる
これを解決するためのhas_many throughについてもこの後で説明

新しいトピックブランチを作成

```
$ git checkout -b following-users
```

###14.1.1 データモデルの問題（および解決策）
ユーザーをフォローするデータモデル構成のための第一歩として、典型的な場合を検討してみる。
あるユーザーが、別のユーザーをフォローしているところを考えてみる。

具体例を挙げると、CalvinはHobbesをフォローしている。逆から見れば、HobbesはCalvinからフォローされている。
CalvinはHobbesから見ればフォロワー（follower）であり、CalvinがHobbesをフォローした（followed）ことになる

Railsにおけるデフォルトの複数形の慣習に従えば、**ユーザーをフォローしているすべてのユーザーの集合はfollowers**となり、`user.followers`はそれらのユーザーの配列を表すことになる。

しかし残念なことに、この名前付けは逆向きではうまくいかない。
あるユーザーがフォローしているすべてのユーザーの集合は、このままではfollowedsとなってしまう
英語の文法からも外れるうえに非常に見苦しい
そこで、Twitterの慣習にならい、ではfollowingという呼称を採用（例: “50 following, 75 followers”）。

followersがフォロワー人数で、followingがフォロー人数を表すデータの表となる。


followingテーブルとhas_many関連付けを使って、フオローしているユーザーのモデリングができる。
user.followingはユーザーの集合でなければならないため、followingテーブルのそれぞれの行は、followed_idで識別可能なユーザーでなければならない。

さらに、それぞれの行はユーザーなので、これらのユーザーに名前(name)やパスワード(password)などの属性を追加する。

![図](https://railstutorial.jp/chapters/6.0/images/figures/naive_user_has_many_following.png)

フォローしているユーザーの素朴な実装例

**このデータモデルの問題点は、非常に無駄が多い。**
各行には、フォローしているユーザーのidのみならず、名前やメールアドレスまであり、これらはいずれもusersテーブルに既にあるものばかりだから。
さらによくないことに、followersの方をモデリングするときにも、同じぐらい無駄の多いfollowersテーブルを別に作成しなければならなくなってしまう。
結論このデータモデルはメンテナンスの観点から見て最悪で、ユーザー名を変更するたびに、usersテーブルのそのレコードだけでなく、followingテーブルとfollowersテーブルの両方について、そのユーザーを含むすべての行を更新しなければならなくなるため。

この問題の根本は、必要な抽象化を行なっていないことにある。
正しいモデルを見つけ出す方法の1つは、Webアプリケーションにおけるfollowingの動作をどのように実装するかをじっくり考えること

RESTアーキテクチャは、作成されたり削除されたりするリソースに関連していたことを思い出すと、2つの疑問が生じる

1. あるユーザーが別のユーザーをフォローするとき、何が作成されるのか。 
2. あるユーザーが別のユーザーをフォロー解除するとき、何が削除されるのか。

この点を踏まえて考えると、この場合アプリケーションによって作成または削除されるのは、つまるところ2人のユーザーの「関係（リレーションシップ）」であることがわかる。
つまり、1人のユーザーは1対多の関係を持つことができ、さらにユーザーはリレーションシップを経由して多くのfollowing（またはfollowers）と関係を持つことができるということ

他にも解決しなくてはいけない問題がある。
Facebookのような友好関係（Friendships）では本質的に左右対称のデータモデルが成り立つが、Twitterのようなフォロー関係では左右非対称の性質がある。
すなわち、CalvinはHobbesをフォローしていても、HobbesはCalvinをフォローしていないといった関係性が成り立つ。
このような左右非対称な関係性を見分けるために、それぞれを*能動的関係（Active Relationship）*と*受動的関係（Passive Relationship）*と呼ぶ。
例えば先ほどの事例のような、CalvinがHobbesをフォローしているが、HobbesはCalvinをフォローしていない場合では、CalvinはHobbesに対して「能動的関係」を持っていることになる。
逆に、HobbesはCalvinに対して「受動的関係」を持っていることになる 。

まずは、フォローしているユーザーを生成するために、**能動的関係**に焦点を当てていく
フォローしているユーザーはfollowed_idがあれば識別することができるので、先ほどのfollowingテーブルをactive_relationshipsテーブルと見立ててみる。
ただしユーザー情報は無駄なので、ユーザーid以外の情報は削除。そして、followed_idを通して、usersテーブルのフォローされているユーザーを見つけるようにする

![能動的関係をとおしてフォローしているユーザーを取得する模式図](https://railstutorial.jp/chapters/6.0/images/figures/user_has_many_following_3rd_edition.png)

能動的関係をとおしてフォローしているユーザーを取得する模式図

能動的関係も受動的関係も、最終的にはデータベースの同じテーブルを使うことになる。
したがって、テーブル名にはこの「関係」を表す「relationships」を使う。
モデル名はRailsの慣習にならって、Relationshipとする。

![Relationshipデータモデル](https://railstutorial.jp/chapters/6.0/images/figures/relationship_model.png)

Relationshipデータモデル

このデータモデルを実装するために、まずはマイグレーションを生成

```
$ rails generate model Relationship follower_id:integer followed_id:integer
```

このリレーションシップは今後follower_idとfollowed_idで頻繁に検索することになるため、それぞれのカラムにインデックスを追加

```db/migrate/[timestamp]_create_relationships.rb
class CreateRelationships < ActiveRecord::Migration[6.0]
  def change
    create_table :relationships do |t|
      t.integer :follower_id
      t.integer :followed_id

      t.timestamps
    end
    add_index :relationships, :follower_id#index追加
    add_index :relationships, :followed_id
    add_index :relationships, [:follower_id, :followed_id], unique: true#複合キーインデックス
  end
end
```

複合キーインデックスという行もあることに注目。
これは、follower_idとfollowed_idの組み合わせが必ずユニークであることを保証する仕組み
これにより、**あるユーザーが同じユーザーを2回以上フォローすることを防ぐ**
このような重複（2回以上フォローすること）が起きないよう、インターフェイス側の実装でも注意を払うが、ユーザーが何らかの方法で（例えばcurlなどのコマンドラインツールを使って）Relationshipのデータを操作するようなことも起こり得る。
しかし一意なインデックスを追加していれば、エラーを発生させて重複を防ぐことができる。

```
$ rails db:migrate
```

###演習
1 id=1のユーザーに対してuser.following.map(&:id)を実行すると、結果はどのようになるでしょうか? 想像してみてください。ヒント: 4.3.2で紹介したmap(&:method_name)のパターンを思い出してください。例えばuser.following.map(&:id)の場合、idの配列を返します。
→フォローしているユーザーのidを返す

2 id=2のユーザーに対してuser.followingを実行すると、結果はどのようになるでしょうか? また、同じユーザーに対してuser.following.map(&:id)を実行すると、結果はどのようになるでしょうか? 想像してみてください。
→フォローしているユーザーの詳細を返す、同じくフォローしているユーザーidが一覧で返る

###14.1.2 User/Relationshipの関連付け
フォローしているユーザーとフォロワーを実装する前に、UserとRelationshipの関連付けを行う。
1人のユーザーにはhas_many（1対多）のリレーションシップがあり、このリレーションシップは2人のユーザーの間の関係なので、フォローしているユーザーとフォロワーの両方に属す（belongs_to）。

次のようなユーザー関連付けのコードを使って新しいリレーションシップを作成

```
user.active_relationships.build(followed_id: ...)
```

この時点ではUser/Micropostの関連付けのモデルのようにはならない。

1つ目の違い
以前、ユーザーとマイクロポストの関連付けをしたときは、次のように書いた

```
class User < ApplicationRecord
  has_many :microposts
  .
end
```

引数の:micropostsシンボルから、Railsはこれに対応するMicropostモデルを探し出し、見つけることができた

同じように今回も記載すると

```
has_many :active_relationships
```

となってしまい、（ActiveRelationshipモデルを探してしまい）Relationshipモデルを見つけることができない。
このため、今回のケースでは、Railsに探して欲しいモデルのクラス名を明示的に伝える必要がある。

2つ目の違いは、先ほどの逆のケースについて
以前はMicropostモデルで、

```
class Micropost < ApplicationRecord
  belongs_to :user
  .
end
```

このように書いた。
micropostsテーブルにはuser_id属性があるので、これを辿って対応する所有者（ユーザー）を特定することができた。
データベースの2つのテーブルを繋ぐとき、このようなidは外部キー（foreign key）と呼び、Userモデルに繋げる外部キーが、Micropostモデルのuser_id属性ということになる。
この外部キーの名前を使って、Railsは関連付けの推測をしている。
具体的には、Railsはデフォルトでは外部キーの名前を<class>_idといったパターンとして理解し、 <class>に当たる部分からクラス名（正確には小文字に変換されたクラス名）を推測するようになっている。

今回のケースではフォローしているユーザーをfollower_idという外部キーを使って特定しなくてはならない。
また、followerというクラス名は存在しないので、ここでもRailsに正しいクラス名を伝える必要が発生する

1点目、2点目の説明をコードにまとめると、UserとRelationshipの関連付けは以下のようになる

```app/models/user.rb
class User < ApplicationRecord
  has_many :microposts, dependent: :destroy
  has_many :active_relationships, class_name:  "Relationship",
                                  foreign_key: "follower_id",
                                  dependent:   :destroy
  .
  .
  .
end
```

（ユーザーを削除したら、ユーザーのリレーションシップも同時に削除される必要がある
そのため、関連付けにdependent: :destroyも追加）

```app/models/relationship.rb
class Relationship < ApplicationRecord
  belongs_to :follower, class_name: "User"
  belongs_to :followed, class_name: "User"
end
```

この定義した関連付けにより多くのメソッドが使えるようになった。
今回使えるようになったメソッドを示す。

|メソッド	|用途|
|:--|:--|
|active_relationship.follower	|フォロワーを返す
|active_relationship.followed	|フォローしているユーザーを返す
|user.active_relationships.create(followed_id: other_user.id)	|userと紐付けて能動的関係を作成/登録する
|user.active_relationships.create!(followed_id: other_user.id)	|userを紐付けて能動的関係を作成/登録する（失敗時にエラーを出力）
|user.active_relationships.build(followed_id: other_user.id)	|userと紐付けた新しいRelationshipオブジェクトを返す

###演習
1 コンソールを開き、表 14.1のcreateメソッドを使ってActiveRelationshipを作ってみましょう。データベース上に２人以上のユーザーを用意し、最初のユーザーが２人目のユーザーをフォローしている状態を作ってみてください。

```Ruby
irb(main):003:0> user=User.first
=> #<User id: 1, name: "Example User", email: "example@railstutorial.org", create...
irb(main):004:0> user_second = User.second
=> #<User id: 2, name: "Eugenio O'Reilly", email: "example-1@railstutorial.org", ...
irb(main):005:0> user.active_relationships.create(followed_id: user_second.id)
=> #<Relationship id: 92, follower_id: 1, followed_id: 2, created_at: "2021-03-11 01:59:04", updated_at: "2021-03-11 01:59:04">
```

2 先ほどの演習を終えたら、active_relationship.followedの値とactive_relationship.followerの値を確認し、それぞれの値が正しいことを確認してみましょう。

```
#<Relationship id: 92, follower_id: 1, followed_id: 2, created_at: "2021-03-11 01:59:04", updated_at: "2021-03-11 01:59:04">
```

フォローしているのが1、フォローされているのが2　という関係性ができた

###14.1.3 Relationshipのバリデーション
先に進む前に、Relationshipモデルの検証を追加して完全なものにしておく。

テストコードとアプリケーションコードを作って実装していく。

ただし、User用のfixtureファイルと同じように、生成されたRelationship用のfixtureでは、
マイグレーションで制約させた一意性を満たすことができない。

このままだと正しくテストを行えないので、今の時点では、
生成されたRelationship用のfixtureファイルを空にしておく。

```test/models/relationship_test.rb
require 'test_helper'

class RelationshipTest < ActiveSupport::TestCase

  def setup
    @relationship = Relationship.new(follower_id: users(:michael).id,
                                     followed_id: users(:archer).id)
  end

  test "should be valid" do
    assert @relationship.valid?
  end

  test "should require a follower_id" do
    @relationship.follower_id = nil
    assert_not @relationship.valid?
  end

  test "should require a followed_id" do
    @relationship.followed_id = nil
    assert_not @relationship.valid?
  end
end
```


```app/models/relationship.rb
class Relationship < ApplicationRecord
  belongs_to :follower, class_name: "User"
  belongs_to :followed, class_name: "User"
  validates :follower_id, presence: true
  validates :followed_id, presence: true
end
```


```test/fixtures/relationships.yml
# 空にする
```

今時点テストはパスする

###演習
1 リスト 14.5のバリデーションをコメントアウトしても、テストが成功したままになっていることを確認してみましょう。（以前のRailsのバージョンでは、このバリデーションが必須でしたが、Rails 5から必須ではなくなりました。今回はフォロー機能の実装を優先しますが、この手のバリデーションが省略されている可能性があることを頭の片隅で覚えておくと良いでしょう。）

```relationship.rb
class Relationship < ApplicationRecord
    belongs_to :follower, class_name: "User"
    belongs_to :followed, class_name: "User"
    # validates :follower_id, presence: true
    # validates :followed_id, presence: true
end
```

コメントアウトしてもテストがパスする

###14.1.4 フォローしているユーザー
followingとfollowersに取りかかる。

今回はhas_many throughを使う。
1人のユーザーにはいくつもの「フォローする」「フォローされる」といった関係性がある（こういった関係性を「多対多」と呼ぶ）
デフォルトのhas_many throughという関連付けでは、Railsはモデル名（単数形）に対応する外部キーを探す。

つまり、次のコードでは、

```
has_many :followeds, through: :active_relationships
```

・Railsは「followeds」というシンボル名を見て、これを「followed」という単数形に変え、 relationshipsテーブルのfollowed_idを使って対象のユーザーを取得してくる。
・しかし、user.followedsという名前は英語として不適切
・代わりに、user.followingという名前を使う。
・そのためには、Railsのデフォルトを上書きする必要がある。
・ここでは:sourceパラメーターを使って、「following配列の元はfollowed idの集合である」ということを明示的にRailsに伝える。


```app/models/user.rb
class User < ApplicationRecord
  has_many :microposts, dependent: :destroy
  has_many :active_relationships, class_name:  "Relationship",
                                  foreign_key: "follower_id",
                                  dependent:   :destroy
  has_many :following, through: :active_relationships, source: :followed
#sourceパラメーターでfollowing配列の元はfollowed_idの集合と明示する
  .
end
```

上で定義した関連付けにより、フォローしているユーザーを配列の様に扱えるようになった。
例えば、include?メソッドを使ってフォローしているユーザーの集合を調べてみたり、関連付けを通してオブジェクトを探しだせるようになる。

```
user.following.include?(other_user) #フォローしている人にother_userがいるか調べる
user.following.find(other_user)#otherユーザーを探して返す
```

followingで取得したオブジェクトは、配列のように要素を追加したり削除したりすることができる

```
user.following << other_user #フォローしている人にother_userをいれる
user.following.delete(other_user)#フォロー配列からother_userを削除する
```

followingメソッドで配列のように扱えるだけでも便利ですが、Railsは単純な配列ではなく、もっと賢くこの集合を扱っている。
例えば次のようなコードでは、

```
following.include?(other_user)
```

フォローしている全てのユーザーをデータベースから取得し、その集合に対してinclude?メソッドを実行しているように見えるが、
しかし実際にはデータベースの中で直接比較をするように配慮している

```
user.microposts.count
```

なお、これはデータベースの中で合計を計算したほうが高速になる点に注意

次に、followingで取得した集合をより簡単に取り扱うために、`follow`や`unfollow`といった便利メソッドを追加する。

これらのメソッドは、例えばuser.follow(other_user)といったように使う。
さらに、これに関連するfollowing?論理値メソッドも追加し、あるユーザーが誰かをフォローしているかどうかを確認できるようにする。

今回は、こういったメソッドは「テストから先に書いていく」　
というのも、Webインターフェイスなどで便利メソッドを使うのはまだ先なので、すぐに使える場面がなく、実装した手応えを得にくいから。
一方で、Userモデルに対するテストを書くのは簡単かつ今すぐできる。Userモデルのテストの中で`follow`や`unfollow`のメソッドを使っていく。
具体的には、following?メソッドで=あるユーザーをまだフォローしていないことを確認、
followメソッドを使って=そのユーザーをフォロー
following?メソッドを使って=フォロー中になったことを確認
最後にunfollowメソッドで=フォロー解除できたことを確認、といった具合でテスト

```test/models/user_test.rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  .
  test "should follow and unfollow a user" do
    michael = users(:michael)
    archer  = users(:archer)
    assert_not michael.following?(archer)
    michael.follow(archer)
    assert michael.following?(archer)
    michael.unfollow(archer)
    assert_not michael.following?(archer)
  end
end
```

followingによる関連付けを使ってfollow、unfollow、following?メソッドを実装していく。
このとき、可能な限りself（user自身を表すオブジェクト）を省略している点に注目

```app/models/user.rb
class User < ApplicationRecord

     # ユーザーをフォローする
    def follow(other_user)
        following << other_user #配列にいれる
    end

    # ユーザーをフォロー解除する
    def unfollow(other_user)
        active_relationships.find_by(followed_id: other_user.id).destroy
        #引数のidを取得し、配列から削除する
    end

    # 現在のユーザーがフォローしてたらtrueを返す
    def following?(other_user)
        following.include?(other_user)#following配列にother_userを含んでいるか
    end

  private
  .
end
```

テストはgreen になるはず

###演習
1 コンソールを開き、リスト 14.9のコードを順々に実行してみましょう。

```Ruby
irb(main):002:0>michael = User.find(3)
   (4.8ms)  SELECT sqlite_version(*)
  User Load (1.7ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 3], ["LIMIT", 1]]
=> #<User id: 3, name: "Cleopatra Bruen", email: "example-2@railstutorial.org", created_at: "2021-03-06 00:51:32", updated_at: "202...
irb(main):003:0> archer = User.find(4)
  User Load (0.3ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 4], ["LIMIT", 1]]
=> #<User id: 4, name: "Gerald VonRueden", email: "example-3@railstutorial.org", created_at: "2021-03-06 00:51:32", updated_at: "20...
irb(main):005:0> michael.following?(archer)
  User Exists? (0.4ms)  SELECT 1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ?  [["follower_id", 3], ["id", 4], ["LIMIT", 1]]
irb(main):006:0> michael.follow(archer) 
   (0.2ms)  begin transaction
  User Load (0.1ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 3], ["LIMIT", 1]]
  Relationship Create (3.3ms)  INSERT INTO "relationships" ("follower_id", "followed_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["follower_id", 3], ["followed_id", 4], ["created_at", "2021-03-11 02:38:13.617905"], ["updated_at", "2021-03-11 02:38:13.617905"]]
   (1.5ms)  commit transactionationships"."follower_id" = ? LIMIT ?  [["follower_id", 3], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 4, name: "Gerald VonRueden", email: "example-3@railstutorial.org", created_at: "2021-03-06 00:51:32", updated_at: "2021-03-06 00:51:32", password_digest: [FILTERED], remember_digest: nil, admin: false, activation_digest: "$2a$12$Q2X8c2Wsw2c6Jtez/VV.2OheICsfU1n1WUbHvJsIHxP...", activated: true, activated_at: "2021-03-06 00:51:32", reset_digest: nil, reset_sent_at: nil>]>
  User Exists? (0.3ms)  SELECT 1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ?  [["follower_id", 3], ["id", 4], ["LIMIT", 1]]
irb(main):007:0>michael.following?(archer)  
=> true
irb(main):008:0> michael.unfollow(archer) 
  Relationship Load (0.4ms)  SELECT "relationships".* FROM "relationships" WHERE "relationships"."follower_id" = ? AND "relationships"."followed_id" = ? LIMIT ?  [["follower_id", 3], ["followed_id", 4], ["LIMIT", 1]]
   (0.1ms)  begin transaction
  Relationship Destroy (2.8ms)  DELETE FROM "relationships" WHERE "relationships"."id" = ?  [["id", 93]]
   (2.4ms)  commit transaction
=> #<Relationship id: 93, follower_id: 3, followed_id: 4, created_at: "2021-03-11 02:38:13", updated_at: "2021-03-11 02:38:13">
irb(main):010:0>  michael.following?(archer)
  User Exists? (0.4ms)  SELECT 1 AS one FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ? AND "users"."id" = ? LIMIT ?  [["follower_id", 3], ["id", 4], ["LIMIT", 1]]
=> false
```


2 先ほどの演習の各コマンド実行時の結果を見返してみて、実際にはどんなSQLが出力されたのか確認してみましょう。
→上記記載

###14.1.5 フォロワー
リレーションシップというパズルの最後の一片は、`user.followers`メソッドを追加すること。
これは上の`user.following`メソッドと対になる。
フォロワーの配列を展開するために必要な情報は、relationshipsテーブルに既にある
つまり、作成したactive_relationshipsのテーブルを再利用することができそう。
実際、follower_idとfollowed_idを入れ替えるだけで、フォロワーについてもフォローする場合と全く同じ方法が活用可能になる

![Relationshipモデルのカラムを入れ替えて作った、フォロワーのモデル](https://railstutorial.jp/chapters/6.0/images/figures/user_has_many_followers_3rd_edition.png)

要は、active_relationshipsをpassive_relationshipsに入れ替えて、
followed_idとfollower_idを入れ替えるだけ。

上記のデータモデルの実装をuser.rbにhas_manyを使って行う。

```app/models/user.rb
class User < ApplicationRecord
  has_many :microposts, dependent: :destroy
  has_many :active_relationships,  class_name:  "Relationship",
                                   foreign_key: "follower_id",
                                   dependent:   :destroy
  has_many :passive_relationships, class_name:  "Relationship",
                                   foreign_key: "followed_id",
                                   dependent:   :destroy
  has_many :following, through: :active_relationships,  source: :followed
  has_many :followers, through: :passive_relationships, source: :follower
  .
  .
  .
end
```

一点、注意すべき箇所は、次のように参照先（followers）を指定するための:sourceキーを省略してもよかった点。

```
has_many :followers, through: :passive_relationships
```

これは:followers属性の場合、Railsが「followers」を単数形にして自動的に外部キーfollower_idを探してくれるから。
必要のない:sourceキーをそのまま残しているのは、has_many :followingとの類似性を強調させるため。

次に、`followers.include?`メソッドを使って先ほどのデータモデルをテストしていく。

```test/models/user_test.rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  .
  .
  .
  test "should follow and unfollow a user" do
    michael  = users(:michael)
    archer   = users(:archer)
    assert_not michael.following?(archer)
    michael.follow(archer)
    assert michael.following?(archer)
    assert archer.followers.include?(michael)←ここだけ追加
    michael.unfollow(archer)
    assert_not michael.following?(archer)
  end
end
```

この時点で、全てのテストが green になる

###演習
1 コンソールを開き、何人かのユーザーが最初のユーザーをフォローしている状況を作ってみてください。最初のユーザーをuserとすると、user.followers.map(&:id)の値はどのようになっているでしょうか?

```Ruby
irb(main):001:0> user = User.first
=> #<User id: 1, name: "Example User", email: "example@railstutorial.org", created_at: "2021-03-06 00:51:30", updated_at: "2021-03-...
irb(main):002:0> user_second = User.second
=> #<User id: 2, name: "Eugenio O'Reilly", email: "example-1@railstutorial.org", created_at: "2021-03-06 00:51:31", updated_at: "20...
irb(main):003:0> user.followers.map(&:id)
  User Load (5.0ms)  SELECT "users".* FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ?  [["followed_id", 1]]
=> [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41]
```

2 上の演習が終わったら、user.followers.countの実行結果が、先ほどフォローさせたユーザー数と一致していることを確認してみましょう。

3 user.followers.countを実行した結果、出力されるSQL文はどのような内容になっているでしょうか? また、user.followers.to_a.countの実行結果と違っている箇所はありますか? ヒント: もしuserに100万人のフォロワーがいた場合、どのような違いがあるでしょうか? 考えてみてください。

```Ruby
irb(main):004:0> user.followers.count
   (0.7ms)  SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ?  [["followed_id", 1]]
=> 38
irb(main):005:0> user.followers.to_a.count
=> 38
```

user.followers.countは100万の場合、配列を生成するため時間がかかる



##14.2 ［Follow］のWebインターフェイス
この節では、フォロー/フォロー解除の基本的なインターフェイスを実装し、フォローしているユーザーと、フォロワーにそれぞれ表示用のページを作成する

###14.2.1 フォローのサンプルデータ
サンプルデータを自動作成するrails db:seedを使って、データベースにサンプルデータを登録できると便利。
先にサンプルデータを自動作成できるようにしておけば、Webページの見た目のデザインから先にとりかかることができ、バックエンド機能の実装を後に回すことができる

以下のコードはリレーションシップのサンプルデータを生成するためのコード
最初のユーザーにユーザー3からユーザー51までをフォローさせ、それから逆にユーザー4からユーザー41に最初のユーザーをフォローさせる。

```db/seeds.rb
.
.
.
# 以下のリレーションシップを作成する
users = User.all
user  = users.first
following = users[2..50]
followers = users[3..40]
following.each { |followed| user.follow(followed) }
followers.each { |follower| follower.follow(user) }
```

```
$ rails db:migrate:reset
$ rails db:seed
```

###演習
1 コンソールを開き、User.first.followers.countの結果がリスト 14.14で期待している結果と合致していることを確認してみましょう。
2 先ほどの演習と同様に、User.first.following.countの結果も合致していることを確認してみましょう。

```Ruby
irb(main):006:0> User.first.followers.count
  User Load (0.7ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
   (0.4ms)  SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."follower_id" WHERE "relationships"."followed_id" = ?  [["followed_id", 1]]
=> 38
irb(main):007:0> User.first.following.count
  User Load (0.3ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
   (1.8ms)  SELECT COUNT(*) FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."followed_id" WHERE "relationships"."follower_id" = ?  [["follower_id", 1]]
=> 50
```


###14.2.2 統計と［Follow］フォーム
これでサンプルユーザーに、フォローしているユーザーとフォロワーができた。
プロフィールページとHomeページを更新して、これを反映させる。

- プロフィールページとHomeページに、フォローしているユーザーとフォロワーの統計情報を表示するためのパーシャルを作成。
- フォロー用とフォロー解除用のフォームを作成
- フォローしているユーザーの一覧（"following"）とフォロワーの一覧（"followers"）を表示する専用のページを作成

Twitterの慣習に従ってフォロー数の単位には「following」を使い、例えば「50 following」といった具合に表示

統計情報には、現在のユーザーがフォローしている人数と、現在のフォロワーの人数が表示させる。
それぞれの表示はリンクになっており、専用の表示ページに移動できるようにする。

そのためのルーティングを今設定する↓↓
このコードでは、resourcesブロックの内側で:memberメソッドを使ってる。

```config/routes.rb
Rails.application.routes.draw do
  root   'static_pages#home'
  get    '/help',    to: 'static_pages#help'
  get    '/about',   to: 'static_pages#about'
  get    '/contact', to: 'static_pages#contact'
  get    '/signup',  to: 'users#new'
  get    '/login',   to: 'sessions#new'
  post   '/login',   to: 'sessions#create'
  delete '/logout',  to: 'sessions#destroy'
  resources :users do
    member do
      get :following, :followers
    end
  end
  resources :account_activations, only: [:edit]
  resources :password_resets,     only: [:new, :create, :edit, :update]
  resources :microposts,          only: [:create, :destroy]
end
```

この場合のURLは /users/1/following や /users/1/followers のようになる
また、どちらもデータを表示するページなので、適切なHTTPメソッドはGETリクエストになる
したがって、getメソッドを使って適切なレスポンスを返すようにする
ちなみに、memberメソッドを使うとユーザーidが含まれているURLを扱うようになるが、idを指定せずにすべてのメンバーを表示するには、次のようにcollectionメソッドを使う。

```
resources :users do
  collection do
    get :tigers
  end
end
```

このコードは /users/tigers というURLに応答する

上で生成されるルーティングテーブルを示す。
この表で示したフォロー用とフォロワー用の名前付きルートを、今後の実装で使っていく。

|HTTPリクエスト	|URL	|アクション	|名前付きルート|
|:--:|:--|:--:|:--|
|GET	|/users/1/following	|following	|following_user_path(1)
|GET	|/users/1/followers	|followers	|followers_user_path(1)

ルーティングを定義したので、統計情報のパーシャルを実装する準備が整った。
このパーシャルでは、divタグの中に2つのリンクを含めるようにする

```app/views/shared/_stats.html.erb
<% @user ||= current_user %>
<div class="stats">
  <a href="<%= following_user_path(@user) %>">
    <strong id="following" class="stat">
      <%= @user.following.count %>
    </strong>
    following
  </a>
  <a href="<%= followers_user_path(@user) %>">
    <strong id="followers" class="stat">
      <%= @user.followers.count %>
    </strong>
    followers
  </a>
</div>
```

このパーシャルはプロフィールページとHomeページの両方に表示されるため、次のコードで現在のユーザーを取得している

```
<% @user ||= current_user %>
```

これは@userがnilでない場合（つまりプロフィールページの場合）は何もせず、nilの場合（つまりHomeページの場合）には@userにcurrent_userを代入するコード。
その後、フォローしているユーザーの人数を、次のように関連付けを使って計算。

```
@user.following.count
```

これはフォロワーについても同様

```
@user.followers.count
```

次のようにCSS idを指定していることにも注目。

```
<strong id="following" class="stat">
...
</strong>
```

こうしておくと、Ajaxを実装するときに便利

これで統計情報パーシャルができあがる

```app/views/static_pages/home.html.erb
<% if logged_in? %>
  <div class="row">
    <aside class="col-md-4">
      <section class="user_info">
        <%= render 'shared/user_info' %>
      </section>
      <section class="stats">
        <%= render 'shared/stats' %>
      </section>
      <section class="micropost_form">
        <%= render 'shared/micropost_form' %>
      </section>
    </aside>
    <div class="col-md-8">
      <h3>Micropost Feed</h3>
      <%= render 'shared/feed' %>
    </div>
  </div>
<% else %>
  .
  .
  .
<% end %>
```

統計情報にスタイルを与えるために、SCSSを追加

```app/assets/stylesheets/custom.scss
.
.
.
/* sidebar */
.
.
.
.gravatar {
  float: left;
  margin-right: 10px;
}

.gravatar_edit {
  margin-top: 15px;
}

.stats {
  overflow: auto;
  margin-top: 0;
  padding: 0;
  a {
    float: left;
    padding: 0 10px;
    border-left: 1px solid $gray-lighter;
    color: gray;
    &:first-child {
      padding-left: 0;
      border: 0;
    }
    &:hover {
      text-decoration: none;
      color: blue;
    }
  }
  strong {
    display: block;
  }
}

.user_avatars {
  overflow: auto;
  margin-top: 10px;
  .gravatar {
    margin: 1px 1px;
  }
  a {
    padding: 0;
  }
}

.users.follow {
  padding: 0;
}

/* forms */
.
.
.
```

この後すぐ、プロフィールにも統計情報パーシャルを表示するが、今のうちに［Follow］/［Unfollow］ボタン用のパーシャルも作成

```app/views/users/_follow_form.html.erb
<% unless current_user?(@user) %>
  <div id="follow_form">
  <% if current_user.following?(@user) %>
    <%= render 'unfollow' %>
  <% else %>
    <%= render 'follow' %>
  <% end %>
  </div>
<% end %>
```

このコードは、followとunfollowのパーシャルに作業を振っているだけ。
パーシャルでは、Relationshipsリソース用の新しいルーティングが必要

```config/routes.rb
Rails.application.routes.draw do
  root                'static_pages#home'
  get    'help'    => 'static_pages#help'
  get    'about'   => 'static_pages#about'
  get    'contact' => 'static_pages#contact'
  get    'signup'  => 'users#new'
  get    'login'   => 'sessions#new'
  post   'login'   => 'sessions#create'
  delete 'logout'  => 'sessions#destroy'
  resources :users do
    member do
      get :following, :followers
    end
  end
  resources :account_activations, only: [:edit]
  resources :password_resets,     only: [:new, :create, :edit, :update]
  resources :microposts,          only: [:create, :destroy]
  resources :relationships,       only: [:create, :destroy]#追加
end
```

ユーザーをフォローするフォーム

```app/views/users/_follow.html.erb
<%= form_with(model: current_user.active_relationships.build, local: true) do |f| %>
  <div><%= hidden_field_tag :followed_id, @user.id %></div>
  <%= f.submit "Follow", class: "btn btn-primary" %>
<% end %>
```

ユーザーをフォロー解除するフォーム

```app/views/users/_unfollow.html.erb
<%= form_with(model: current_user.active_relationships.find_by(followed_id: @user.id),
            html: { method: :delete }, local: true) do |f| %>
  <%= f.submit "Unfollow", class: "btn" %>
<% end %>
```

これらの2つのフォームでは、いずれもform_withを使ってRelationshipモデルオブジェクトを操作している。
これらの2つのフォームの主な違いは、ユーザーフォローは新しいリレーションシップを作成するのに対し、
フォロー解除は既存のリレーションシップを見つけ出すという点

すなわち、前者はPOSTリクエストを Relationshipsコントローラに送信してリレーションシップをcreate（作成）し、
後者はDELETEリクエストを送信してリレーションシップをdestroy（削除）する（アクションは後に実装）

最終的に、このフォロー/フォロー解除フォームにはボタンしかない。
しかし、それでもこのフォームはfollowed_idをコントローラに送信する必要がある

hidden_field_tagメソッドを使い実装する。このメソッドは、次のフォーム用HTMLを生成するメソッド

```HTML
<input id="followed_id" name="followed_id" type="hidden" value="3" />
```

隠しフィールドのinputタグを使うことで、ブラウザ上に表示させずに適切な情報を含めることができる。

このテクニックを使ってフォロー用フォームをパーシャルとしてプロフィール画面に表示した結果が以下のコード

```app/views/users/show.html.erb
<% provide(:title, @user.name) %>
<div class="row">
  <aside class="col-md-4">
    <section class="user_info">
      <h1>
        <%= gravatar_for @user %>
        <%= @user.name %>
      </h1>
    </section>
    <section class="stats">←注目
      <%= render 'shared/stats' %>←注目
    </section>
  </aside>
  <div class="col-md-8">
    <%= render 'follow_form' if logged_in? %>←注目
    <% if @user.microposts.any? %>
      <h3>Microposts (<%= @user.microposts.count %>)</h3>
      <ol class="microposts">
        <%= render @microposts %>
      </ol>
      <%= will_paginate @microposts %>
    <% end %>
  </div>
</div>
```

![プロフィール画面（/users/2）に［Follow］ボタンが表示されている](https://railstutorial.jp/chapters/6.0/images/figures/profile_follow_button.png)
プロフィール画面（/users/2）に［Follow］ボタンが表示されている

![プロフィール画面（/users/5）に［Unfollow］ボタンが表示されている](https://railstutorial.jp/chapters/6.0/images/figures/profile_unfollow_button.png)

プロフィール画面（/users/5）に［Unfollow］ボタンが表示されている

実はこのボタンの実装には２通りの方法がある。
１つは標準的な方法、もう１つはAjaxを使う方法。
でもその前に、フォローしているユーザーとフォロワーを表示するページをそれぞれ作成してHTMLインターフェイスを完成させる

###演習
1　ブラウザから /users/2 にアクセスし、フォローボタンが表示されていることを確認してみましょう。同様に、/users/5 では［Unfollow］ボタンが表示されているはずです。さて、/users/1 にアクセスすると、どのような結果が表示されるでしょうか?
→確認済

2　ブラウザからHomeページとプロフィールページを表示してみて、統計情報が正しく表示されているか確認してみましょう。
→確認済

3　Homeページに表示されている統計情報に対してテストを書いてみましょう。ヒント: リスト 13.28で示したテストに追加してみてください。同様にして、プロフィールページにもテストを追加してみましょう。

```site_layout_test.rb
 test "count relationships" do
    log_in_as(@user)
    get root_path
    assert_match @user.active_relationships.count.to_s, response.body
    assert_match @user.passive_relationships.count.to_s, response.body
  end
```

```users_profile_test.rb
  end
    assert_match @user.active_relationships.count.to_s, response.body
    assert_match @iser.passive_relationhips.count.to_s, response.body
  end
```

###14.2.3 ［Following］と［Followers］ページ
フォローしているユーザーを表示するページと、フォロワーを表示するページは、いずれもプロフィールページとユーザー一覧ページを合わせたような作りになるという点で似ている
どちらにもフォローの統計情報などのユーザー情報を表示するサイドバーと、ユーザーのリストがあり、サイドバーには小さめのユーザープロフィール画像のリンクを格子状に並べて表示する予定。
モックアップはこちら

![フォローしているユーザー用ページのモックアップ](https://railstutorial.jp/chapters/6.0/images/figures/following_mockup_bootstrap.png)

![ユーザーのフォロワー用ページのモックアップ
](https://railstutorial.jp/chapters/6.0/images/figures/followers_mockup_bootstrap.png)

ここでの最初の作業は、フォローしているユーザーのリンクとフォロワーのリンクを動くようにすること
Twitterに倣って、どちらのページでもユーザーのログインを要求するようにする。
そこで前回のアクセス制御と同様に、まずはテストから書いていく。

```test/controllers/users_controller_test.rb
require 'test_helper'

class UsersControllerTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:michael)
    @other_user = users(:archer)
  end
  .
  .
  .
  test "should redirect following when not logged in" do
    get following_user_path(@user)←名前付きパス
    assert_redirected_to login_url
  end

  test "should redirect followers when not logged in" do
    get followers_user_path(@user)
    assert_redirected_to login_url
  end
end
```

フォローしているユーザーを表示するページと、フォロワーを表示するページの実装には１つだけトリッキーな部分がある
それはUsersコントローラに2つの新しいアクションを追加する必要があるということ
以前定義した2つのルーティングにもとづいており、これらはそれぞれfollowingおよびfollowersと呼ぶ必要がある。
それぞれのアクションでは、タイトルを設定し、ユーザーを検索し、@user.followingまたは@user.followersからデータを取り出し、ページネーションを行なって、ページを出力する必要がある

```app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :logged_in_user, only: [:index, :edit, :update, :destroy,
                                        :following, :followers]
  .
  .
  .
  def following
    @title = "Following"
    @user  = User.find(params[:id])
    @users = @user.following.paginate(page: params[:page])
    render 'show_follow'
  end

  def followers
    @title = "Followers"
    @user  = User.find(params[:id])
    @users = @user.followers.paginate(page: params[:page])
    render 'show_follow'
  end
```

Railsは慣習に従って、アクションに対応するビューを暗黙的に呼び出す
例えば、showアクションの最後でshow.html.erbを呼び出す、といった具合で。
一方で、上のいずれのアクションも、renderを明示的に呼び出し、show_followという同じビューを出力している。
したがって、作成が必要なビューはこれ1つでOK。
呼び出しているビューが同じである理由は、このERbはどちらの場合でもほぼ同じだから

```app/views/users/show_follow.html.erb
<% provide(:title, @title) %>
<div class="row">
  <aside class="col-md-4">
    <section class="user_info">
      <%= gravatar_for @user %>
      <h1><%= @user.name %></h1>
      <span><%= link_to "view my profile", @user %></span>
      <span><b>Microposts:</b> <%= @user.microposts.count %></span>
    </section>
    <section class="stats">
      <%= render 'shared/stats' %>
      <% if @users.any? %>
        <div class="user_avatars">
          <% @users.each do |user| %>
            <%= link_to gravatar_for(user, size: 30), user %>
          <% end %>
        </div>
      <% end %>
    </section>
  </aside>
  <div class="col-md-8">
    <h3><%= @title %></h3>
    <% if @users.any? %>
      <ul class="users follow">
        <%= render @users %>
      </ul>
      <%= will_paginate %>
    <% end %>
  </div>
</div>
```

followingもfollowersアクションも1つのビューでこんな感じでできる
“following”をとおって描画したビュー
“followers”をとおって描画したビュー
上のコードでは現在のユーザーを一切使っていない点に注目。
したがって、他のユーザーのフォロワー一覧ページもうまく動く
![](https://railstutorial.jp/chapters/6.0/images/figures/user_following.png)

![](https://railstutorial.jp/chapters/6.0/images/figures/user_followers.png)

なお、テストはパスする

次に、show_followの描画結果を確認するため、統合テストを書いていく
ただし今回の統合テストは現実性のテストだけに留めており、網羅的なテストにはしていない。
HTML構造を網羅的にチェックするテストは壊れやすく、生産性を逆に落としかねないから。
したがって今回は、正しい数が表示されているかどうかと、正しいURLが表示されているかどうかの２つのテストを書く。

統合テストを生成する

```
$ rails generate integration_test following
      invoke  test_unit
      create    test/integration/following_test.rb
```

今度はテストデータをいくつか揃える

リレーションシップ用のfixtureにデータを追加する。

次のように書くことで

```
orange:
  content: "I just ate an orange!"
  created_at: <%= 10.minutes.ago %>
  user: michael
```

ユーザーとマイクロポストを関連付けできる。
上のコードではユーザー名を次のように書いているが

```
user: michael
```

これは内部的には次のようなコードに自動的に変換されている。

```
user_id: 1
```

この例を参考にしてRelationship用のfixtureにテストデータを追加する

```test/fixtures/relationships.yml
one:
  follower: michael
  followed: lana

two:
  follower: michael
  followed: malory

three:
  follower: lana
  followed: michael

four:
  follower: archer
  followed: michael
```

前半の2つでMichaelがLanaとMaloryをフォローし、後半の2つでLanaとArcherがMichaelをフォローしてる。
あとは、正しい数かどうかを確認するために、assert_matchメソッドを使ってプロフィール画面のマイクロポスト数をテスト。
さらに、正しいURLかどうかをテストする

```test/integration/following_test.rb
require 'test_helper'

class FollowingTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:michael)
    log_in_as(@user)
  end

 test "following page" do
    get following_user_path(@user)#show_followページを描写
    assert_not @user.following.empty?#@userのフォローは空でないか
    assert_match @user.following.count.to_s, response.body#フォローの数はあっているか
    @user.following.each do |user|
      assert_select "a[href=?]", user_path(user)#フォローそれぞれにaタグとuserへのリンクがあるか
    end
  end

  test "followers page" do
    get followers_user_path(@user)#show_followページ描写
    assert_not @user.followers.empty?#フォローワーは空でないか
    assert_match @user.followers.count.to_s, response.body#フォローワーの数はあっているか
    @user.followers.each do |user|
      assert_select "a[href=?]", user_path(user)#それぞれにリンクはあるか
    end
  end
end
```

なおこのコードは

```
assert_not @user.following.empty?
```

このコードは次のコードを確かめるためのテストであって、

```
@user.following.each do |user|
  assert_select "a[href=?]", user_path(user)
end
```

無意味なテストではないことに注意。
つまり、もし@user.following.empty?の結果がtrueであれば、assert_select内のブロックが実行されなくなるため、その場合においてテストが適切なセキュリティモデルを確認できなくなることを防ぐ。

テストはパスする

###演習
ブラウザから /users/1/followers と /users/1/following を開き、それぞれが適切に表示されていることを確認してみましょう。サイドバーにある画像は、リンクとしてうまく機能しているでしょうか?
→確認済
リスト 14.29のassert_selectに関連するコードをコメントアウトしてみて、テストが正しく red に変わることを確認してみましょう。

```
<% provide(:title, @title) %>
<div class="row">
  <aside class="col-md-4">
    <section class="user_info">
      <%= gravatar_for @user %>
      <h1><%= @user.name %></h1>
      <span><%= link_to "view my profile", @user %></span>
      <span><b>Microposts:</b> <%= @user.microposts.count %></span>
    </section>
    <section class="stats">
      <%= render 'shared/stats' %>
      <% if @users.any? %>
        <div class="user_avatars">
          <%# @users.each do |user| %>
            <%#= link_to gravatar_for(user, size: 30), user %>
          <%# end %>
        </div>
      <% end %>
    </section>
  </aside>
  <div class="col-md-8">
    <h3><%= @title %></h3>
    <% if @users.any? %>
      <ul class="users follow">
        <%# <%= render @users %> %>
      </ul>
      <%= will_paginate %>
    <% end %>
  </div>
</div>
```

###14.2.4 ［Follow］ボタン（基本編）
［Follow］/［Unfollow］ボタンを動作させる。
フォローとフォロー解除はそれぞれリレーションシップの作成と削除に対応しているため、まずはRelationshipsコントローラが必要

```
$ rails generate controller Relationships
```

Relationshipsコントローラのアクションでアクセス制御することはそこまで難しくない。

しかし、前回のアクセス制御の時と同様に最初にテストを書き、それをパスするように実装することでセキュリティモデルを確立させていく。
今回はまず、コントローラのアクションにアクセスする時、ログイン済みのユーザーであるかどうかをチェックする。

もしログインしていなければログインページにリダイレクトされるので、Relationshipのカウントが変わっていないことを確認する。

```test/controllers/relationships_controller_test.rb
require 'test_helper'

class RelationshipsControllerTest < ActionDispatch::IntegrationTest

test "create should require logged-in user" do
    assert_no_difference 'Relationship.count' do#数に違いはないか
      post relationships_path#post　relationpashを通って
    end
    assert_redirected_to login_url#ログインurlに飛ぶか
  end

  test "destroy should require logged-in user" do
    assert_no_difference 'Relationship.count' do
      delete relationship_path(relationships(:one))
    end
    assert_redirected_to login_url
  end
```

次に、テストをパスさせるために、logged_in_userフィルターをRelationshipsコントローラのアクションに対して追加

```app/controllers/relationships_controller.rb
class RelationshipsController < ApplicationController
  before_action :logged_in_user

  def create
  end

  def destroy
  end
end
```

［Follow］/［Unfollow］ボタンを動作させるためには、フォーム(show)から送信されたパラメータを使って、
followed_idに対応するユーザーを見つけてくる必要がある。
その後、見つけてきたユーザーに対して適切にfollow/unfollowメソッドを使う

```app/controllers/relationships_controller.rb
class RelationshipsController < ApplicationController
  before_action :logged_in_user

  def create
    user = User.find(params[:followed_id])
    current_user.follow(user)
    redirect_to user
  end

  def destroy
    user = Relationship.find(params[:id]).followed
    current_user.unfollow(user)
    redirect_to user
  end
end
```

上記をみてれば、先ほどのセキュリティ問題が実はそれほど重要なものではないことを理解できる。

もし、ログインしていないユーザーが(curlなどのコマンドラインツールなどを使って）これらのアクションに直接アクセスするようなことがあれば、current_userはnilになり、
どちらのメソッドでも2行目で例外が発生する。

エラーにはなるが、アプリケーションやデータに影響は生じない。

このままでも支障はないが、このような例外には頼らない方がいいので、セキュリティの為のレイヤーを追加した。

これで、フォロー/フォロー解除の機能が完成した。

どのユーザーも、他のユーザーをフォローしたりリフォローしたりできる。
ブラウザ上でボタンをクリックして、確かめてみる。
(振る舞いを検証する統合テストはのちに実装する)

###演習
ブラウザ上から /users/2 を開き、［Follow］と［Unfollow］を実行してみましょう。うまく機能しているでしょうか?
→確認済
先ほどの演習を終えたら、Railsサーバーのログを見てみましょう。フォロー/フォロー解除が実行されると、それぞれどのテンプレートが描画されているでしょうか?

```
Started DELETE "/relationships/94" for ::1 at 2021-03-12 08:22:41 +0900
Processing by RelationshipsController#destroy as HTML
  Parameters: {"authenticity_token"=>"3g9qXSWhTU293nfQKklwQdERbg5xtiOMcaqMnUSy+tAvJmR3ZgtMsTElPna8sOQ0di+VPQXqHlzCHeK5HvjBlg==", "commit"=>"Unfollow", "id"=>"94"}
  User Load (0.3ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 3], ["LIMIT", 1]]
  ↳ app/helpers/sessions_helper.rb:16:in `current_user'
  Relationship Load (0.5ms)  SELECT "relationships".* FROM "relationships" WHERE "relationships"."id" = ? LIMIT ?  [["id", 94], ["LIMIT", 1]]
  ↳ app/controllers/relationships_controller.rb:15:in `destroy'
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
  ↳ app/controllers/relationships_controller.rb:15:in `destroy'
  Relationship Load (0.3ms)  SELECT "relationships".* FROM "relationships" WHERE "relationships"."follower_id" = ? AND "relationships"."followed_id" = ? LIMIT ?  [["follower_id", 3], ["followed_id", 2], ["LIMIT", 1]]
  ↳ app/models/user.rb:96:in `unfollow'
   (0.2ms)  begin transaction
  ↳ app/models/user.rb:96:in `unfollow'
  Relationship Destroy (1.7ms)  DELETE FROM "relationships" WHERE "relationships"."id" = ?  [["id", 94]]
  ↳ app/models/user.rb:96:in `unfollow'
   (1.6ms)  commit transaction
  ↳ app/models/user.rb:96:in `unfollow'
Redirected to http://localhost:3000/users/2
Completed 302 Found in 25ms (ActiveRecord: 4.8ms | Allocations: 5129)


Started GET "/users/2" for ::1 at 2021-03-12 08:22:41 +0900
Processing by UsersController#show as HTML
  Parameters: {"id"=>"2"}
  User Load (0.3ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 2], ["LIMIT", 1]]
  ↳ app/controllers/users_controller.rb:6:in `show'
  Rendering users/show.html.erb within layouts/application
```

###14.2.5 ［Follow］ボタン（Ajax編）
フォロー関連の機能の実装はこのとおり完了しましたが、ステータスフィードに取りかかる前にもう一つだけ機能を洗練させる

Relationshipsコントローラのcreateアクションと destroyアクションを単に元のプロフィールにリダイレクトさせた。
つまり、ユーザーはプロフィールページを最初に表示し、それからユーザーをフォローし、その後すぐ元のページにリダイレクトされるという流れになる。
ユーザーをフォローした後、本当にそのページから離れて元のページに戻らないといけないのか。この点を考えなおす

これはAjaxを使うことで解決できる。
Ajaxを使えば、Webページからサーバーに「非同期」で、ページを移動することなくリクエストを送信することができる。
WebフォームにAjaxを採用するのは今や当たり前になりつつあるので、RailsでもAjaxを簡単に実装できるようになっている。

フォロー用とフォロー解除用のパーシャルをこれに沿って更新するのは簡単。次のコードがあるとすると、

```
form_with(model: ..., local: true)
```

上のコードを次のように置き換えるだけです。

```
form_with(model: ..., remote: true)
```

たったこれだけで、Railsは自動的にAjaxを使うようになる

```app/views/users/_follow.html.erb
<%= form_with(model: current_user.active_relationships.build, remote: true) do |f| %>
  <div><%= hidden_field_tag :followed_id, @user.id %></div>
  <%= f.submit "Follow", class: "btn btn-primary" %>
<% end %>
```

```app/views/users/_unfollow.html.erb
<%= form_with(model: current_user.active_relationships.find_by(followed_id: @user.id),
             html: { method: :delete }, remote: true) do |f| %>
  <%= f.submit "Unfollow", class: "btn" %>
<% end %>
```

ERbによって実際に生成されるHTMLはそれほど重要ではないが、参考までに以下のようなHTMLが生成される

```<form action="/relationships/117" class="edit_relationship" data-remote="true"
      id="edit_relationship_117" method="post">
  .
  .
  .
</form>
```

ここでは、formタグの内部でdata-remote="true"を設定している。

これは、JavaScriptによるフォーム操作を許可することをRailsに知らせるためのもの

フォームの更新が終わったので、今度はこれに対応するRelationshipsコントローラを改造して、Ajaxリクエストに応答できるようにする。
こういったリクエストの種類によって応答を場合分けするときは、respond_toメソッドというメソッドを使う

```
respond_to do |format|
  format.html { redirect_to user }
  format.js
end
```

この文法は少々変わっていて、上の（ブロック内の）コードのうち、いずれかの1行が実行されるという点が重要（このためrespond_toメソッドは、上から順に実行する逐次処理というより、if文を使った分岐処理に近いイメージ）。

RelationshipsコントローラでAjaxに対応させるために、respond_toメソッドをcreateアクションとdestroyアクションにそれぞれ追加
このとき、ユーザーのローカル変数（user）をインスタンス変数（@user）に変更した点に注目
Ajaxのフォームに変更したので、インスタンス変数が必要になったため。

```app/controllers/relationships_controller.rb
class RelationshipsController < ApplicationController
  before_action :logged_in_user

  def create
    @user = User.find(params[:followed_id])
    current_user.follow(@user)
    respond_to do |format|
      format.html { redirect_to @user }
      format.js
    end
  end

  def destroy
    @user = Relationship.find(params[:id]).followed
    current_user.unfollow(@user)
    respond_to do |format|
      format.html { redirect_to @user }
      format.js
    end
  end
end
```

Ajaxリクエストに対応したので、今度はブラウザ側でJavaScriptが無効になっていた場合（Ajaxリクエストが送れない場合）でもうまく動くようにする

```config/application.rb
require_relative 'boot'
.
.
.
module SampleApp
  class Application < Rails::Application
    .
    .
    .
    # 認証トークンをremoteフォームに埋め込む
    config.action_view.embed_authenticity_token_in_remote_forms = true
  end
end
```

一方で、JavaScriptが有効になっていても、まだ十分に対応できていない部分がある
というのも、Ajaxリクエストを受信した場合は、Railsが自動的にアクションと同じ名前を持つ**JavaScript用の埋め込みRuby**（.js.erb）ファイル（create.js.erbやdestroy.js.erbなど）を呼び出すから

これらのファイルではJavaScriptと埋め込みRuby（ERb）をミックスして現在のページに対するアクションを実行することができる。
ユーザーをフォローしたときやフォロー解除したときにプロフィールページを更新するために、これから作成および編集しなければならないのは、まさにこれらのファイル

JS-ERbファイルの内部では、DOM（Document Object Model）を使ってページを操作するため、RailsがjQuery JavaScriptヘルパーを自動的に提供している。
これによりjQueryライブラリの膨大なDOM操作用メソッドが使えるようになるが、今回使うのは2つだけ。

まずはドル記号（$）とCSS idを使って、DOM要素にアクセスする文法について知る必要がある。
例えばfollow_formの要素をjQueryで操作するには、次のようにアクセスする

```
$("#follow_form")
```

jQueryの文法はCSSの記法から影響を受けており、#シンボルを使ってCSSのidを指定 jQueryはCSSと同様、ドット.を使ってCSSクラスを操作できる

次に必要なメソッドはhtml
引数の中で指定された要素の内側にあるHTMLを更新する
例えばフォロー用フォーム全体を"foobar"という文字列で置き換えたい場合は、次のようなコードになる。

```
$("#follow_form").html("foobar")
```

純粋なJavaScriptと異なり、JS-ERbファイルでは組み込みRuby（ERb）が使える。
create.js.erbファイルでは、フォロー用のフォームをunfollowパーシャルで更新し、フォロワーのカウントを更新するのにERbを使っている（フォローに成功した場合の動作）

以下のコードではescape_javascriptメソッドを使っている点に注目。
このメソッドは、JavaScriptファイル内にHTMLを挿入するときに実行結果をエスケープするために必要

```app/views/relationships/create.js.erb
$("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>");
$("#followers").html('<%= @user.followers.count %>');
```

destroy.js.erbファイルの方も同様

```app/views/relationships/destroy.js.erb
$("#follow_form").html("<%= escape_javascript(render('users/follow')) %>");
$("#followers").html('<%= @user.followers.count %>');
```

これらのコードにより、プロフィールページを更新させずにフォローとフォロー解除ができるようになったはず

###演習
ブラウザから /users/2 にアクセスし、うまく動いているかどうか確認してみましょう。
先ほどの演習で確認が終わったら、Railsサーバーのログを閲覧し、フォロー/フォロー解除を実行した直後のテンプレートがどうなっているか確認してみましょう。


###14.2.6 フォローをテストする
フォローボタンが動くようになったので、バグを検知するためのシンプルなテストを書いていく。

ユーザーのフォローに対するテストでは、 /relationshipsに対してPOSTリクエストを送り、フォローされたユーザーが1人増えたことをチェックする

```Ruby
assert_difference '@user.following.count', 1 do
  post relationships_path, params: { followed_id: @other.id }
end
```

これは標準的なフォローに対するテストで、Ajax版もやり方は同じ
Ajaxのテストでは、`xhr :true`オプションを使うようにするだけ

```Ruby
assert_difference '@user.following.count', 1 do
  post relationships_path, params: { followed_id: @other.id }, xhr: true
end
```

ここで使っているxhr（XmlHttpRequest）というオプションをtrueに設定すると、Ajaxでリクエストを発行するように変わる。
したがって、respond_toでは、JavaScriptに対応した行が実行されるようになる。

また、ユーザーをフォロー解除するときも構造はほとんど同じで、postメソッドをdeleteメソッドに置き換えてテストする。
つまり、そのユーザーのidとリレーションシップのidを使ってDELETEリクエストを送信し、フォローしている数が1つ減ることを確認する。

したがって、実際に加えるテストは、

```Ruby
assert_difference '@user.following.count', -1 do
  delete relationship_path(relationship)
end
```

上の従来どおりのテストと、下のAjax用のテストの２つ

```Ruby
assert_difference '@user.following.count', -1 do
  delete relationship_path(relationship), xhr: true
end
```

これらのテストをまとめた結果

```test/integration/following_test.rb
require 'test_helper'

class FollowingTest < ActionDispatch::IntegrationTest

  def setup
    @user  = users(:michael)
    @other = users(:archer)
    log_in_as(@user)
  end
  .
  .
  .
  test "should follow a user the standard way" do
    assert_difference '@user.following.count', 1 do
      post relationships_path, params: { followed_id: @other.id }
    end
  end

  test "should follow a user with Ajax" do
    assert_difference '@user.following.count', 1 do
      post relationships_path, xhr: true, params: { followed_id: @other.id }
    end
  end

  test "should unfollow a user the standard way" do
    @user.follow(@other)
    relationship = @user.active_relationships.find_by(followed_id: @other.id)
    assert_difference '@user.following.count', -1 do
      delete relationship_path(relationship)
    end
  end

  test "should unfollow a user with Ajax" do
    @user.follow(@other)
    relationship = @user.active_relationships.find_by(followed_id: @other.id)
    assert_difference '@user.following.count', -1 do
      delete relationship_path(relationship), xhr: true
    end
  end
end
```

この時点では、テストは green 

###演習
1 リスト 14.36のrespond_toブロック内の各行を順にコメントアウトしていき、テストが正しくエラーを検知できるかどうか確認してみましょう。実際、どのテストケースが落ちたでしょうか?

```relationships_controller.rb
   def create
        @user = User.find(params[:followed_id])
        current_user.follow(@user)
        respond_to do |format|#ajax対応
            # format.html { redirect_to @user }
            format.js
        end
    end
```

エラーになる

2 リスト 14.40のxhr: trueがある行のうち、片方のみを削除するとどういった結果になるでしょうか? このとき発生する問題の原因と、なぜ先ほどの演習で確認したテストがこの問題を検知できたのか考えてみてください。

```following_test.rb
test "should follow a user with Ajax" do
    assert_difference '@user.following.count', 1 do
      # post relationships_path, xhr: true, params: { followed_id: @other.id }
    end
  end
```

```Ruby
 FAIL["test_should_follow_a_user_with_Ajax", #<Minitest::Reporters::Suite:0x00007fa480617b88 @name="FollowingTest">, 20.608980000019073]
 test_should_follow_a_user_with_Ajax#FollowingTest (20.61s)
        "@user.following.count" didn't change by 1.
        Expected: 3
          Actual: 2
```

##14.3 ステータスフィード

最後の難関、ステータスフィードの実装に取りかかる。

現在のユーザーにフォローされているユーザーのマイクロポストの配列を作成し、現在のユーザー自身のマイクロポストと合わせて表示する
このセクションを通して、複雑さを増したフィードの実装に進んでいく。

これを実現するためには、RailsとRubyの高度な機能の他に、SQLプログラミングの技術も必要

ステータスフィードの最終形

![](https://railstutorial.jp/chapters/6.0/images/figures/page_flow_home_page_feed_mockup_bootstrap.png)

###14.3.1 動機と計画

ステータスフィードの基本的なアイデアはシンプル。

以下の図の矢印で示されているように、この目的は、現在のユーザーによってフォローされているユーザーに対応するユーザーidを持つマイクロポストを取り出し、同時に現在のユーザー自身のマイクロポストも一緒に取り出すこと。

![](https://railstutorial.jp/chapters/6.0/images/figures/user_feed.png)

どのようにフィードを実装するのかはまだ明確ではないが、テストについてはやや明確そうなので、まずはテストから書いていく

このテストで重要なことは、フィードに必要な３つの条件を満たすこと。

1）フォローしているユーザーのマイクロポストがフィードに含まれていること。
2）自分自身のマイクロポストもフィードに含まれていること。
3）フォローしていないユーザーのマイクロポストがフィードに含まれていないこと、の３つ

まずはMichaelがLanaをフォローしていて、Archerをフォローしていないという状況を作ってみる
この状況のMichaelのフィードでは、Lanaと自分自身の投稿が見えていて、Archerの投稿は見えないこと状況。

先ほどの３つの条件をアサーションに変換して、Userモデルにfeedメソッドがあることに注意しながら、更新したUserモデルに対するテストを書く

```test/models/user_test.rb
require 'test_helper'

class UserTest < ActiveSupport::TestCase
  .
  .
  .
  test "feed should have the right posts" do
    michael = users(:michael)
    archer  = users(:archer)
    lana    = users(:lana)
    # フォローしているユーザーの投稿を確認
    lana.microposts.each do |post_following|
      assert michael.feed.include?(post_following)
    end
    # 自分自身の投稿を確認
    michael.microposts.each do |post_self|
      assert michael.feed.include?(post_self)
    end
    # フォローしていないユーザーの投稿を確認
    archer.microposts.each do |post_unfollowed|
      assert_not michael.feed.include?(post_unfollowed)
    end
  end
end
```

テストは red になる

###演習

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

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

ステータスフィードに対する要件定義はテストで明確になったので（つまりこのテストにパスすれば良いので）、早速フィードの実装に着手する

最終的なフィードの実装はやや込み入っているため、細かい部品を1つずつ確かめながら導入していく。

最初に、このフィードで必要なクエリについて考える
ここで必要なのは、micropostsテーブルから、あるユーザー（つまり自分自身）がフォローしているユーザーに対応するidを持つマイクロポストをすべて選択（select）すること。このクエリを模式的に書くと次のようになる

```
SELECT * FROM microposts
WHERE user_id IN (<list of ids>) OR user_id = <user id>
```

上のコードを書く際に、SQLが`IN`というキーワードをサポートしていることを前提にしている。
このキーワードを使うことで、idの集合の内包（set inclusion）に対してテストを行える

13章のプロトフィードでは、上のような選択を行うために
Active Recordのwhereメソッドを使っていることを思い出す。

この時に選択すべき対象はシンプルで、
現在のユーザーに対応するユーザーidを持つマイクロポストを選択すればよかった。

```Ruby
Micropost.where("user_id = ?", id)
```

今回必要になる選択は、上よりも少し複雑で、例えば次のような形になる。

```Ruby
Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
```

これらの条件から、フォローされているユーザーに対応するidの配列が必要であることがわかってきた。
これを行う方法の1つは、Rubyの`mapメソッド`を使うこと

このメソッドはすべての「列挙可能（enumerable）」なオブジェクト（配列やハッシュなど、要素の集合で構成されるあらゆるオブジェクト）で使える。

他の例題として、mapメソッドを使って配列を文字列に変換すると

```Ruby
$ rails console
>> [1, 2, 3, 4].map { |i| i.to_s }
=> ["1", "2", "3", "4"]
```

上に示したような状況では、各要素に対して同じメソッドが実行される。
これはよく使われる方法であり、次のようにアンパサンド（Ampersand）& と、メソッドに対応するシンボルを使った短縮表記が使える。

この短縮表記であれば、変数iを使わずに済む

```Ruby
>> [1, 2, 3, 4].map(&:to_s)
=> ["1", "2", "3", "4"]
```

この結果に対してjoinメソッドを使うと、idの集合をカンマ区切りの文字列として繋げることができる

```Ruby
>> [1, 2, 3, 4].map(&:to_s).join(', ')
=> "1, 2, 3, 4"
```

上のコードを使えば、`user.following`にある各要素のidを呼び出し、フォローしているユーザーのidを配列として扱うことができる。

例えばデータベースの最初のユーザーに対して実行すると、次のような結果になる。

```Ruby
>> User.first.following.map(&:id)
=> [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
42, 43, 44, 45, 46, 47, 48, 49, 50, 51]
```

実際、この手法は実に便利なので、Active Recordでは次のようなメソッドも用意されている。

```Ruby
>> User.first.following_ids
=> [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
42, 43, 44, 45, 46, 47, 48, 49, 50, 51]
```

この`following_ids`メソッドは、`has_many :following`の関連付けをしたときにActive Recordが自動生成したもの

これにより、user.followingコレクションに対応するidを得るためには、関連付けの名前の末尾に_idsを付け足すだけで済む。

結果として、フォローしているユーザーidの文字列は、次のようにして取得することができる。

```Ruby
>> User.first.following_ids.join(', ')
=> "3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41,
42, 43, 44, 45, 46, 47, 48, 49, 50, 51
```

なお、以上は説明のためのコードであり、実際にSQL文字列に挿入するときは、このように記述する必要はない。

実は、`?`を内挿すると自動的にこの辺りの面倒を見てくれる。
さらに、データベースに依存する一部の非互換性まで解消してくれる。

つまり、ここでは`following_idsメソッド`をそのまま使えばよいだけ

結果、最初に想像していたとおり、

```
Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
```

というコードが無事に動く

```app/models/user.rb
class User < ApplicationRecord
  .
  .
  .
  # パスワード再設定の期限が切れている場合はtrueを返す
  def password_reset_expired?
    reset_sent_at < 2.hours.ago
  end

  # ユーザーのステータスフィードを返す
  def feed
    Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
  end

  # ユーザーをフォローする
  def follow(other_user)
    following << other_user
  end
  .
  .
  .
end
```

これでテストは green になるはず

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

```user.rb
  # ユーザーのステータスフィードを返す
  def feed
  # Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
    Micropost.where("user_id IN (?)", following_ids)#OR user_id = ?を削除
  end
```


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

```user.rb
  def feed
    Micropost.where("user_id = ?", id)#上の逆にする
    # Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
    # Micropost.where("user_id IN (?)", following_ids)
```


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

```user.rb
  def feed
    Micropost.all
    # Micropost.where("user_id = ?", id)
    # Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
    # Micropost.where("user_id IN (?)", following_ids)
```

###14.3.3 サブセレクト
上のフィードの実装は、投稿されたマイクロポストの数が膨大になったときにうまくスケールしない
つまり、フォローしているユーザーが5,000人程度になるとWebサービス全体が遅くなる可能性がある

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

今のコードの問題点は、following_idsでフォローしているすべてのユーザーをデータベースに問い合わせし、さらに、フォローしているユーザーの完全な配列を作るために再度データベースに問い合わせしている点

```user.rb
  # ユーザーのステータスフィードを返す
  def feed
    Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
  end
```

これは集合に内包されているかどうかだけしかチェックされていないため、この部分はもっと効率的なコードに置き換えられるはず。

また、SQLは本来このような集合の操作に最適化されています。実際、このような問題は、SQLのサブセレクト（subselect）を使うと解決できる

まずはフィードをリファクタリングする

```app/models/user.rb
class User < ApplicationRecord
  # ユーザーのステータスフィードを返す
  def feed
    Micropost.where("user_id IN (:following_ids) OR user_id = :user_id",
     following_ids: following_ids, user_id: id)
  end
```

上の実装では、これまでのコード

```Ruby
Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)#これまでのコード
↓↓
Micropost.where("user_id IN (:following_ids) OR user_id = :user_id",#書き換えたコード
    following_ids: following_ids, user_id: id)
```

前者の疑問符(?)を使った文法も便利だが、同じ変数を複数の場所に挿入したい場合は、後者の置き換え後の文法を使う方がより便利

これからSQLクエリにもう1つのuser_idを追加。
特に、次のRubyコードは、

```
following_ids
```

このようなSQLに置き換えることができる

```Ruby
following_ids = "SELECT followed_id FROM relationships
                 WHERE  follower_id = :user_id"
```

このコードをSQLのサブセレクトとして使う。
つまり、「ユーザー1がフォローしているユーザーすべてを選択する」というSQLを既存のSQLに内包させる形になり、結果としてSQLは次のようになる

```Ruby
SELECT * FROM microposts
WHERE user_id IN (SELECT followed_id FROM relationships
                  WHERE  follower_id = 1)
      OR user_id = 1
```

このサブセレクトは集合のロジックを（Railsではなく）データベース内に保存するので、より効率的にデータを取得することができる

これでもっと効率的なフィードを実装する準備ができた。
（ここに記述されているコードは生のSQLを表す文字列であり、following_idsという文字列はエスケープされているのではなく、見やすさのために式展開しているだけだという点に注意）

```app/models/user.rb
class User < ApplicationRecord

  # ユーザーのステータスフィードを返す
  def 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)
  end
```

このコードはRailsとRubyとSQLのコードが複雑に絡み合っていて厄介だがちゃんと動く

テストもパスする

もちろん、サブセレクトを使えばいくらでもスケールできるなどということはない。

大規模なWebサービスでは、バックグラウンド処理を使ってフィードを非同期で生成するなどのさらなる改善が必要
ただし、Webサービスをスケールさせる技術は非常に高度かつデリケートな問題なので、今回はここまでの改善で止めておく。

これでステータスフィードの実装は完了


この時点で、masterブランチに変更を取り込む準備ができた。

```
$ rails test
$ git add -A
$ git commit -m "Add user following"
$ git checkout master
$ git merge following-users
```

コードをリポジトリにpushして、本番環境にデプロイ

```
$ git push
$ git push heroku
$ heroku pg:reset DATABASE
$ heroku run rails db:migrate
$ heroku run rails db:seed
```

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

```test/integration/following_test.rb
 test "feed on Home page" do
    get root_path
    @user.feed.paginate(page: 1).each do |micropost|
      assert_match CGI.escapeHTML(micropost.content), response.body
    end
  end
```

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

```test/integration/following_test.rb
test "feed on Home page" do
    get root_path
    @user.feed.paginate(page: 1).each do |micropost|
      # assert_match CGI.escapeHTML(micropost.content), response.body
      assert_match micropost.content, response.body
    end
  end
```

```
 I&#39;m sorry. Your words made sense, but your sarcastic tone did not.\n    \n  </span>\n  <span class=\"timestamp\">\n    Posted 11 minutes ago.\n  </span>\n</li><li id=\"micropost-499495288\">\n  <a href=\"/users/762146111\"><img alt=\"Michael Example\" class=\"gravatar\" 
```

いろいろ文字がエスケープされずおかしなことになる

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

```app/models/user.rb
class User < ApplicationRecord
  .

  # ユーザーのステータスフィードを返す
  def feed
    part_of_feed = "relationships.follower_id = :id or microposts.user_id = :id"
    Micropost.joins(user: :followers).where(part_of_feed, { id: id })
  end
  .
  .
  .
end
```

テストはパスするし、

```Ruby
irb(main):001:0> User.first.feed
   (1.3ms)  SELECT sqlite_version(*)
  User Load (1.3ms)  SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT ?  [["LIMIT", 1]]
  Micropost Load (5.4ms)  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]]
=> #<ActiveRecord::Relation [#<Micropost id: 304, content: "aaa", user_id: 3, created_at: "2021-03-10 01:58:23", updated_at: "2021-03-10 01:58:23">, #<Micropost id: 303, content: "aaa", user_id: 3, created_at: "2021-03-09 23:48:59", updated_at: "2021-03-09 23:48:59">, #<Micropost id: 302, content: "jhjkhkj", user_id: 3, created_at: "2021-03-09 02:53:02", updated_at: "2021-03-09 02:53:02">, #<Micropost id: 301, content: "aaaaaaaaaaaaaaaaaaaaaaa", user_id: 3, created_at: "2021-03-09 02:03:51", updated_at: "2021-03-09 02:03:51">, #<Micropost id: 300, content: "Qui laboriosam fuga et eveniet.", user_id: 6, created_at: "2021-03-06 00:52:26", updated_at: "2021-03-06 00:52:26">, #<Micropost id: 299, content: "Qui laboriosam fuga et eveniet.", user_id: 5, created_at: "2021-03-06 00:52:26", updated_at: "2021-03-06 00:52:26">, #<Micropost id: 298, content: "Qui laboriosam fuga et eveniet.", user_id: 4, created_at: "2021-03-06 00:52:26", updated_at: "2021-03-06 00:52:26">, #<Micropost id: 297, content: "Qui laboriosam fuga et eveniet.", user_id: 3, created_at: "2021-03-06 00:52:26", updated_at: "2021-03-06 00:52:26">, #<Micropost id: 296, content: "Qui laboriosam fuga et eveniet.", user_id: 2, created_at: "2021-03-06 00:52:26", updated_at: "2021-03-06 00:52:26">, #<Micropost id: 294, content: "Dolorum ducimus libero autem non.", user_id: 6, created_at: "2021-03-06 00:52:26", updated_at: "2021-03-06 00:52:26">, ...]>
```

完走！！お疲れ様でした！！


あとは、独自で機能を拡張するって感じです。

- Ruby/Railsのバージョンを上げてみよう
- 返信機能
- メッセージ機能
- フォロワーの通知
- RSSフィード
- REST API
- 検索機能
- 他の拡張機能
- 上記の他にも、「いいね機能」「シェア機能」「minitestの代わりにRSpecで書き直す」「erbの代わりにHamlで書き直す」「エラーメッセージをI18nで日本語化する」「オートコンプリート機能」といったアイデア

個人的にはまず自分が発生しているエラーを直してから、「いいね機能」とバージョンをあげることにトライしてみようかなと思います