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

#この章でやること
マイクロポスト（呟き）を作れるようにする


これまでユーザー、セッション、アカウント有効化、パスワードリセットという４つのリソースについて見きた。
そして、これらのうち「ユーザー」というリソースだけが、Active Recordによってデータベース上のテーブルと紐付いている。

全ての準備が整った今、ユーザーが短いメッセージを投稿できるようにするためのリソース「マイクロポスト」を追加していく

この章では、Micropostデータモデルを作成し、Userモデルと`has_many`および`belongs_to`メソッドを使って関連付けを行い、さらに、結果を処理し表示するために必要なフォームとその部品を作成する

##13.1 Micropostモデル

Micropostリソースの最も本質的な部分を表現するMicropostモデルを作成するところから始める


トピックブランチを作成

```
$ git checkout -b user-microposts
```

###13.1.1 基本的なモデル
Micropostモデルは、マイクロポストの内容を保存するcontent属性と、特定のユーザーとマイクロポストを関連付けるuser_id属性の2つの属性だけを持つ

・content属性
・user_id

content属性＝マイクロポストの投稿にString型ではなくText型を使う
Text型はある程度の量のテキストを格納するときに使われる型。
String型でも255文字までは格納できるため、この型でもOKだが、Text型の方が表現豊かなマイクロポストを実現できる

Railsのgenerate modelコマンドを使ってMicropostモデルを生成

```terminal
$ rails generate model Micropost content:text user:references
```

ApplicationRecordを継承したモデルMicropostモデルが生成される。
ただし、今回は`user:references`という引数も含めていたため生成されたモデルの中に、ユーザーと１対１の関係であることを表すbelongs_toのコードも追加されている

```app/models/micropost.rb
class Micropost < ApplicationRecord
   belongs_to :user
end
```

データベースにusersテーブルを作るマイグレーションを生成したときと同様に、このgenerateコマンドはmicropostsテーブルを作成するためのマイグレーションファイルを生成

Userモデルとの最大の違いはreferences型を利用している点。
これを利用すると、自動的にインデックスと外部キー参照付きのuser_idカラムが追加され、UserとMicropostを関連付けする下準備をしてくれる
Userモデルのときと同じで、Micropostモデルのマイグレーションファイルでもt.timestampsという行（マジックカラム）が自動的に生成され、これによりcreated_atとupdated_atというカラムが自動で追加される

```db/migrate/[timestamp]_create_microposts.rb
class CreateMicroposts < ActiveRecord::Migration[6.0]
  def change
    create_table :microposts do |t|
      t.text :content
      t.references :user, foreign_key: true

      t.timestamps
    end
    add_index :microposts, [:user_id, :created_at]#indexあり
  end
end
```

user_idとcreated_atカラムにインデックスが付与されていることに注目。
こうすることで、user_idに関連付けられたすべてのマイクロポストを作成時刻の逆順で取り出しやすくなる

また、user_idとcreated_atの両方を１つの配列に含めている点にも注目
こうすることでActive Recordは、両方のキーを同時に扱う複合キーインデックス（Multiple Key Index）作成する

※複合キーインデックスとは
https://qiita.com/towtow/items/4089dad004b7c25985e3
こちらを参考になんとなく理解しました

データベースを更新

```
$ rails db:migrate
```

###演習
1 RailsコンソールでMicropost.newを実行し、インスタンスを変数micropostに代入してください。その後、user_idに最初のユーザーのidを、contentに "Lorem ipsum" をそれぞれ代入してみてください。この時点では、 micropostオブジェクトのマジックカラム（created_atとupdated_at）には何が入っているでしょうか?

2 先ほど作ったオブジェクトを使って、micropost.userを実行してみましょう。どのような結果が返ってくるでしょうか? また、micropost.user.nameを実行した場合の結果はどうなるでしょうか?
先ほど作ったmicropostオブジェクトをデータベースに保存してみましょう。この時点でもう一度マジックカラムの内容を調べてみましょう。今度はどのような値が入っているでしょうか?

micropost=Micropost.newを作る時に何度もName_errorとなってしまい、30分ぐらいエラー処理に奔走したが、結局原因不明。
VSCODEを再起動したらできるようになった（ローカル環境おかしい...）

```Ruby
#1
irb(main):001:0> micropost=Micropost.new
=> #<Micropost id: nil, content: nil, user_id: nil, created_at: nil, updated_at: ...
irb(main):002:0> micropost.user_id = User.first.id[["LIMIT", 1]]
irb(main):003:0> micropost.content = "Lorem ipsum"
=> "Lorem ipsum"
irb(main):004:0> micropost.created_at
=> nil
irb(main):005:0> micropost.updated_at
=> nil
#2
irb(main):006:0> micropost.user
 1], ["LIMIT", 1]] "2021-02-24 22:24:02", updated_at: "2021-02-24 22:24:02", password_digest: [FILTERED], remember_digest: nil, admin: true, activation_digest: "$2a$12$/bhC1QYO9ClKaNARa9RuPeFsxs3xEgqvwuZbfq7uCW/...", activated: true, activated_at: "2021-02-24 22:24:01", reset_digest: nil, reset_sent_at: nil>
irb(main):007:0> micropost.user.name
=> "Example User"
irb(main):008:0> micropost.save
   (0.2ms)  begin transaction
  Micropost Create (6.9ms)  INSERT INTO "microposts" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["content", "Lorem ipsum"], ["user_id", 1], ["created_at", "2021-02-26 23:49:47.041348"], ["updated_at", "2021-02-26 23:49:47.041348"]]
   (1.4ms)  commit transaction
=> true
irb(main):009:0> micropost.created_at
=> Fri, 26 Feb 2021 23:49:47 UTC +00:00
irb(main):010:0> micropost.updated_at
=> Fri, 26 Feb 2021 23:49:47 UTC +00:00
```

###13.1.2 Micropostのバリデーション

基本的なモデルを作成したので、次に要求される制限を実現するためのバリデーションを追加していく。
Micropostモデルを作成したときに、マイクロポストは投稿したユーザーのid（user_id）を持たせるようにした。
これを使って、慣習的に正しくActive Recordの関連付けを実装していくが、
まずはMicropostモデル単体を（テスト駆動開発で）動くようにしてみる

Micropostの初期テストはUserモデルの初期テストと似ている。

そのステップは

- setupのステップで、fixtureのサンプルユーザーと紐付けた新しいマイクロポストを作成
- 作成したマイクロポストが有効かどうかチェック
- あらゆるマイクロポストはユーザーのidを持っているべきなので、user_idの存在性のバリデーションに対するテスト
 
このステップで追加する。

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

class MicropostTest < ActiveSupport::TestCase

  def setup
    @user = users(:michael)
    # このコードは慣習的に正しくない
    @micropost = Micropost.new(content: "Lorem ipsum", user_id: @user.id)
  end

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

  test "user id should be present" do
    @micropost.user_id = nil
    assert_not @micropost.valid?
  end
end
```

setupメソッドの中でコメントしているとおり、マイクロポストを作成するコードは動きますが、慣習的には正しくないので、後で修正する

元々あるUserモデルのテストと同じで、
１つ目のテスト（should be valid）では、現実に即しているかどうかをテスト（reality check）ている。
２つ目のテスト(user id should be present)では、user_idが存在しているかどうか（nilではないか）をテストしている。

このテストをパスさせるために、`micropost.rb`で存在性のバリデーションを追加してみる。

```app/models/micropost.rb
class Micropost < ApplicationRecord
  belongs_to :user
  validates :user_id, presence: true
end
```

Rails5では上記のバリデーションを追加していなくてもテストが成功する。
しかし、これは上記のmicropost_newのコードを書いた場合のみ発生する。

この部分を、慣習的に正しいコードを書くと、user_idに対する存在性のバリデーションが期待通りに動く。

以上の背景を踏まえると、今の所テストは成功する。


次に、マイクロポストの`content属性`に対するバリデーションを追加する
user_id属性と同様に、content属性も存在する必要があり、さらにマイクロポストが140文字より長くならないよう制限を加える

テスト駆動開発でMicropostモデルのバリデーションを追加する

```test/models/micropost_test.rb

test "content should be present" do
    @micropost.content = "   "#contentが空
    assert_not @micropost.valid?#有効にならないか
  end

  test "content should be at most 140 characters" do
    @micropost.content = "a" * 141#中身が141文字
    assert_not @micropost.valid?#有効にならないか
  end
end
```

```app/models/micropost.rb
class Micropost < ApplicationRecord
  belongs_to :user
  validates :user_id, presence: true
  validates :content, presence: true, length: { maximum: 140 }
