動機
- ロジックの分離、疎結合化を進めたかった。
- 切り出し先ではデータベースアクセスを一切させない!
- すべてのデータを引数渡し!
- ステートレス!
- テストしやすく!
- 独立性を確保!
- そのため、次の流れで進めることにした。
- 呼び出し元ですべてのデータ取得を事前に行う。
- 必要な全データをシリアライズして、切り出し先に引数で渡す。
- 受け取ったデータに対して必要な処理を行う。(データベースアクセスなし)
-
serializable_hash
やas_json
の日本語情報があまりなく、備忘録として本記事を執筆しようと思った。
serializable_hash の使い方
特定の属性だけ、含めたいとき
self
は ActiveRecord
オブジェクトであることが前提。
self.serializable_hash(
only: %w(name gender age)
)
特定の属性を除きたいとき
self.serializable_hash(
except: %w(id created_at updated_at)
)
特定のメソッドの実行結果を serialize 結果に含めたいとき
def price_with_currency
"%5.2f 円" % self.price
end
def serialize_for_discount_logic
self.serializable_hash(
except: %w(id created_at updated_at),
methods: %w(price_with_currency)
)
end
Association 先を含めたいとき
class Order < ActiveRecord::Base
belongs_to :customer
def serialize_with_the_buyer_full
self.serializable_hash(
include: customer
)
end
end
Association 先でもシリアライズする属性を指定したいとき。
class Order < ActiveRecord::Base
belongs_to :customer
def serialize_with_the_buyer_partial
self.serializable_hash(
include: {
customer: {
except: %w(id created_at updated_at)
}
}
)
end
end
属性取得方法をカスタマイズしたいとき
read_attribute_for_serialization
をオーバーライドすると、属性の取得方法をカスタマイズできる。
デフォルトは Object#send
。 attr_accessor
を使っている場合など、通常の利用では、このオーバーライドは不要。
逆に getter を定義しない場合とかに便利。
person.rb
class Person
include ActiveModel::Model
include ActiveModel::Serialization
def initialize(data = {})
@data = data
end
def age= age
@data["age"] = age
end
def read_attribute_for_serialization(key)
@data[key]
end
def attributes
@data.dup
end
end
person = Person.new
person.age = 20
person.serializable_hash()
as_json の話
as_json
メソンドは実は serializable_hash
のうすいラッパーなので、上記で書いているのは、 as_json
でもあてはまります。