current_user関係のN+1を解決したい

# まえがき
先日からkindleチックなwebアプリを作っていたところcurrent_user関係のN+1問題にぶち当たったので解決方法等をまとめておく

# あらすじ
機能として「ログインなしでもセッションを使ってカートに商品は入れられるけど、ログインするときにセッションが残ってたらマージしてユーザーのカートと統合する」みたいなものです。

要するに、ログインなしの情報をユーザに引き継がせるみたいな。
（電子書籍を扱う想定なのでユーザーは同じ電子書籍を2つ以上変えません）

# コード
以下の想定。application_controller.rbでセッションの有り無しを判別して、
存在する場合はcartモデルの`combine_and_destroy_other_cart!`を呼ぶ。
内容としては
- 過去に注文していないもの
- カートに既に存在してもの

以上を満たすものをユーザーのカートに追加する。

その後セッションを破壊する。
以下詳細
### モデル
```ruby
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable, :confirmable
  has_one :cart, dependent: :destroy
  has_many :orders, dependent: :destroy
  has_many :order_items, dependent: :destroy
  has_many :ordered_books, through: :order_items, source: :book
  validates :name, presence: true
  ...
```
```ruby
class Book < ApplicationRecord
  has_many :cart_items, dependent: :destroy
  has_many :order_items, dependent: :destroy
  ...
```
```ruby
class Cart < ApplicationRecord
  belongs_to :user, optional: true
  has_many :cart_items, dependent: :destroy
  has_many :books, through: :cart_items

  def combine_and_destroy_other_cart!(other_cart)
    transaction do
      other_books = other_cart.books.where.not(id: user.ordered_books)
      self.books = (books + other_books).uniq
      other_cart.destroy!
    end
  end
end
```
```ruby
class CartItem < ApplicationRecord
  belongs_to :cart
  belongs_to :book

  validates :cart_id, uniqueness: { scope: :book_id }
  ...
```
### コントローラ
```ruby:application_controller.rb
class ApplicationController < ActionController::Base
  helper_method :current_cart

  private

  def current_cart
    if user_signed_in?
      current_cart = current_user.cart || current_user.create_cart!
      if session[:cart_id]
        guest_cart = Cart.find_by(id: session[:cart_id])
        if guest_cart.present?
          current_cart.combine_and_destroy_other_cart!(guest_cart)
          session.delete(:cart_id)
        end
      end
    else
      current_cart = Cart.find_by(id: session[:cart_id]) || Cart.create
      session[:cart_id] ||= current_cart.id
    end
    current_cart
  end
end
```
```ruby:carts_controller.rb
class CartsController < ApplicationController
  def show
    @cart_items = current_cart.cart_items.preload(book: :cover_img_attachment).order_by_oldest
  end
end

```
# 起こったこと
bulletをtest環境でも適応したら以下の事象が発生。
```
     Bullet::Notification::UnoptimizedQueryError:
       user: user
       GET /cart
       USE eager loading detected
         Cart => [:user]
         Add to your query: .includes([:user])

       Call stack
         /app/models/cart.rb:9:in `block in combine_and_destroy_other_cart!'
```

user参照してるやんけ！なんでや！とは思いつつ
`combine_and_destroy_other_cart!`でuser.ordered_booksにアクセスしてるのでそん時にuserが事前に呼ばれてないから改めて呼んでるでって怒られてるのかと解釈。
current_cartでuserに関するcartは作ってるしなぁということで色々試してみた。

# やってみたこと１
### current_user.cart.includes(:user)にしてみた
```
     Failure/Error: current_cart = current_user.cart.includes(:user) || current_user.create_cart!
     
     NoMethodError:
       undefined method `includes' for an instance of Cart
```
まぁ怒られるなと思った。
ActiveRecordに対してじゃなくてインスタンスに向けてincludesしてるからまぁ怒られるわなと。

# やってみたこと２
### current_cartのメソッドを修正
```
current_cart = Cart.find_or_create_by(user: current_user)
```
これだと通った。
以下参考だがcurrent_userが悪さしてると思ったんだけどどうなんだろ〜
> https://madogiwa0124.hatenablog.com/entry/2018/07/08/230956

ちょっとしらべよ。