ActiveStorage と variant と それから N+1

## 概要と対策手順
1. `Active Storage`を使ってポートフォリオを作成
1. 作成後のレビューで`Active Storage`で`N+1`を確認する
1. 原因は`Active Storage`の`variant`だった
1. 現環境では対処できないのと発覚。
1. `variant`をやめて`CSS`での制御に変更し解決。

※ポートフォリオの詳細に関しては[こちら](https://qiita.com/ryo1103/items/973d05c52b2adb3e84b2)に投稿してあります。

## 実行環境
- Ruby ( 2.7.2 )
- Ruby on Rails ( 6.1.4.1 )
- PostgreSQL

## Active Storage と N+1
- 対象になったのはこちら(記事の投稿者を示すユーザーアイコン)
![スクリーンショット 2021-09-16 10.56.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1748419/76965a2f-84ae-f4b2-1fe5-000aa35296c1.png)
- 当時のコントローラーの処理は下記

```.rb
@articles = Article.published.includes(:tag_maps, :tags, :keeps,
                                       user: { avatar_attachment: :blob }).order(created_at: :desc).page(params[:page]).per(PER_PAGE)
```
- `avatar`取得時の`N+1`には下記を参考に対応しているつもりだった。

参考： [Active StorageのN+1問題に対処する](https://shuttodev.hatenablog.com/entry/2019/09/10/012916)


## 原因は variant だった
- ローカルに接続した時のログを眺めていると `ActiveStorage::VariantRecord Load` が記事の表示件数分弱表示されていた。
- アイコンの画像を場所ごとにサイズを分けて表示したかったので`application_helper.rb`に下記の処理をしていた。

```application_helper.rb
  def user_icon(user, size)
    case size
    when "mypage"
      user.avatar.variant(resize: "72x72").processed
    when "profile"
      user.avatar.variant(resize: "64x64").processed
    when "list"
      user.avatar.variant(resize: "25x25").processed
    end
  end
```
このバリアント画像を`N+1`しているらしい。

## 現環境では対処できない
- 自分で調べてみたが座礁したので、相談をし調査継続
- [週刊Railsウォッチ](https://techracho.bpsinc.jp/hachi8833/2021_06_21/108935)さんと[Ruby on Rails API](https://edgeapi.rubyonrails.org/classes/ActiveStorage/Attachment.html)に辿り着き、下記を見て対処方法と`variant`が原因の`N+1`だと気づく。

> 現在のActive Storageではvariantトラッキングで添付ファイルごとにvariantが存在するかどうかをチェックするクエリが走る。通常のRailsのN+1防止策（includes）ではこれを防止できない。
このプルリクはwith_all_variant_recordsメソッドを追加し、かつincludesがActive Storageの添付ファイルで期待どおり動作するようになる。

- 解決の糸口は見えてきたので、try&error で挑戦するも一向に解決できず。
    - `scope :with_all_variant_records, -> { includes(blob: :variant_records) }`という記述をみた際に先ほど参考にした[Active StorageのN+1問題に対処する](https://shuttodev.hatenablog.com/entry/2019/09/10/012916)内で出てきた`scope :"with_attached_#{name}", -> { includes("#{name}_attachment": :blob) }`を参考にしたがずっと`undefind methods with_all_variant_records`と表示される(今思うと感の良い人なら気付きそうなログですね)
- どうやら自分の使用している`Rails`のverでは`with_all_variant_records`が実装されていないという事を教えてもらいました。(githubの英語訳を見てた時に`Rails7`に対応する的な事が書いてあったのはこの事かと後で合点がいく)
- 確かに `github` の [main](https://github.com/rails/rails/blob/main/activestorage/app/models/active_storage/attachment.rb) には` scope :with_all_variant_records, -> { includes(blob: :variant_records) }`が記載され
- [6.1.4.1](https://github.com/rails/rails/blob/v6.1.4.1/activestorage/app/models/active_storage/attachment.rb)の方には記載がなかった。(通りで`undefind methods`な訳ですね)

## CSS制御に変更し
- 件の根本的な原因と現状は分かりましたが、現状の改善はできていません。
- 今回は`variant`をやめて`css`で調整する方向に転換しました。

### 変更手順
- `application_helper.rb`で使用している`variant(resize: "サイズ").processed`の記述を削除
- `CSS`で画像調整する記述を用意

### CSSでの画像調整
- 今回は`SCSS`を利用
- `html.erb`内で`variant`を使用していた部分のクラス名を変更
- 同じ記述が増えるので`@mixin`と`@include`を使用する

```.scss
@mixin icon_size($size: 25px) {
  border-radius: 50%;
  width: $size;
  height: $size;
}

/* アイコンサイズ */
.list-user-icon {
  @include icon_size;
}
.profile-user-icon {
  @include icon_size(64px);
}
.mypage-user-icon {
  @include icon_size(72px);
}
```

スッキリ！！




## 解決
これでログを見ても`ActiveStorage::VariantRecord Load` が減りました。
無事解決！！
今回の件で解決方法を正規ルートに拘りすぎず、違う形でも解決できる方法も一緒に考えることができるようになる力も必要だなと感じました。