この記事は ZOZOテクノロジーズその2 Advent Calendar 2018 - Qiita 13日目の記事です。
今回はActiveModel::Serializers(AMS)の便利メソッドを紹介します。 github.com
AMSを使ってjsonシリアライズ実装をしていたのですが、以下のような非ActiveRecordオブジェクトのシリアライズで
動的にSerializerを切り替えたい場面で困った事がありました。
困った事例
ワールドトリガーを例にサンプルコードを書きます。
人物とトリガーを表すクラスが存在し、人物オブジェクトをAMSをつかってシリアライズする実装を考えてみます。
クラス定義
# 人物は1つだけトリガーを持つことができるとする class Person < ActiveModelSerializers::Model attributes :name, :trigger end # ノーマルトリガーはオプション装備をひとつ文字列で指定可能 class NormalTrigger < ActiveModelSerializers::Model attributes :name, :option end # 黒トリガーは製作者の名前を持つ class BlackTrigger < ActiveModelSerializers::Model attributes :maker end
実際にオブジェクトを作成
normal_trigger = NormalTrigger.new(name: 'レイガスト', option: 'スラスター') osamu = Person.new(name: '三雲 修', trigger: normal_trigger) black_trigger = BlackTrigger.new(maker: '空閑 有吾') yuma = Person.new(name: '空閑 遊真', trigger: black_trigger)
各トリガーに対応するシリアライザを作成
class NormalTriggerSerializer < ActiveModel::Serializer attributes :name, :option end class BlackTriggerSerializer < ActiveModel::Serializer attributes :maker end
最後にPersonに対応するSerializerを作成
triggerオブジェクトに2種類のクラスのオブジェクトが入る可能性があり、動的にSerializerを切り替えてやる必要があって困りました。
class PersonSerializer < ActiveModel::Serializer attribute :name, :trigger def trigger # ノーマルトリガー、黒トリガー2種類のクラスのオブジェクトを扱うため、動的にSerializerを切り替えたい NormalTriggerSerializer.new(object.trigger) # ノーマルトリガーの場合 BlackTriggerSerializer.new(object.trigger) # 黒トリガーの場合 end end
解決法
上記のようにシリアライザの中で複数のSerializerを使い分ける場合、どのSerializerを呼べばいいかわからない問題に直面しますが
ActiveModelSerializers::SerializableResource#serializer_instance
を使用することで1行で回避可能です。
class PersonSerializer < ActiveModel::Serializer attributes :name, :trigger def trigger ActiveModelSerializers::SerializableResource.new(object.trigger).serializer_instance end end
この他ActiveModelSerializers::SerializableResource#serializer
メソッドは与えられたリソースからSerializerクラスを返します。
これらのメソッドを利用することで、リソースから動的に対応するSerializerクラスやそのインスタンスを取得可能です。