【Rails】Railsチュートリアルのseeds.rbをリファクタリングする

11/2 RailsチュートリアルやRailsガイドの中の人である安川様より本記事をお褒め頂きました🎉

https://twitter.com/yasulab/status/1587618841269440512?s=20&t=soN_UOEWfdAyR65ALs2hiw

# 目的
Railsチュートリアルにて、`seeds.rb`を用いて任意の数のユーザーとそれに対するマイクロポストの作成をおこないました。
先輩エンジニアに見てもらうと「リファクタリングできるよ〜」とのことだったので、その方法をまとめます。

# 何が問題なのか？
まずは`create`メソッドの使い方をおさらいします。
例として今、`number`というキーをもつ`Number`モデルから、インスタンスを5つ作成することにします。

```ruby: レベル1
Number.create!(number: 1)
Number.create!(number: 2)
Number.create!(number: 3)
Number.create!(number: 4)
Number.create!(number: 5)
```
毎回`Number.create!`を実行するパターンです。
この方法では`create!`メソッドを5回おこなっていることになります。
`create!`メソッドは実行するとSQL文を発行し、DBへアクセスします。
つまりこの方法では5回DBにアクセスし、それだけ負荷が大きいということです。

```ruby: レベル2
Number.create!( [
  {number: 1},
  {number: 2},
  {number: 3},
  {number: 4},
  {number: 5}
] )
```
`create!`メソッドを一回実行するだけで5つのインスタンスを作成する方法です。
同じキーをもつ複数のハッシュは配列化していっぺんに`create`メソッドの引数にとることができます。
配列内は各インスタンスのキーとバリューを指定したハッシュの形をとります。
しかしこれでも、毎回キーを指定する必要がありスマートな書き方ではありません。


```ruby: レベル3
numbers_array = 5.times.map {|n| {
  number: n
} }
Number.create!(numbers_array)
```
そこで、`numbers_array`という変数を用意し、そこに配列を代入します。
配列の指定方法は`map`メソッドと`times`メソッドを組み合わせます。
ブロックの中では`n`という変数を使用することができるので、バリューをこのように指定します。

定義した`numbers_array`を`Number.create!`の引数にすることでインスタンスを生成することができます。


# 手順
それでは、Railsチュートリアルの`seeds.rb`をリファクタリングしていきます。

## ユーザーをまとめて生成する

```ruby:before
99.times do |n|
  name  = Faker::Name.name
  email = "example-#{n+1}@railstutorial.org"
  password = "password"
  User.create!(name:  name,
              email: email,
              password:              password,
              password_confirmation: password,
              activated: true,
              activated_at: Time.zone.now)
end
```
この生成方法では、`User.create!`を99回おこなっていることになります。
つまり、DBへのアクセスが99回おこなわれており、負荷が大きいです。
`create!`メソッドが1回で済むように下のようにリファクタリングしました。

```ruby:after
# 各キーをもつハッシュが99個入った配列をつくる
users_array = 99.times.map {|n| {
  name: Faker::Name.name,
  email: "example-#{n+1}@railstutorial.org",
  password: "password",
  password_confirmation: "password",
  activated: true,
  activated_at: Time.zone.now
} }
# create!の引数にusers_array配列をとる
User.create!(users_array)
```
まず、`users_array`という、99人のユーザー情報が入った配列を作成します。
このとき、`99.times.map`で複数のハッシュをいっぺんに作っていることに注目してください。

その後、`User.create!`の引数に`users_array`配列をとることで`create!`メソッドの実行が一度で済むようにしました。
これによりDBへのアクセスも1回で済み、負荷を小さくすることができます。

## ユーザーの一部を対象にマイクロポストを生成する
```ruby:before
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
```
この生成方法は、6人のユーザーに対して50回ずつ`create!`をしていることになります。
つまり、6×50=300回もDBにアクセスしています。


```ruby:after
# Userから6人取り出し、each文を回す
User.order(:created_at).take(6).each do |user|
  # 50個のcontentを持つハッシュを作成し、contents_array配列に代入
  contents_array = 50.times.map {{
    content: Faker::Lorem.sentence(word_count: 5)
  }}
  # create!の引数にcontents_array配列をとる
  user.microposts.create!(contents_array)
end
```
まず、`User`から6人を取り出します。
`each`文の中では以下の内容が実行されます。

+ `content`キーをもつ50個のハッシュを生成し、`contents_array`配列に代入
+ `each`で定義した6人の`user`に対し、`create!`メソッドを実行
+ `create!`メソッドの引数には`content_array`配列をとる

これにより、`create!`メソッドは6回で済むようになりました。


# 参照

https://pikawaka.com/rails/create#%E8%A4%87%E6%95%B0%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%82%BF%E3%83%B3%E3%82%B9%E3%82%92%E4%BF%9D%E5%AD%98%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95

https://obel.hatenablog.jp/entry/20191215/1576350000

https://docs.ruby-lang.org/ja/latest/method/Array/i/map.html

https://docs.ruby-lang.org/ja/latest/method/Integer/i/times.html