関連する ActiveModel::Serializer を切り替える

## はじめに

- Associations において利用する Serializer を切り替えました
- AMS で該当する機能は見つからなかったので素朴に実現しました

## 目次

- はじめに
- TL;DR
- 環境
- 構成
  - Model
  - API
- やりたいこと
- 実現方法
  - Controller
  - Serializer
- まとめ
- 参考

## TL;DR

- Rendering の際に `options` を渡す

```ruby:reservations_controller.rb
  render json: reservation,
          include: '**', restaurant_type: :detail
```

- Serializer の initialize で `options` を受け取る

```ruby:reservation_serializer.rb
  def initialize(object, options = {})
    super
    @restaurant_type = options[:restaurant_type]
```

- `options` の値によって 関連先で利用する Serializer を切り替える

```ruby:reservation_serializer.rb
  attributes :id, :restaurant

  def restaurant
    serializer = case @restaurant_type
                  when :base then RestaurantSerializer
                  when :detail then RestaurantDetailSerializer
                  else RestaurantSerializer
                  end

    serializer.new(object.restaurant, @options)
```

## 環境

```markdown
- ruby: 2.6.2
- rails: 5.2.3
- active_model_serializers: 0.10.9 
```

## 構成

### Model

```ruby:app/models/restaurant.rb
# == Schema Information
#
# Table name: restaurants
#
#  id                   :bigint(8)        not null, primary key
#  name                 :string           not null
#  phone                :string           not null
#  address              :string           not null

class Restaurant < ApplicationRecord
  has_many :reservations
```

```ruby:app/models/reservation.rb
# == Schema Information
#
# Table name: reservations
#
#  id                   :bigint(8)        not null, primary key
#  restaurant_id        :bigint(8)        not null

class Reservation < ApplicationRecord
  belongs_to :restaurant
```

### API

```json:reservations_list.json
{
  "reservations": [
    {
      "id": 42,
      "restaurant": {
        "id": 42,
        "name": "Domino's Pizza"
      }
    }
  ]
}
```

```json:reservation_detail.json
{
  "reservation": {
    "id": 42,
    "restaurant": {
      "id": 42,
      "name": "Domino's Pizza",
      "phone": "03-1234-5678",
      "address": "東京都XXXXXX"
    }
  }
}
```

## やりたいこと

- Reservations List API では restaurant の `id, name` のみを含める
- Reservation Detail API では restaurant の `id, name, phone, address` を含める

## 実現方法

### Controller

- `restaurant_type` を Serializers に渡す

```ruby:reservations_controller.rb
module API
  class ReservationsController < API::ApplicationController
    def index
      reservations = Reservation.all
      render json: reservations,
              include: '**', restaurant_type: :base
    end

    def show
      reservation = Reservation.find(params[:id])
      render json: reservation,
              include: '**', restaurant_type: :detail
    end
  end
end
```

### Serializers

- initialize で `options[:restaurant_type]` を受け取る
- `options[:restaurant_type]` の値によって, Serializer を切り替える
- `@options` を 渡すことで, options や scope を 引き継ぐ

```ruby:reservation_serializer.rb
class ReservationSerializer < ActiveModel::Serializer
  attributes :id, :restaurant

  def initialize(object, options = {})
    super
    @options = options
    @restaurant_type = options[:restaurant_type]
  end

  def restaurant
    serializer = case @restaurant_type
                  when :base then RestaurantSerializer
                  when :detail then RestaurantDetailSerializer
                  else RestaurantSerializer
                  end

    serializer.new(object.restaurant, @options)
  end
end
```

```ruby:restaurant_serializer.rb
class RestaurantSerializer < ActiveModel::Serializer
  attributes :id, :name
end
```

```ruby:restaurant_detail_serializer.rb
class RestaurantDetailSerializer < RestaurantSerializer
  attributes :address, :phone
end
```

## まとめ

- 今回は牧歌的な方法で切り替えました
- 良い方法があれば教えてください :bow: 

## 参考

- [Specify association serializer with conditional? · Issue \#825 · rails\-api/active\_model\_serializers](https://github.com/rails-api/active_model_serializers/issues/825)
- [Passing serializer options for associations · Issue \#331 · rails\-api/active\_model\_serializers](https://github.com/rails-api/active_model_serializers/issues/331)