end
```

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


###演習
Railsコンソールを開き、user_idとcontentが空になっているmicropostオブジェクトを作ってみてください。このオブジェクトに対してvalid?を実行すると、失敗することを確認してみましょう。また、生成されたエラーメッセージにはどんな内容が書かれているでしょうか?

コンソールを開き、今度はuser_idが空でcontentが141文字以上のmicropostオブジェクトを作ってみてください。このオブジェクトに対してvalid?を実行すると、失敗することを確認してみましょう。また、生成されたエラーメッセージにはどんな内容が書かれているでしょうか?

```Ruby
#1　空文字は有効にならない
irb(main):001:0> micropost2=Micropost.new
=> #<Micropost id: nil, content: nil, user_id: nil, created_at: nil, updated_at: ...
irb(main):002:0> micropost2.user_id=""
=> ""
irb(main):003:0> micropost2.content=""
=> ""
irb(main):004:0> micropost2.valid?
=> false
irb(main):006:0> micropost2.errors.full_messages
=> ["User must exist", "User can't be blank", "Content can't be blank"]
#2 文字数が多いと有効にならない
irb(main):007:0> micropost3=Micropost.new(user_id: "",content: "a" * 141)
=> #<Micropost id: nil, content: "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa...
irb(main):008:0> micropost3.valid?
=> false
irb(main):009:0> micropost3.errors.full_messages
=> ["User must exist", "User can't be blank", "Content is too long (maximum is 140 characters)"]
```

###13.1.3 User/Micropostの関連付け

Webアプリケーション用のデータモデルを構築するにあたって、個々のモデル間での関連付けを十分考えておくことが重要
今回の場合,それぞれのマイクロポストは１人のユーザーと関連付けられ、それぞれのユーザーは（潜在的に）複数のマイクロポストと関連付けられる
これらの関連付けを実装するための一環として、Micropostモデルに対するテストを作成し、さらにUserモデルにいくつかのテストを追加していく

この節で定義する`belongs_to`/`has_many`関連付けを使うことで
以下メソッドをRailsで使えるようになる

|メソッド	|用途|
|:--:|:--|
|micropost.user	|Micropostに紐付いたUserオブジェクトを返す
|user.microposts	|Userのマイクロポストの集合をかえす
|user.microposts.create(arg)	|userに紐付いたマイクロポストを作成する
|user.microposts.create!(arg)	|userに紐付いたマイクロポストを作成する（失敗時に例外を発生）
|user.microposts.build(arg)	|userに紐付いた新しいMicropostオブジェクトを返す
|user.microposts.find_by(id: 1)	|userに紐付いていて、idが1であるマイクロポストを検索する

上記メソッドは

```
Micropost.create
Micropost.create!
Micropost.new
```

このメソッドになっていることに注意してください。

```
user.microposts.create
user.microposts.create!
user.microposts.build
```

これらのメソッドは使うと、紐付いているユーザーを通してマイクロポストを作成することができる（慣習的に正しい方法）。
新規のマイクロポストがこの方法で作成される場合、user_idは自動的に正しい値に設定される

この方法を使うと、例えば次のような

```
@user = users(:michael)
# このコードは慣習的に正しくない
@micropost = Micropost.new(content: "Lorem ipsum", user_id: @user.id)
```

上のコードが以下に書き換えられる。

```
@user = users(:michael)
@micropost = @user.microposts.build(content: "Lorem ipsum")
```

（newメソッドと同様に、buildメソッドはオブジェクトを返しますがデータベースには反映されない。）
一度正しい関連付けを定義してしまえば、@micropost変数のuser_idには、関連するユーザーのidが自動的に設定される。

`@user.microposts.build`のようなコードを使うためには、 UserモデルとMicropostモデルをそれぞれ更新して、関連付ける必要がある
Micropostモデルの方では、belongs_to :userというコードが必要だが、既に自動的に生成されている。
一方、Userモデルの方では、has_many :micropostsと追加する必要がある

```app/models/user.rb
class User < ApplicationRecord
  has_many :microposts
  .
  .
  .
end
```

正しく関連付けができたら、setupメソッドを修正して、慣習的に正しくマイクロポストを作成してみる

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

class MicropostTest < ActiveSupport::TestCase

  def setup
    @user = users(:michael)
    @micropost = @user.microposts.build(content: "Lorem ipsum")
  end

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

  test "user id should be present" do
    @micropost.user_id = nil
    assert_not @micropost.valid?
  end
  .
  .
  .
end
```

些細なリファクタリングなので、テストはパスする

###演習
1 データベースにいる最初のユーザーを変数userに代入してください。そのuserオブジェクトを使ってmicropost = user.microposts.create(content: "Lorem ipsum")を実行すると、どのような結果が得られるでしょうか?

```Ruby
irb(main):001:0> user=User.first
=> #<User id: 1, name: "Example User", email: "example@railstutorial.org", create...
irb(main):002:0> micropost = user.microposts.create(content: "Lorem ipsum")
=> #<Micropost id: 302, content: "Lorem ipsum", user_id: 1, created_at: "2021-03-...
```

2 先ほどの演習課題で、データベース上に新しいマイクロポストが追加されたはずです。user.microposts.find(micropost.id)を実行して、本当に追加されたのかを確かめてみましょう。また、先ほど実行したmicropost.idの部分をmicropostに変更すると、結果はどうなるでしょうか?

```Ruby
irb(main):003:0> user.microposts.find(micropost.id)
=> #<Micropost id: 302, content: "Lorem ipsum", user_id: 1, created_at: "2021-03-02 07:24:15", updated_at: "2021-03-02 07:24:15">
irb(main):005:0> user.microposts.find(micropost)
Traceback (most recent call last):
        2: from (irb):4
        1: from (irb):5:in `rescue in irb_binding'
ArgumentError (You are passing an instance of ActiveRecord::Base to `find`. Please pass the id of the object by calling `.id`.)
```

引数エラーになる


3 user == micropost.userを実行した結果はどうなるでしょうか? また、user.microposts.first == micropost を実行した結果はどうなるでしょうか? それぞれ確認してみてください。

```Ruby
irb(main):006:0> user == micropost.user
=> true
irb(main):007:0> user.microposts.first == micropost
  Micropost Load (0.8ms)  SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ?  [["user_id", 1], ["LIMIT", 1]]
=> true
```


###13.1.4 マイクロポストを改良する
この項では、UserとMicropostの関連付けを改良していく。

具体的には、ユーザーのマイクロポストを特定の順序で取得できるようにしたり、マイクロポストをユーザーに依存させて、ユーザーが削除されたらマイクロポストも自動的に削除されるようにしてく

####デフォルトのスコープ
user.micropostsメソッドはデフォルトでは読み出しの順序に対して何も保証しないが、 ブログやTwitterの慣習に従って、作成時間の逆順、つまり最も新しいマイクロポストを最初に表示するようにしてみる

これを実装するためには、`default scope`というテクニックを使う

この機能のテストは、「アプリケーション側の実装が本当は間違っているのにテストが成功してしまう」という罠がある。
正しいテストを書くために、ここではテスト駆動開発で進めていく

具体的には、まずデータベース上の最初のマイクロポストが、fixture内のマイクロポスト（most_recent）と同じであるか検証するテストを書いてく


```test/models/micropost_test.rb

  test "order should be most recent first" do
    assert_equal microposts(:most_recent), Micropost.first #最新のpostがPostの最初か
  end
end
```

マイクロポストのfixtureがあるという前提のテストなので、fixtureを定義する

前に定義したcontent属性の他に、それに関連付けられたuser属性も定義する必要がある
Railsには、こうした関連付けをfixtureで構築する次のような便利な方法が備わっている。


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

userをmichaelと認識させると、このマイクロポストを以下のユーザーfixtureにある対応ユーザーに関連付けることをRailsに指示できる。

```
michael:
  name: Michael Example
  email: michael@example.com
  .
  .
  .
```

マイクロポストの完全なfixutre

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

tau_manifesto:
  content: "Check out the @tauday site by @mhartl: https://tauday.com"
  created_at: <%= 3.years.ago %>
  user: michael

cat_video:
  content: "Sad cats are sad: https://youtu.be/PKffm2uI4dk"
  created_at: <%= 2.hours.ago %>
  user: michael

most_recent:
  content: "Writing a short test"
  created_at: <%= Time.zone.now %>
  user: michael
```


上のコードは埋め込みRubyを使ってcreated_atカラムに値を明示的にセットしている点に注目。
created_atカラムはRailsによって自動的に更新されるため基本的には手動で更新できないが、fixtureファイルの中では更新可能になっている

この時点でテストは通らない

```
$ rails test test/models/micropost_test.rb
```

次に、Railsの`default_scope`メソッドを使ってこのテストを成功させる。
`default_scope`メソッドは、データベースから要素を取得したときの、デフォルトの順序を指定するメソッド
特定の順序にしたい場合は、default_scopeの引数にorderを与える

例えば、created_atカラムの順にしたい場合

`order(:created_at)`

ただし、デフォルトの順序が昇順（ascending）なっているので、このままでは数の小さい値から大きい値にソートされてしまう（最も古い投稿が最初に表示されてしまう）。順序を逆にしたい場合は、次のように生のSQLを引数に与える必要がある

`order('created_at DESC')`

DESCとは、SQLの降順（descending）指す

したがって、これで新しい投稿から古い投稿の順に並ぶ

バージョンが新しいRailsでは上のコードはこのようにも書ける
`order(created_at: :desc)`

このコードを使ってMicropostモデルを更新した結果

```app/models/micropost.rb
class Micropost < ApplicationRecord
  belongs_to :user
  default_scope -> { order(created_at: :desc) }#降順
  validates :user_id, presence: true
  validates :content, presence: true, length: { maximum: 140 }
end
```

上のコードはラムダ式（Stabby lambda）という文法を使っている
これは、Procやlambda（もしくは無名関数）と呼ばれるオブジェクトを作成する文法で,->というラムダ式は、ブロック`{}`を引数に取り、Procオブジェクトを返す。
このオブジェクトは、callメソッドが呼ばれたとき、ブロック内の処理を評価する。コンソールで確かめる

```Ruby
>> -> { puts "foo" }
=> #<Proc:0x007fab938d0108@(irb):1 (lambda)>
>> -> { puts "foo" }.call
foo
=> nil
```

テストはパスする

default scopeの使い方
https://qiita.com/qsona/items/2ca522675b27ed2ec5ba


####Dependent: destroy
今度はマイクロポストに第二の要素を追加する
サイト管理者はユーザーを破棄する権限を持つ。
ユーザーが破棄された場合、ユーザーのマイクロポストも同様に破棄されるべき

この振る舞いは、`has_manyメソッド`にオプションを渡してあげることで実装できる


```app/models/user.rb
class User < ApplicationRecord
  has_many :microposts, dependent: :destroy
end
```

`dependent: :destroy`というオプションを使うと、ユーザーが削除されたときに、そのユーザーに紐付いた（そのユーザーが投稿した）マイクロポストも一緒に削除されるようになる。
これは、管理者がシステムからユーザーを削除したとき、持ち主の存在しないマイクロポストがデータベースに取り残されてしまう問題を防ぐ。

次に、上にの文が正しく動くかどうか、テストを使ってUserモデルを検証していく
このテストでは、（idを紐づけるための）ユーザーを作成することと、そのユーザーに紐付いたマイクロポストを作成する必要がある
その後、ユーザーを削除してみて、マイクロポストの数が1つ減っているかどうかを確認

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

class UserTest < ActiveSupport::TestCase

  def setup
    @user = User.new(name: "Example User", email: "user@example.com",
                     password: "foobar", password_confirmation: "foobar")
  end

 test "associated microposts should be destroyed" do
    @user.save#@userをDBへ登録
    @user.microposts.create!(content: "Lorem ipsum")#contentを作成
    assert_difference 'Micropost.count', -1 do#userが消えたらpostも1減るか
      @user.destroy
    end
  end
