そもそもで、性能を気にする場合にserializeを使うのか否かといった議論とか、そもそもserializeって何っていう話とかはQiitaにたくさん記事があるのでそちらも併せてごらんください。
ここでは、とにかくserializeしますってなった後の話をしたいと思います。
以下、サンプルです。税区分をデータベースでは整数で保持するけど、モデル上は文字列で取り扱う場合です。
module TaxType
TAX_TYPE = [[1, '税別'], [2, '税込'], [3, '非課税'], [4, '不課税']]
I2S = TAX_TYPE.to_h
S2I = TAX_TYPE.map(&:reverse).to_h
def self.dump(value)
S2I[value]
end
def self.load(value)
I2S[value]
end
end
class Slip
...
serialize :tax_type, TaxType
...
end
ここで、もともと税区分が'税別'だったときに'税別'を設定する場合、変更なしとして扱いたい。
changed? が false という評価になってほしいわけです。
slip.tax_type = '税別'
ですが実際は、changed? はtrueになってしまいます。
変更されたか否かにかかわらず、常にtrueです。中身を見ずにtrueを設定しています。
この設計といいますか、仕様の意図は、巨大なJSONオブジェクトが変更されたかどうかを計算しようとするととても時間がかかるので、変更されたかどうかを計算をしないと決める必要があった。
それであれば、保存しておくに越したことはない。
というところではないでしょうか。
今回は単に文字列ですから、比較のコストは大したことはありません。
ついでと言っては何ですが、そのようなカラムはほかにもあるかもしれませんので、
before_save do
changes_to_save.each do |attribute, value|
was, is = value
if was == is
clear_attribute_change attribute
end
end
true
end
という感じで、保存の前に変更されているとマークされているもの全体に対して、実際はどうなんだ?ということを一つ一つチェックしていきます。
もしバリデーション中に変更されているかどうかの結果を使用したい場合は、この定義を before_save ではなく before_validation で行います。
保存のときだけなら、別にいつでも良いです。
せっかくなので、concernsに置けるようにしてみます。
module ClearChangesIfEqual
extend ActiveSupport::Concern
included do
before_save do
changes_to_save.each do |attribute, value|
was, is = value
if was == is
clear_attribute_change attribute
end
end
true
end
end
end
必要に応じてmodelにincludeして使います。
コード自体は大したことがないもので、べたに比較し続けるかんじです。
ですが、挙動のイメージがわかないと、どこで処理をするのが良いのか判断に迷いますね。
では、今日もここまで読んでくれてありがとう。