end
```

テストはパスする

###演習
１　Micropost.first.created_atの実行結果と、Micropost.last.created_atの実行結果を比べてみましょう。

```Ruby
irb(main):001:0> Micropost.first.created_at
=> Sat, 06 Mar 2021 00:52:26 UTC +00:00
irb(main):002:0> Micropost.last.created_at
=> Sat, 06 Mar 2021 00:52:24 UTC +00:00
```

2 Micropost.firstを実行したときに発行されるSQL文はどうなっているでしょうか? 同様にして、Micropost.lastの場合はどうなっているでしょうか? ヒント: それぞれをコンソール上で実行したときに表示される文字列が、SQL文になります。

```Ruby
irb(main):003:0> Micropost.first
  Micropost Load (0.5ms)  SELECT "microposts".* FROM "microposts" ORDER BY "microposts"."created_at" DESC LIMIT ?  [["LIMIT", 1]]
=> #<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">
irb(main):004:0> Micropost.last
  Micropost Load (0.3ms)  SELECT "microposts".* FROM "microposts" ORDER BY "microposts"."created_at" ASC LIMIT ?  [["LIMIT", 1]]
=> #<Micropost id: 1, content: "Qui id qui vero velit.", user_id: 1, created_at: "2021-03-06 00:52:24", updated_at: "2021-03-06 00:52:24">
```

firstの時にASC(昇順)で取り出して、lastの時はDESC(降順)で取り出している。

3 データベース上の最初のユーザーを変数userに代入してください。そのuserオブジェクトが最初に投稿したマイクロポストのidはいくつでしょうか? 次に、destroyメソッドを使ってそのuserオブジェクトを削除してみてください。削除すると、そのuserに紐付いていたマイクロポストも削除されていることをMicropost.findで確認してみましょう。

```Ruby
irb(main):005:0> user = User.first
=> #<User id: 1, name: "Example User", email: "example@railstutorial.org", create...
irb(main):006:0> user.microposts.first
=> #<Micropost id: 295, content: "Qui laboriosam fuga et eveniet.", user_id: 1, created_at: "2021-03-06 00:52:26", updated_at: "2021-03-06 00:52:26">
irb(main):006:0> user_1 = user.microposts.first
=> #<Micropost id: 295, content: "Qui laboriosam fuga et eveniet.", user_id: 1, c...
irb(main):008:0> user_1.destroy
   (0.2ms)  begin transaction
  ActiveStorage::Attachment Load (1.8ms)  SELECT "active_storage_attachments".* FROM "active_storage_attachments" WHERE "active_storage_attachments"."record_id" = ? AND "active_storage_attachments"."record_type" = ? AND "active_storage_attachments"."name" = ? LIMIT ?  [["record_id", 295], ["record_type", "Micropost"], ["name", "image"], ["LIMIT", 1]]
  Micropost Destroy (3.7ms)  DELETE FROM "microposts" WHERE "microposts"."id" = ?  [["id", 295]]
   (1.5ms)  commit transaction
=> #<Micropost id: 295, content: "Qui laboriosam fuga et eveniet.", user_id: 1, created_at: "2021-03-06 00:52:26", updated_at: "2021-03-06 00:52:26">
irb(main):009:0> Micropost.find(1)
  Micropost Load (0.6ms)  SELECT "microposts".* FROM "microposts" WHERE "microposts"."id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<Micropost id: 1, content: "Qui id qui vero velit.", user_id: 1, created_at: "2021-03-06 00:52:24", updated_at: "2021-03-06 00:52:24">
```



##13.2 マイクロポストを表示する
Web経由でマイクロポストを作成する方法は現時点ではないが
1　マイクロポストを表示することと、2　テストすることならできる。

ここでは、Twitterのような独立したマイクロポストのindexページは作らずに、ユーザーのshowページで直接マイクロポストを表示させる
ユーザープロフィールにマイクロポストを表示させるため、最初にシンプルなERbテンプレートを作成する
サンプルデータ生成タスクにマイクロポストのサンプルを追加して、画面にサンプルデータが表示されるようにしていく

加えてユーザーのプロフィール画面（show.html.erb）でそのユーザーのマイクロポストを表示させたり、これまでに投稿した総数も表示させたりしていく。

まずは、Micropostのコントローラとビューを作成するために、コントローラを生成。
なお、今回使うのはビューだけで、Micropostsコントローラは後で使う

```
$ rails generate controller Microposts
```

今回の目的は、ユーザー毎にすべてのマイクロポストを描画できるようにする
以前みたコードでは

```
<ul class="users">
  <%= render @users %>
</ul>
```

このコードは`_user.html.erbパーシャル`を使って自動的に@users変数内のそれぞれのユーザーを出力していた。
これを参考に、`_micropost.html.erbパーシャル`を使ってマイクロポストのコレクションを表示しようとすると、次のようになる。

```
<ol class="microposts">
  <%= render @microposts %>
</ol>
```

順序付きリストのolタグを使っているのは、マイクロポストが特定の順序（新しい→古い）に依存しているため。
次に、対応する`_micropost.html.erbパーシャル`

```app/views/microposts/_micropost.html.erb
<li id="micropost-<%= micropost.id %>">
  <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
  <span class="user"><%= link_to micropost.user.name, micropost.user %></span>
  <span class="content"><%= micropost.content %></span>
  <span class="timestamp">
    Posted <%= time_ago_in_words(micropost.created_at) %> ago.
  </span>
</li>
```

上のコードでは`time_ago_in_words`ヘルパーメソッドを使ってる。
メソッド名の表すとおり「3分前に投稿」といった文字列を出力する。
具体的な効果について後述する

新たなクラスにも後にCSSを振っていく

```
<li id="micropost-<%= micropost.id %>">
```

これは一般的に良いとされる慣習で、例えば将来、JavaScriptを使って各マイクロポストを操作したくなったときなどに役立つ。

ここまでが上のコードの解説


次は、一度にすべてのマイクロポストが表示されてしまう潜在的問題に対処する。usersインデックスページでは、ページネーションを使ったが、今回もそれを利用する

```
<%= will_paginate @microposts %>
```

ユーザー一覧画面のコードと比較すると、少し異なる。

```
<%= will_paginate %>
```

上のコードは引数なしで動作していた。
これは`will_paginate`が、Usersコントローラのコンテキストにおいて、`@usersインスタンス変数`が存在していることを前提としているため。このインスタンス変数は、`ActiveRecord::Relation`クラスのインスタンス。

今回の場合はUsersコントローラのコンテキストからマイクロポストをページネーションしたいため（つまりコンテキストが異なるため）、明示的に`@microposts`変数を`will_paginate`に渡す必要がある。
したがって、そのようなインスタンス変数(@microposts)をUsersコントローラのshowアクションで定義しなければいけない

```app/controllers/users_controller.rb
class UsersController < ApplicationController
  def show
    @user = User.find(params[:id])
    @microposts = @user.microposts.paginate(page: params[:page])  # pageを受け取ってmicropostsに渡して表示する。
  end
```

paginateメソッドに注目。
マイクロポストの関連付けを経由してmicropostsテーブルに到達し、必要なマイクロポストのページを引き出してくれる。

最後の課題はマイクロポストの投稿数を表示すること。これはcountメソッドを使うことで解決できる。

```
user.microposts.count
```

paginateと同様に、関連付けをとおしてcountメソッドを呼び出すことができる。
countメソッドではデータベース上のマイクロポストを全部読みだしてから結果の配列に対してlengthを呼ぶ、といった無駄な処理はしていない
データベースに代わりに計算してもらい、特定のuser_idに紐付いたマイクロポストの数をデータベースに問い合わせている。
（それでもcountメソッドがアプリケーションのボトルネックになるようなことがあれば、さらに高速なcounter cacheを使うこともできる）

これですべての要素が揃ったので、プロフィール画面にマイクロポストを表示させてみる

```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>
  </aside>
  <div class="col-md-8">
    <% if @user.microposts.any? %>
      <h3>Microposts (<%= @user.microposts.count %>)</h3>
      <ol class="microposts">
        <%= render @microposts %>
      </ol>
      <%= will_paginate @microposts %>
    <% end %>
  </div>
</div>
```

###演習
1 7.3.3で軽く説明したように、今回ヘルパーメソッドとして使ったtime_ago_in_wordsメソッドは、Railsコンソールのhelperオブジェクトから呼び出すことができます。このhelperオブジェクトのtime_ago_in_wordsメソッドを使って、3.weeks.agoや6.months.agoを実行してみましょう。

```Ruby
irb(main):010:0> helper.time_ago_in_words(3.weeks.ago)
=> "21 days"
irb(main):011:0> helper.time_ago_in_words(6.months.ago)
=> "6 months"
```

2 helper.time_ago_in_words(1.year.ago)と実行すると、どういった結果が返ってくるでしょうか?

```Ruby
irb(main):012:0> helper.time_ago_in_words(1.year.ago)
=> "about 1 year"
```

3  micropostsオブジェクトのクラスは何でしょうか? ヒント: リスト 13.23内のコードにあるように、まずはpaginateメソッド（引数はpage: nil）でオブジェクトを取得し、その後classメソッドを呼び出してみましょう。

```Ruby
irb(main):013:0> user = User.find(1)
  User Load (33.5ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
=> #<User id: 1, name: "Example User", email: "example@railstutorial.org", create...
irb(main):014:0> microposts = user.microposts.paginate(page: nil)
  Micropost Load (2.6ms)  SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ? OFFSET ?  [["user_id", 1], ["LIMIT", 11], ["OFFSET", 0]]
   (0.4ms)  SELECT COUNT(*) FROM "microposts" WHERE "microposts"."user_id" = ?  [["user_id", 1]]
=> #<ActiveRecord::AssociationRelation [#<Micropost id: 289, content: "Dolorum du...
irb(main):015:0> microposts.class
=> Micropost::ActiveRecord_AssociationRelation
```

###13.2.2 マイクロポストのサンプル
サンプルデータ生成タスクにマイクロポストも追加する

すべてのユーザーにマイクロポストを追加しようとすると時間が掛かり過ぎるので、takeメソッドを使って最初の6人だけに追加。
※takeメソッドについては1.1.2参照
https://railsguides.jp/active_record_querying.html

>takeメソッドはレコードを1つ取り出します。どのレコードが取り出されるかは指定されません。
>takeメソッドで返すレコードの最大数を数値の引数で指定することもできます

```
User.order(:created_at).take(6)
（このとき、orderメソッドを経由することで、作成されたユーザーの最初の6人を明示的に呼び出すようにしている）
```

この6人については、1ページの表示限界数（30）を越えさせるために、それぞれ50個分のマイクロポストを追加するようにしていく。
また、各投稿内容は、`Faker gem`に`Lorem.sentence`という便利なメソッドがあるため、利用する。（自動的にランダムのPostを作ってくれる）

```db/seeds.rb
.
# ユーザーの一部を対象にマイクロポストを生成する
users = User.order(:created_at).take(6)
50.times do
  content = Faker::Lorem.sentence(word_count: 5)
  users.each { |user| user.microposts.create!(content: content) }
end
```

ここで、いつものように開発環境用のデータベースで再度サンプルデータを生成

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

生成し終わったら、Railsサーバーを一度落として、起動し直す

CSSを追加

```app/assets/stylesheets/custom.scss
.
.
.
/* microposts */

.microposts {
  list-style: none;
  padding: 0;
  li {
    padding: 10px 0;
    border-top: 1px solid #e8e8e8;
  }
  .user {
    margin-top: 5em;
    padding-top: 0;
  }
  .content {
    display: block;
    margin-left: 60px;
    img {
      display: block;
      padding: 5px 0;
    }
  }
  .timestamp {
    color: $gray-light;
    display: block;
    margin-left: 60px;
  }
  .gravatar {
    float: left;
    margin-right: 10px;
    margin-top: 5px;
  }
}

aside {
  textarea {
    height: 100px;
    margin-bottom: 5px;
  }
}

span.image {
  margin-top: 10px;
  input {
    border: 0;
  }
}
```

###演習
1 (1..10).to_a.take(6)というコードの実行結果を推測できますか? 推測した値が合っているかどうか、実際にコンソールを使って確認してみましょう。
予想：6までの数字を取り出す{1,2,3,4,5,6}

```Ruby
irb(main):016:0> (1..10).to_a.take(6)
=> [1, 2, 3, 4, 5, 6]
```

2 先ほどの演習にあったto_aメソッドの部分は本当に必要でしょうか? 確かめてみてください。

```Ruby
irb(main):017:0> (1..10).take(6)
=> [1, 2, 3, 4, 5, 6]# 不要だった
```

3Fakerはlorem ipsum以外にも、非常に多種多様の事例に対応しています。Fakerのドキュメント（英語）を眺めながら画面に出力する方法を学び、実際に架空の大学名やHipster IpsumやChuck Norris facts（参考: チャック・ノリスの真実）を画面に出力してみましょう。（訳注: もちろん日本語にも対応していて、例えば沖縄らしい用語を出力するfaker-okinawaもあります。ぜひ遊んでみてください。）
https://github.com/faker-ruby/faker

```Ruby
irb(main):019:0> Faker::Games::Pokemon.name#ポケモンの名前をランダムに出す
=> "Gyarados"
irb(main):021:0> Faker::Games::Pokemon.move#ポケモンの技を出す
=> "Flamethrower"
irb(main):022:0> Faker::JapaneseMedia::OnePiece.character#ワンピースのキャラを出す
=> "Blueno"
irb(main):023:0> Faker::JapaneseMedia::OnePiece.character
=> "Hannyabal"
```

###13.2.3 プロフィール画面のマイクロポストをテストする
アカウントを有効化したばかりのユーザーはプロフィール画面にリダイレクトされるので、そのプロフィール画面が正しく描画されていることは、単体テストを通して確認済み
ここでは、プロフィール画面で表示される*マイクロポスト*に対して、統合テストを書いていく
まずは、プロフィール画面用の統合テストを生成

```
$ rails generate integration_test users_profile
      invoke  test_unit
      create    test/integration/users_profile_test.rb
```

マイクロポストのページネーションをテストするために、マイクロポスト用のfixtureにいくつかテストデータを追加する必要がある


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

tau_manifesto:
  content: "Check out the @tauday site by @mhartl: https://tauday.com"
  created_at: <%= 3.years.ago %>
  user: michael

cat_video:
  content: "Sad cats are sad: https://youtu.be/PKffm2uI4dk"
  created_at: <%= 2.hours.ago %>
  user: michael

most_recent:
  content: "Writing a short test"
  created_at: <%= Time.zone.now %>
  user: michael

<% 30.times do |n| %>
micropost_<%= n %>:
  content: <%= Faker::Lorem.sentence(word_count: 5) %>#5単語で作る
  created_at: <%= 42.days.ago %>
  user: michael
<% end %>
```

テストデータの準備は完了
今回のテストでは、
1プロフィール画面にアクセスした後に、2ページタイトルとユーザー名、3Gravatar、4マイクロポストの投稿数、5ページ分割されたマイクロポスト、といった順でテストしていく

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

class UsersProfileTest < ActionDispatch::IntegrationTest
  include ApplicationHelper

  def setup
    @user = users(:michael)
  end

test "profile display" do
    get user_path(@user)#user画面取得
    assert_template 'users/show'#showページが出力されるか
    assert_select 'title', full_title(@user.name)#user.nameがタイトルになっているか
    assert_select 'h1', text: @user.name
    assert_select 'h1>img.gravatar'#h1の中にgaravatarクラスのimagタグがあるか
    assert_match @user.microposts.count.to_s, response.body#HTMLの全てからポストのカウント投稿数を探す
    assert_select 'div.pagination'
    @user.microposts.paginate(page: 1).each do |micropost|
      assert_match micropost.content, response.body
    end
  end
end
```

マイクロポストの投稿数をチェックするために、response.bodyを使用
response.bodyにはそのページの完全なHTMLが含まれています（HTMLのbodyタグだけではない）
したがって、そのページのどこかしらにマイクロポストの投稿数が存在するのであれば、次のように探し出してマッチできる

```
assert_match @user.microposts.count.to_s, response.body
```

これはassert_selectよりもずっと抽象的なメソッド。
assert_selectではどのHTMLタグを探すのか伝える必要があるが(div.paginateのように)
assert_matchメソッドではその必要がない点が異なる。

また、assert_selectの引数では、ネストした文法を使っている点にも注目

```
assert_select 'h1>img.gravatar'
```

このように書くことで、h1タグ（トップレベルの見出し）の内側にある、gravatarクラス付きのimgタグがあるかどうかをチェックできる。

テストは green になる

###エラー発生

```
Missing helper file helpers/microposts_helper.rb (AbstractController::Helpers::MissingHelperError)
```

私の場合、ここでエラーが発生し、以下サイトを参考に

```
$ spring stopを実行したら直りました。
```

https://eri2490.hatenablog.com/entry/2020/07/03/233832

###演習
リスト 13.28にある２つの'h1'のテストが正しいか確かめるため、該当するアプリケーション側のコードをコメントアウトしてみましょう。テストが green から red に変わることを確認してみてください。

```
<%# <h1> %>
        <%= gravatar_for @user %>
        <%= @user.name %>
<%# </h1> %>
```

失敗する

リスト 13.28にあるテストを変更して、will_paginateが１度のみ表示されていることをテストしてみましょう。ヒント: 表 5.2を参考にしてください。

```
 assert_select 'div.pagination',count:1 
```

##13.3 マイクロポストを操作する
データモデリングとマイクロポスト表示テンプレートの両方が完成したので、次はWeb経由でそれらを作成するためのインターフェイスを作る
ここではステータスフィードの基礎的なものを作って、ユーザーがマイクロポストをWeb経由で破棄できるようする

従来のRails開発の慣習と異なる箇所が1つ
Micropostsリソースへのインターフェイスは、主にプロフィールページとHomeページのコントローラを経由して実行されるので、Micropostsコントローラにはnewやeditのようなアクションは不要
つまり、createとdestroyがあれば十分
したがって、Micropostsのリソース以下のようになる

```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
  resources :account_activations, only: [:edit]
  resources :password_resets,     only: [:new, :create, :edit, :update]
  resources :microposts,          only: [:create, :destroy]
end
```

|HTTPリクエスト	|URL	|アクション	|名前付きルート
|:--:|:--|:--|:--|
|POST	|/microposts	|create	|microposts_path
|DELETE	|/microposts/1	|destroy|	micropost_path(micropost)

###13.3.1 マイクロポストのアクセス制御
Micropostsリソースの開発では、Micropostsコントローラ内のアクセス制御から始める。
関連付けられたユーザーを通してマイクロポストにアクセスするので、createアクションやdestroyアクションを利用するユーザーは、ログイン済み出ないといけない。

ログイン済みかどうかを確かめるテストでは、Usersコントローラ用のテストがそのまま使える。
正しいリクエストを各アクションに向けて発行し、マイクロポストの数が変化していないかどうか、また、リダイレクトされるかどうかを確かめればよい

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

class MicropostsControllerTest < ActionDispatch::IntegrationTest

  def setup
    @micropost = microposts(:orange)
  end

  test "should redirect create when not logged in" do
    assert_no_difference 'Micropost.count' do
      post microposts_path, params: { micropost: { content: "Lorem ipsum" } }
    end
    assert_redirected_to login_url
  end

  test "should redirect destroy when not logged in" do
    assert_no_difference 'Micropost.count' do
      delete micropost_path(@micropost)
    end
    assert_redirected_to login_url
  end
end
```

このテストにパスするコードを書くためには、少しアプリケーション側のコードをリファクタリングしておく必要がある
というのも、以前beforeフィルターのlogged_in_userメソッドを使って、ログインを要求した。
その際はUsersコントローラ内にlogged_in_userメソッドがあったので、beforeフィルターで指定していたが、このメソッドはMicropostsコントローラでも必要。
そこで、各コントローラが継承するApplicationコントローラにlogged_in_userメソッドを移す

```app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  include SessionsHelper

  private

    # ユーザーのログインを確認する
    def logged_in_user
      unless logged_in?
        store_location
        flash[:danger] = "Please log in."
        redirect_to login_url
      end
    end
end
```

Usersコントローラからもlogged_in_userを削除

```app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
  .
  .
  .
  private

    def user_params
      params.require(:user).permit(:name, :email, :password,
                                   :password_confirmation)
    end

    # beforeフィルター

    # 正しいユーザーかどうかを確認
    def correct_user
      @user = User.find(params[:id])
      redirect_to(root_url) unless current_user?(@user)
    end

    # 管理者かどうかを確認
    def admin_user
      redirect_to(root_url) unless current_user.admin?
    end
end
```

これでMicropostsコントローラからもlogged_in_userメソッドを呼び出せるようになった
これにより、createアクションやdestroyアクションに対するアクセス制限が、beforeフィルターで簡単に実装できるようになる

```app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
  before_action :logged_in_user, only: [:create, :destroy]

  def create
  end

  def destroy
  end
end
```

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

###演習
なぜUsersコントローラ内にあるlogged_in_userフィルターを残したままにするとマズイのでしょうか? 考えてみてください。
→コードが重複して、エラーの元になるため

###13.3.2 マイクロポストを作成する

第7章では、HTTP POSTリクエストをUsersコントローラのcreateアクションに発行するHTMLフォームを作成することで、ユーザーのサインアップを実装した。
マイクロポスト作成の実装もこれと似ているが、別のmicropost/new ページを使う代わりに、ホーム画面（つまりルートパス）にフォームを置くという点が異なる
![図](https://railstutorial.jp/chapters/6.0/images/figures/home_page_with_micropost_form_mockup_bootstrap.png)

最後にホーム画面を実装したときは、[sign up now!]ボタンが中央にあった。

要は、ユーザーがログインしている場合のみ、マイクロポスト作成フォームを表示させるようにする。
(ホーム画面の表示を、ログイン状態と日ログイン状態で変わるよう実装する。)

その前に、まずはマイクロポストのcreateアクションを作り始める。

このアクションもユーザー用アクションと似ていて、違う点は
新しいマイクロポストをbulidするためにUser/Micropost関連付けを使っている点。

```app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
  before_action :logged_in_user, only: [:create, :destroy]

  def create
    @micropost = current_user.microposts.build(micropost_params) # formで送った値をmicropost_paramsで受け取り、親モデルと関連付け(bulid)してcurrent_userに渡して代入
    if @micropost.save
    if @micropost.save
      flash[:success] = "Micropost created!"
      redirect_to root_url
    else
      render 'static_pages/home'
    end
  end

  def destroy
  end

  private

    def micropost_params
      params.require(:micropost).permit(:content)
    end
end
```

マイクロポスト作成フォームを構築するために、サイト訪問者がログインしているかどうかに応じて異なるHTMLを提供するコードを使う

```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="micropost_form">
        <%= render 'shared/micropost_form' %>
      </section>
    </aside>
  </div>
<% else %>
  <div class="center jumbotron">
    <h1>Welcome to the Sample App</h1>

    <h2>
      This is the home page for the
      <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a>
      sample application.
    </h2>

    <%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %>
  </div>

  <%= link_to image_tag("rails.svg", alt: "Rails logo", width: "200px"),
              "https://rubyonrails.org/" %>
<% end %>
```

上のコードを動かすためには、いくつかのパーシャルを作る必要がある

```app/views/shared/_user_info.html.erb
<%= link_to gravatar_for(current_user, size: 50), current_user %>
<h1><%= current_user.name %></h1>
<span><%= link_to "view my profile", current_user %></span>
<span><%= pluralize(current_user.microposts.count, "micropost") %></span>#以下解説
```

プロフィールサイドバーのときと同様、
ユーザー情報にもユーザーが投稿したマイクロポストの総数が表示されていることにも注目。

プロフィールサイドバーでは、1 micropostや2 micropostsというように、
ユーザーの投稿数に合わせてスペルを追従させたいので、pluralizeメソッドを使って調整している。

次はマイクロポスト作成フォームを定義する。
これはユーザー登録フォームに似ている。

```app/views/shared/_micropost_form.html.erb
<%= form_with(model: @micropost, local: true) do |f| %>#1こ目の変更
  <%= render 'shared/error_messages', object: f.object %>#2こ目の変更
  <div class="field">
    <%= f.text_area :content, placeholder: "Compose new micropost..." %>
  </div>
  <%= f.submit "Post", class: "btn btn-primary" %>
<% end %>
```

上のフォームが動くようにするためには、2箇所の変更が必要
**1つは、次のように@micropostを定義すること**

```app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController

  def home
    @micropost = current_user.microposts.build if logged_in?
  end

  def help
  end

  def about
  end

  def contact
  end
end
```

もちろん、current_userメソッドはユーザーがログインしているときしか使えない。
したがって、@micropost変数もログインしているときのみ定義される

もう1つの変更は、**エラーメッセージのパーシャルを再定義すること**

以前のエラーメッセージパーシャルが@user変数を直接参照していた。
今回は代わりに@micropost変数を使う必要がある
これらのケースをまとめると、フォーム変数fをf.objectとすることによって、関連付けられたオブジェクトにアクセスすることができる。
したがって、

```
form_with(model: @user, local: true) do |f|
```

上のようにf.objectが@userとなる場合と、

```
form_with(model: @micropost, local: true) do |f|
```

上のようにf.objectが@micropostになる場合などがある。

パーシャルにオブジェクトを渡すために、値がオブジェクトで、キーがパーシャルでの変数名と同じハッシュを利用
言い換えると、`object: f.object`はerror_messagesパーシャルの中で`object`という変数名を作成してくれるので、この変数を使ってエラーメッセージを更新すればよいということになる

```app/views/shared/_error_messages.html.erb
<% if object.errors.any? %>
  <div id="error_explanation">
    <div class="alert alert-danger">
      The form contains <%= pluralize(object.errors.count, "error") %>.#変更
    </div>
    <ul>
    <% object.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
<% end %>
```

この時点でテストを走らせてみるとエラーになる

なぜ失敗しているのか。
error_messagesパーシャルは他の場所でも使われていた
1　ユーザー登録、2　パスワード再設定　3　ユーザー編集
それぞれのビューを更新する必要があった

```app/views/users/new.html.erb
<% provide(:title, 'Sign up') %>
<h1>Sign up</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_with(model: @user, local: true) do |f| %>
      <%= render 'shared/error_messages', object: f.object %>#変更
      <%= f.label :name %>
      <%= f.text_field :name, class: 'form-control' %>
```

```app/views/users/edit.html.erb
<% provide(:title, "Edit user") %>
<h1>Update your profile</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_with(model: @user, local: true) do |f| %>
      <%= render 'shared/error_messages', object: f.object %>

      <%= f.label :name %>
      <%= f.text_field :name, class: 'form-control' %>
```

```app/views/password_resets/edit.html.erb
<% provide(:title, 'Reset password') %>
<h1>Reset password</h1>

<div class="row">
  <div class="col-md-6 col-md-offset-3">
    <%= form_with(model: @user, url: password_reset_path(params[:id]),
                    local: true) do |f| %>
      <%= render 'shared/error_messages', object: f.object %>

      <%= hidden_field_tag :email, @user.email %>
```

これで、すべてのテストが green になるはず


###注意
テストで失敗する。

site_layout_testの**演習時**にログインされたユーザでも
assert_select "a[href=?]", signup_pathがあるかテストした
今はサインアップ後は出ないはずなので、エラーになる

```
test "layout links login" do
    log_in_as(@user)
    get root_path
    assert_select "a[href=?]", root_path, count: 2
    assert_select "a[href=?]", help_path
    assert_select "a[href=?]", about_path
    assert_select "a[href=?]", contact_path
    # assert_select "a[href=?]", signup_path
    assert_select "a[href=?]", users_path
    assert_select "a[href=?]", user_path(@user)
    assert_select "a[href=?]", edit_user_path(@user)
    assert_select "a[href=?]", logout_path
  end
```

###演習
Homeページをリファクタリングして、if-else文の分岐のそれぞれに対してパーシャルを作ってみましょう。

```home.html.erb
<% if logged_in? %>
 <%= render 'static_pages/if' %>
<% else %>
  <%=render 'static_pages/else'%>
<% end %>
```

```_if.html.erb
 <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.html.erb
<div class="center jumbotron">
    <h1>Welcome to the Sample App</h1>

    <h2>
      This is the home page for the
      <a href="https://railstutorial.jp/">Ruby on Rails Tutorial</a>
      sample application.
    </h2>

    <%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %>
  </div>

  <%= link_to image_tag("rails.svg", alt: "Rails logo", width: "200px"),
              "https://rubyonrails.org/" %>
```

###13.3.3 フィードの原型
マイクロポスト投稿フォームが動くようになりましたが、今の段階では投稿した内容をすぐに見ることができない
というのも、Homeページにまだマイクロポストを表示する部分が実装されていないから

実際に正しく動いているか確認するには、エントリーを投稿後にプロフィールページに移動してポストを確認すればよいが、これはかなり面倒な作業。

以下のモックアップのような、ユーザー自身のポストをHomeに載せた方が便利なので、これを実装する。

![モックアップ](https://railstutorial.jp/chapters/6.0/images/figures/proto_feed_mockup.png)


すべてのユーザーがフィードを持つので、feedメソッドはUserモデルで作るのが自然
フィードの原型では、まずは現在ログインしているユーザーのマイクロポストをすべて取得

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

  # 試作feedの定義
  # 完全な実装は次章の「ユーザーをフォローする」を参照
  def feed
    Micropost.where("user_id = ?", id)
  end

```

次のコードで使われている疑問符は、セキュリティ上重要な役割を果たしている。

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

上の疑問符があることで、SQLクエリに代入する前にidがエスケープされるため、SQLインジェクション（SQL Injection）呼ばれる深刻なセキュリティホールを避けることができる。※SQLインジェクションとは、セキュリティの不備を攻撃するを方法のこと
この場合のid属性は単なる整数（すなわちself.idはユーザーのid）であるため危険はないが、**SQL文に変数を代入する場合は常にエスケープする**習慣を身に付けた方がいい

サンプルアプリケーションにフィード機能を導入すべく、
ログインユーザーのフィード用にインスタンス変数`@feed_items`を追加し、
Homeページにはフィード用のパーシャルを追加する。

Homeページに変更を加えた結果は下に記載するが、
その前に、ユーザーがログインしているかどうかを調べる後置if文が変化している点に注目

```
@micropost = current_user.microposts.build if logged_in?
```

上のコードが、次のような前置if文に変わっている。

```
  if logged_in?
    @micropost  = current_user.microposts.build
    @feed_items = current_user.feed.paginate(page: params[:page])
  end
（訳注: 1行のときは後置if文、2行以上のときは前置if文を使うのがRubyの慣習）
```

homeアクションにフィードのインスタンス変数を追加するコード

```app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController

  def home
    if logged_in?
      @micropost  = current_user.microposts.build
      @feed_items = current_user.feed.paginate(page: params[:page])
    end
  end
```

```app/views/shared/_feed.html.erb
<% if @feed_items.any? %>
  <ol class="microposts">
    <%= render @feed_items %>
  </ol>
  <%= will_paginate @feed_items %>
<% end %>
```

上記がステータスフィードのパーシャル。
Micropostのパーシャルとは少し異なっている。

```
<%= render @feed_items %>
```

@feed_itemsの各要素がMicropostクラスを持っていたため、
RailsはMicropostのパーシャルを呼び出すことができる。

このように、
**Railsは対応する名前のパーシャルを、渡されたリソースのディレクトリ内から探しにいくことができる。**

```
app/views/microposts/_micropost.html.erb
```

あとは、_feedパーシャルをHomeページに組み込むことで、
フィードとして表示することができる。

先ほどifパーシャルを使ったので、こちらに組み込む。


```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="micropost_form">
        <%= render 'shared/micropost_form' %>
      </section>
    </aside>
    <div class="col-md-8">
      <h3>Micropost Feed</h3>
      <%= render 'shared/feed' %>
    </div>
  </div>
<% else %>
  .
  .
  .
<% end %>
```

ただし、マイクロポストの投稿が失敗すると、Homeページは@feed_itemsインスタンス変数を期待しているため、現状では壊れてしまう。
解決方法は、Micropostsコントローラのcreateアクションへの送信が失敗した場合に備えて、必要なフィード変数をこのブランチで渡しておくこと

```app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
  before_action :logged_in_user, only: [:create, :destroy]

  def create
    @micropost = current_user.microposts.build(micropost_params)
    if @micropost.save
      flash[:success] = "Micropost created!"
      redirect_to root_url
    else
      @feed_items = current_user.feed.paginate(page: params[:page])
      render 'static_pages/home'
    end
  end
```

まだページネーションは動かない

ページを下にスクロールしてページネーション用リンクを表示してみると、“2”のリンクと“Next”のリンクがどちらも同じ次のページを指す。createアクションはMicropostsコントローラにあるので、このURLは「/microposts?page=2」となるが、
これはMicropostsの存在していないindexアクションを開こうとしているため。
その結果、どちらのリンクをクリックしてもエラーが発生する

この問題は、Homeページに対応するcontrollerパラメータとaction パラメータを明示的にwill_paginateに渡すことで解決できる。たとえば、static_pagesコントローラとhomeアクションを渡す

```app/views/shared/_feed.html.erb
<% if @feed_items.any? %>
  <ol class="microposts">
    <%= render @feed_items %>
  </ol>
  <%= will_paginate @feed_items,
                    params: { controller: :static_pages, action: :home } %>
<% end %>
```

ページネーションのどちらのリンクをクリックしても2番目のページが期待どおり表示される

###演習
1 新しく実装したマイクロポストの投稿フォームを使って、実際にマイクロポストを投稿してみましょう。Railsサーバーのログ内にあるINSERT文では、どういった内容をデータベースに送っているでしょうか? 確認してみてください。

```Ruby
Started POST "/microposts" for ::1 at 2021-03-09 11:03:51 +0900
Processing by MicropostsController#create as HTML
  Parameters: {"authenticity_token"=>"+SAHjHnKqCrd3R+MJaDmeBMqeVOFELehi2hFpKOgMOCCp2W/AvXqiZh2pVOYSGAFP5sgIKjvVwdTBH6gRh+ABQ==", "micropost"=>{"content"=>"aaaaaaaaaaaaaaaaaaaaaaa"}, "commit"=>"Post"}
  User Load (0.2ms)  SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 3], ["LIMIT", 1]]
  ↳ app/helpers/sessions_helper.rb:16:in `current_user'
   (0.2ms)  begin transaction
  ↳ app/controllers/microposts_controller.rb:7:in `create'
  Micropost Create (1.8ms)  INSERT INTO "microposts" ("content", "user_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["content", "aaaaaaaaaaaaaaaaaaaaaaa"], ["user_id", 3], ["created_at", "2021-03-09 02:03:51.480124"], ["updated_at", "2021-03-09 02:03:51.480124"]]
```

2 コンソールを開き、user変数にデータベース上の最初のユーザーを代入してみましょう。その後、Micropost.where("user_id = ?", user.id)とuser.microposts、そしてuser.feedをそれぞれ実行してみて、実行結果がすべて同じであることを確認してみてください。ヒント: ==で比較すると結果が同じかどうか簡単に判別できます。

```Ruby
rb(main):001:0> user=User.first
irb(main):002:0> a=Micropost.where("user_id = ?", user.id)
  Micropost Load (1.8ms)  SELECT "microposts".* FROM "microposts" WHERE (user_id = 1) ORDER BY "microposts"."created_at" DESC LIMIT ?  [["LIMIT", 11]]
=> #<ActiveRecord::Relation [#<Micropost id: 289, content: "Dolorum ducimus liber...
irb(main):003:0> b=user.microposts
user_id" = ? ORDER BY "microposts"."created_at" DESC LIMIT ?  [["user_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Micropost id: 289, content: "...
irb(main):004:0> c=user.feed
  Micropost Load (3.3ms)  SELECT "microposts".* FROM "microposts" WHERE (user_id IN (S
        WHERE follower_id = 1)]]
=> #<ActiveRecord::Relation [#<Micropost id: 301, content: "aaaaaaaaaaaaaaaaaaaaa...
irb(main):005:0> a==buser_id" = ? ORDER BY "microposts"."created_at" DESC  [["user_id", 1]]ORDER BY "microposts"."created_at" DESC
=> true
irb(main):006:0> b==c
  Micropost Load (2.7ms)  SELECT "microposts".* FROM "microposts" WHERE (user_id IN (SELECT followed_id FROM relationships
        WHERE follower_id = 1)
=> false
```

なぜかfalseになる...

###13.3.4 マイクロポストを削除する

最後の機能として、マイクロポストリソースにポストを削除する機能を追加する

ユーザー削除と同じく"delete" リンクで行えるようにする。
ユーザーの削除は管理者ユーザーのみが行えるように制限されていたのに対し、
Postは自分が投稿したマイクロポストに対してのみ削除リンクが動作するようにしていく。

最初のステップとして、マイクロポストのパーシャルに削除リンクを追加

```app/views/microposts/_micropost.html.erb
<li id="micropost-<%= micropost.id %>">
  <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
  <span class="user"><%= link_to micropost.user.name, micropost.user %></span>
  <span class="content"><%= micropost.content %></span>
  <span class="timestamp">
    Posted <%= time_ago_in_words(micropost.created_at) %> ago.
    <% if current_user?(micropost.user) %>
      <%= link_to "delete", micropost, method: :delete,       #deleteリンク
                                       data: { confirm: "You sure?" } %>
    <% end %>
  </span>
</li>
```

次に、Micropostsコントローラのdestroyアクションを定義
これもユーザーにおける実装とだいたい同じで、
大きな違いは、admin_userフィルターで@user変数を使うのではなく、
関連付けを使ってマイクロポストを見つけようとしている点。

これにより、あるユーザーが他のユーザーのマイクロポストを削除しようとすると、自動的に失敗するようになる。

具体的には、correct_userフィルター内でfindメソッドを呼び出すことで、
現在のユーザーが削除対象のマイクロポストを保有しているかどうか確認する


```app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
  before_action :logged_in_user, only: [:create, :destroy]
  before_action :correct_user,   only: :destroy
  .
  def destroy
    @micropost.destroy
    flash[:success] = "Micropost deleted"
    redirect_to request.referrer || root_url
  end

  private

    def micropost_params
      params.require(:micropost).permit(:content)
    end

    def correct_user
      @micropost = current_user.microposts.find_by(id: params[:id])
      redirect_to root_url if @micropost.nil?
    end
end
```

destroyメソッドではリダイレクトを使っている点に注目

```
request.referrer || root_url
```

ここでは`request.referrer`というメソッドを使っている。
このメソッドはフレンドリーフォワーディングの`request.url`変数と似て、
一つ前のURLを返す（今回の場合、Homeページ）
このため、マイクロポストがHomeページから削除された場合でもプロフィールページから削除された場合でも、request.referrerを使うことでDELETEリクエストが発行されたページに戻すことができる
ちなみに、元に戻すURLが見つからなかった場合でも（例えばテストではnilが返ってくることもある）、||演算子でroot_urlをデフォルトに設定しているため、問題ない

###演習
1 マイクロポストを作成し、その後、作成したマイクロポストを削除してみましょう。次に、Railsサーバーのログを見てみて、DELETE文の内容を確認してみてください。

2 redirect_to request.referrer || root_urlの行をredirect_back(fallback_location: root_url)と置き換えてもうまく動くことを、ブラウザを使って確認してみましょう（このメソッドはRails 5から新たに導入されました）。


###13.3.5 フィード画面のマイクロポストをテストする

Micropostモデルとインターフェースが完成した。
残りは、Micropostsコントローラの認可をチェックする短いテストと、それらをまとめる統合テストを書くこと。

マイクロポスト用のfixtureに、別々のユーザーに紐付けられたマイクロポストを追加していく

```test/fixtures/microposts.yml
.
.
.
ants:
  content: "Oh, is that what you want? Because that's how you get ants!"
  created_at: <%= 2.years.ago %>
  user: archer

zone:
  content: "Danger zone!"
  created_at: <%= 3.days.ago %>
  user: archer

tone:
  content: "I'm sorry. Your words made sense, but your sarcastic tone did not."
  created_at: <%= 10.minutes.ago %>
  user: lana

van:
  content: "Dude, this van's, like, rolling probable cause."
  created_at: <%= 4.hours.ago %>
  user: lana
```

次に、自分以外のユーザーのマイクロポストは削除をしようとすると、適切にリダイレクトされることをテストで確認する

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

class MicropostsControllerTest < ActionDispatch::IntegrationTest

  def setup
    @micropost = microposts(:orange)
  end

  test "should redirect create when not logged in" do
    assert_no_difference 'Micropost.count' do
      post microposts_path, params: { micropost: { content: "Lorem ipsum" } }
    end
    assert_redirected_to login_url
  end

  test "should redirect destroy when not logged in" do
    assert_no_difference 'Micropost.count' do
      delete micropost_path(@micropost)
    end
    assert_redirected_to login_url
  end

  test "should redirect destroy for wrong micropost" do
    log_in_as(users(:michael))
    micropost = microposts(:ants)
    assert_no_difference 'Micropost.count' do
      delete micropost_path(micropost)
    end
    assert_redirected_to root_url
  end
end
```

最後に、統合テストを書く。
今回の統合テストでは、
1　ログイン、
2マイクロポストのページ分割の確認
3　無効なマイクロポストを投稿
4　有効なマイクロポストを投稿
5　マイクロポストの削除
6　他のユーザーのマイクロポストには［delete］リンクが表示されないことを確認
といった順でテストする

```
$ rails generate integration_test microposts_interface
      invoke  test_unit
      create    test/integration/microposts_interface_test.rb
```

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

class MicropostsInterfaceTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:michael)
  end

  test "micropost interface" do
    log_in_as(@user)
    get root_path
    assert_select 'div.pagination'
    # 無効な送信
    assert_no_difference 'Micropost.count' do
      post microposts_path, params: { micropost: { content: "" } }
    end
    assert_select 'div#error_explanation'
    assert_select 'a[href=?]', '/?page=2'  # 正しいページネーションリンク
    # 有効な送信
    content = "This micropost really ties the room together"
    assert_difference 'Micropost.count', 1 do
      post microposts_path, params: { micropost: { content: content } }
    end
    assert_redirected_to root_url
    follow_redirect!
    assert_match content, response.body
    # 投稿を削除する
    assert_select 'a', text: 'delete'
    first_micropost = @user.microposts.paginate(page: 1).first
    assert_difference 'Micropost.count', -1 do
      delete micropost_path(first_micropost)
    end
    # 違うユーザーのプロフィールにアクセス（削除リンクがないことを確認）
    get user_path(users(:archer))
    assert_select 'a', text: 'delete', count: 0
  end
end
```

既にアプリケーション側のコードは実装してあるので、このテストは green になる


###演習
１　リスト 13.56で示した４つのコメント（「無効な送信」など）のそれぞれに対して、テストが正しく動いているか確認してみましょう。具体的には、対応するアプリケーション側のコードをコメントアウトし、テストが red になることを確認し、元に戻すと green になることを確認してみましょう。
→確認

2　サイドバーにあるマイクロポストの合計投稿数をテストしてみましょう。このとき、単数形（micropost）と複数形（microposts）が正しく表示されているかどうかもテストしてください。ヒント: リスト 13.58を参考にしてみてください。

```micropost_interface_test.rb
 test "micropost sidebar count" do
    log_in_as(@user)
    get root_path
    assert_match "#{@user.microposts.count} microposts", response.body
    # まだマイクロポストを投稿していないユーザー
    other_user = users(:malory)
    log_in_as(other_user)
    get root_path
    assert_match "0 microposts", response.body
    other_user.microposts.create!(content: "A micropost")
    get root_path
    assert_match "1 micropost", response.body
  end
```


##13.4 マイクロポストの画像投稿
ここまででマイクロポストに関する基本的な操作はすべて実装できた。
この節では、応用編として画像付きマイクロポストを投稿できるようにしていく。
手順として、まずは開発環境用のβ版を実装し、その後、いくつかの改善をとおして本番環境用の完成版を実装する。

画像アップロード機能を追加するためには、2つの視覚的な要素が必要です。
1つは画像をアップロードするためのフォーム
もう1つは投稿された画像そのもの

![モックアップ](https://railstutorial.jp/chapters/6.0/images/figures/micropost_image_mockup.png)

###13.4.1 基本的な画像アップロード

Railsでファイルをアップロードするのに最も便利な方法は、Railsに組み込まれているActive Storageという機能を用いること。Active Storageを使うことで画像を簡単に扱うことができ、画像に関連付けるモデルも自由に指定できる。
ここではActive Storageを画像アップロードにしか使わないが、Active Storageはかなり一般性の高いつくりになっており、平文テキストはもちろん、PDFファイルや音声ファイルといったさまざまなバイナリファイルも扱える

Active Storageをアプリケーションに追加するのは、次のコマンド一発で完了する

```
$ rails active_storage:install
```

上のコマンドを実行すると、添付ファイルの保存に用いるデータモデルを作成するためのデータベースマイグレーションが1つ生成される。

なのでマイグレーションを実行する

```
$ rails db:migrate
```

Active Storage APIの中で最初に知っておく必要があるのは`has_one_attached`メソッドで,
指定のモデルと、アップロードされたファイルを関連付けるのに使う
この場合はimageを指定してMicropostモデルと関連付ける

```app/models/micropost.rb
class Micropost < ApplicationRecord
  belongs_to       :user
  has_one_attached :image
  default_scope -> { order(created_at: :desc) }
  validates :user_id, presence: true
  validates :content, presence: true, length: { maximum: 140 }
end
```

このアプリケーションでは「マイクロポスト1件につき画像は1件」という設計を採用しているが、Active Storageではそのほかにhas_many_attachedオプションも提供している。
これは、Active Recordオブジェクト1件につき複数のファイルを添付できるようになる

Homeページに画像アップロードを追加するためには、マイクロポストのフォームにfile_fieldタグを含める必要がある

```app/views/shared/_micropost_form.html.erb
<%= form_with(model: @micropost, local: true) do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <div class="field">
    <%= f.text_area :content, placeholder: "Compose new micropost..." %>
  </div>
  <%= f.submit "Post", class: "btn btn-primary" %>
  <span class="image">
    <%= f.file_field :image %>
  </span>
<% end %>
```

最後に、Micropostsコントローラを更新して、新たに作成したmicropostオブジェクトに画像を追加できるようにする。
Actuve Storage APIにはそのためのattachメソッドが提供されているので、これを使う。
具体的には、Micropostsコントローラのcreateアクションの中で、アップロードされた画像を@micropostオブジェクトにアタッチし、このアップロードを許可するために、micropost_paramsメソッドを更新して:imageを許可済み属性リストに追加し、Web経由で更新できるようにする必要もある

```app/controllers/microposts_controller.rb
class MicropostsController < ApplicationController
  before_action :logged_in_user, only: [:create, :destroy]
  before_action :correct_user,   only: :destroy

  def create
    @micropost = current_user.microposts.build(micropost_params)
    @micropost.image.attach(params[:micropost][:image])
    if @micropost.save
      flash[:success] = "Micropost created!"
      redirect_to root_url
    else
      @feed_items = current_user.feed.paginate(page: params[:page])
      render 'static_pages/home'
    end
  end

  def destroy
    @micropost.destroy
    flash[:success] = "Micropost deleted"
    redirect_to request.referrer || root_url
  end

  private

    def micropost_params
      params.require(:micropost).permit(:content, :image)
    end

    def correct_user
      @micropost = current_user.microposts.find_by(id: params[:id])
      redirect_to root_url if @micropost.nil?
    end
end
```

一度画像がアップロードされれば、micropostパーシャルのimage_tagヘルパーを用いて、関連付けられたmicropost.imageを描画（レンダリング）できるようになる
また、画像の無い（テキストのみの）マイクロポストでは画像を表示させないようにするために、attached?という論理値を返すメソッドを使っている点に注目


```app/views/microposts/_micropost.html.erb
<li id="micropost-<%= micropost.id %>">
  <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
  <span class="user"><%= link_to micropost.user.name, micropost.user %></span>
  <span class="content">
    <%= micropost.content %>
    <%= image_tag micropost.image if micropost.image.attached? %>
  </span>
  <span class="timestamp">
    Posted <%= time_ago_in_words(micropost.created_at) %> ago.
    <% if current_user?(micropost.user) %>
      <%= link_to "delete", micropost, method: :delete,
                                       data: { confirm: "You sure?" } %>
    <% end %>
  </span>
</li>
```


###演習
1 画像付きのマイクロポストを投稿してみましょう。もしかして、大きすぎる画像が表示されてしまいましたか?（心配しないでください、次の13.4.3でこの問題を直します）。

2 リスト 13.64に示すテンプレートを参考に、13.4で実装した画像アップローダーをテストしてください。テストの準備として、まずはサンプル画像をfixtureディレクトリに追加してください（リスト 13.63）。リスト 13.64で追加したテストでは、Homeページにあるファイルアップロードと、投稿に成功した時に画像が表示されているかどうかをチェックしています。なお、テスト内にあるfixture_file_uploadというメソッドは、fixtureで定義されたファイルをアップロードする特別なメソッドです。ヒント: image属性が有効かどうかを確かめるときは、11.3.3で紹介したassignsメソッドを使ってください。このメソッドを使うと、投稿に成功した後にcreateアクション内のマイクロポストにアクセスするようになります。

```test/integration/microposts_interface_test.rb
test "micropost interface" do
    log_in_as(@user)#ログイン
    get root_path
    assert_select 'div.pagination'#paginationクラスのdivがあるか
    assert_select 'input[type=file]'←変更
    # 無効な送信
    assert_no_difference 'Micropost.count' do#以下作業してポストの数に変動ないか
      post microposts_path, params: { micropost: { content: "" } }#postが空＝エラー
    end
    assert_select 'div#error_explanation'#errorが出るか
    assert_select 'a[href=?]', '/?page=2'  # 正しいページネーションリンク
    # 有効な送信
    content = "This micropost really ties the room together"
    image = fixture_file_upload('test/fixtures/kitten.jpg', 'image/jpeg')
    assert_difference 'Micropost.count', 1 do##以下作業してポストの数は1増えるか
      post microposts_path, params: { micropost:
                                    { content: content, image: image } }
    end
    assert assigns(:micropost).image.attached?←変更
    assert_redirected_to root_url#投稿後rootに戻るか
    follow_redirect!
    assert_match content, response.body#htmlにcontentが表示されているか
    # 投稿を削除する
    assert_select 'a', text: 'delete'#aタグのdeleteテキストはあるか
    first_micropost = @user.microposts.paginate(page: 1).first#最初のポストを代入
    assert_difference 'Micropost.count', -1 do#1減るか
      delete micropost_path(first_micropost)#postを削除して
    end
    # 違うユーザーのプロフィールにアクセス（削除リンクがないことを確認）
    get user_path(users(:archer))
    assert_select 'a', text: 'delete', count: 0
  end
```


###13.4.2 画像の検証
今時点、アップロードされた画像に対する制限がないため、もしユーザーが巨大なファイルを上げたり、無効なファイルを上げると問題が発生してしまう。
これを直すために、画像サイズやフォーマットに対するバリデーションを実装する。

Active Storageは、こうしたフォーマット機能やバリデーション機能がネイティブでサポートされていない。
よくあることらしいが、こういう場合はそうした機能をgemで追加する

```Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

gem 'rails',                      '6.0.3'
gem 'active_storage_validations', '0.8.2'
gem 'bcrypt',                     '3.1.13'
.
```

```
$ bundle install
```

このgemのドキュメントでは、次のようにcontent_typeを検査することで画像をバリデーションできることがわかる

```
content_type: { in: %w[image/jpeg image/gif image/png],
                message: "must be a valid image format" }
```

上のコードは、サポートする画像フォーマットに対応する画像MIME typeをチェックする

同様に、ファイルサイズも以下のようにバリデーションできる

```
size: { less_than: 5.megabytes,
        message: "should be less than 5MB" }
```

上は画像の最大サイズを5 MBに制限している。

この内容を記載する

```app/models/micropost.rb
class Micropost < ApplicationRecord
  belongs_to       :user
  has_one_attached :image
  default_scope -> { order(created_at: :desc) }
  validates :user_id, presence: true
  validates :content, presence: true, length: { maximum: 140 }
  validates :image,   content_type: { in: %w[image/jpeg image/gif image/png],
                                      message: "must be a valid image format" },
                      size:         { less_than: 5.megabytes,
                                      message: "should be less than 5MB" }
end
```

上記のバリデーションを強化するため、クライアント側（つまりブラウザ）にも画像アップロードのサイズやフォーマットをチェックする仕組みを追加する。
手始めに、JavaScript（具体的にはjQuery）を加え、ユーザーがアップロードしようとする画像が巨大過ぎるときにアラートを表示するようにする

```app/views/shared/_micropost_form.html.erb
<%= form_with(model: @micropost, local: true) do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <div class="field">
    <%= f.text_area :content, placeholder: "Compose new micropost..." %>
  </div>
  <%= f.submit "Post", class: "btn btn-primary" %>
  <span class="image">
    <%= f.file_field :image %>
  </span>
<% end %>

<script type="text/javascript">
  $("#micropost_image").bind("change", function() {
    var size_in_megabytes = this.files[0].size/1024/1024;
    if (size_in_megabytes > 5) {
      alert("Maximum file size is 5MB. Please choose a smaller file.");
      $("#micropost_image").val("");
    }
  });
</script>
```

id micropost_imageが変化した際、アラートを出してくれる

最後にacceptパラメータをfile_field入力タグで用いれば、有効なフォーマットでないとアップロードできないことをユーザーに伝えられるようになる

```app/views/shared/_micropost_form.html.erb
<%= form_with(model: @micropost, local: true) do |f| %>
  <%= render 'shared/error_messages', object: f.object %>
  <div class="field">
    <%= f.text_area :content, placeholder: "Compose new micropost..." %>
  </div>
  <%= f.submit "Post", class: "btn btn-primary" %>
  <span class="image">
    <%= f.file_field :image, accept: "image/jpeg,image/gif,image/png" %>←追加
  </span>
<% end %>

<script type="text/javascript">
  $("#micropost_image").bind("change", function() {
    var size_in_megabytes = this.files[0].size/1024/1024;
    if (size_in_megabytes > 5) {
      alert("Maximum file size is 5MB. Please choose a smaller file.");
      $("#micropost_image").val("");
    }
  });
</script>
```

上のコードは最初に有効な画像フォーマットだけを選択可能にしておき、それ以外のファイルタイプを灰色で表示する

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


###演習
5MB以上の画像ファイルを送信しようとした場合、どうなりますか?
→エラーとなる
無効な拡張子のファイルを送信しようとした場合、どうなりますか?
→選択できない


###13.4.3 画像のリサイズ
ファイルサイズに対するバリデーションはOKだが、画像サイズ（縦横の長さ）に対する制限はないので、大きすぎる画像サイズがアップロードされるとレイアウトが壊れてしまう
とはいえ、ユーザーに手元で画像サイズを変更させるのは不便なので、画像を表示させる前にサイズを変更する（リサイズする）ようにいく

画像をリサイズするためには、画像を操作するプログラムが必要
今回はImageMagickというプログラムを使うので、これを開発環境にインストール
クラウドIDEでは、次のコマンドでこのプログラムをインストール

```
$ sudo apt-get -y install imagemagick
```

クラウドIDEや同等のLinux環境を使っていない場合、Macの場合であれば、Homebrewで
brew install imagemagick　を実行すればOK

次は、画像処理のためにいくつかのgemが必要なので、image_processing gemや、Ruby製ImageMagickプロセッサであるmini_magick gemなどを追加する

```Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

gem 'rails',                      '6.0.3'
gem 'image_processing',           '1.9.3'
gem 'mini_magick',                '4.9.5'
gem 'active_storage_validations', '0.8.2'
.
```

```
$ bundle install
```

インストール後にRailsサーバーの再起動


必要なソフトウェアをインストールしたら、Active Storageが提供するvariantメソッドで変換済み画像を作成できるようする
特にresize_to_limitオプションを用いて、以下のように画像の幅や高さが500ピクセルを超えることのないように制約をかける

```
image.variant(resize_to_limit: [500, 500])
```

このコードは別途display_imageメソッドに置いて利便性を高めておく

```app/models/micropost.rb
class Micropost < ApplicationRecord
  belongs_to       :user
  has_one_attached :image
  default_scope -> { order(created_at: :desc) }
  validates :user_id, presence: true
  validates :content, presence: true, length: { maximum: 140 }
  validates :image,   content_type: { in: %w[image/jpeg image/gif image/png],
                                      message: "must be a valid image format" },
                      size:         { less_than: 5.megabytes,
                                      message: "should be less than 5MB" }

  # 表示用のリサイズ済み画像を返す
  def display_image
    image.variant(resize_to_limit: [500, 500])
  end
end
```

これでdisplay_imageをmicropostパーシャルで使えるようになった

```app/views/microposts/_micropost.html.erb
<li id="micropost-<%= micropost.id %>">
  <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
  <span class="user"><%= link_to micropost.user.name, micropost.user %></span>
  <span class="content">
    <%= micropost.content %>
    <%= image_tag micropost.display_image if micropost.image.attached? %>←リサイズして表示
  </span>
  <span class="timestamp">
    Posted <%= time_ago_in_words(micropost.created_at) %> ago.
    <% if current_user?(micropost.user) %>
      <%= link_to "delete", micropost, method: :delete,
                                       data: { confirm: "You sure?" } %>
    <% end %>
  </span>
</li>
```

variantによるリサイズは、このメソッドが最初に呼ばれるときにオンデマンドで実行され、以後は結果をキャッシュされるので効率的


###演習
解像度の高い画像をアップロードし、リサイズされているかどうか確認してみましょう。画像が長方形だった場合、リサイズはうまく行われているでしょうか?
→確認ずみ

###13.4.4 本番環境での画像アップロード

ここはAWSのアカウントを削除したため、スキップする

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

本番環境へデプロイ

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

なお、必要なgemのインストールはこれで終わり
参考までに、最終状態のGemfile

```Gemfile
source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

gem 'rails',                      '6.0.3'
gem 'aws-sdk-s3',                 '1.46.0', require: false
gem 'image_processing',           '1.9.3'
gem 'mini_magick',                '4.9.5'
gem 'active_storage_validations', '0.8.2'
gem 'bcrypt',                     '3.1.13'
gem 'faker',                      '2.1.2'
gem 'will_paginate',              '3.1.8'
gem 'bootstrap-will_paginate',    '1.0.0'
gem 'bootstrap-sass',             '3.4.1'
gem 'puma',                       '4.3.6'
gem 'sass-rails',                 '5.1.0'
gem 'webpacker',                  '4.0.7'
gem 'turbolinks',                 '5.2.0'
gem 'jbuilder',                   '2.9.1'
gem 'bootsnap',                   '1.4.5', require: false

group :development, :test do
  gem 'sqlite3', '1.4.1'
  gem 'byebug',  '11.0.1', platforms: [:mri, :mingw, :x64_mingw]
end

group :development do
  gem 'web-console',           '4.0.1'
  gem 'listen',                '3.1.5'
  gem 'spring',                '2.1.0'
  gem 'spring-watcher-listen', '2.0.1'
end

group :test do
  gem 'capybara',                 '3.28.0'
  gem 'selenium-webdriver',       '3.142.4'
  gem 'webdrivers',               '4.1.2'
  gem 'rails-controller-testing', '1.0.4'
  gem 'minitest',                 '5.11.3'
  gem 'minitest-reporters',       '1.3.8'
  gem 'guard',                    '2.16.2'
  gem 'guard-minitest',           '2.4.6'
end

group :production do
  gem 'pg',         '1.1.4'
end

# Windows ではタイムゾーン情報用の tzinfo-data gem を含める必要があります
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]

```