ActiveRecordのDirtyメソッドには非推奨なメソッドがある?

Ruby on Rails

Ruby on Rails で、例えばモデルの属性の変更前の値が知りたい時、ActiveRecord::AttributeMethods::Dirty のメソッドを使い、以下のように取得できたりします。

person = Person.create(name: "Allison")
person.name = 'Alice'
person.name_in_database # => "Allison"

他にも便利なメソッドが定義されている Dirty モジュールですが、色々定義されていてよく忘れるので、適切なメソッドは何か調べたりすることが多いです。

そんな時、たまに「非推奨になったメソッドがある」みたいな記事が目に引っかかり、そこで記載されているものは使わないでおこうと避けてきました。
そこで今使えるメソッドを一覧化しておきたいと思い、この記事でまとめておこうと思います。

結論

と思い調べたのですが、結論現時点(Rails8.0.1まで)で非推奨になっていたり、使えなくなったようなメソッドはないです。
詳細は後で記述しますが、Rails5.2で一部のメソッドの挙動が変わりはしたのですが、メソッド自体は後方互換性のためにこれまで通り使用できます。
よって、5.2以降のバージョンを使っている場合は、公式ドキュメント等でメソッドの挙動を理解して使えば問題ありません。

非推奨云々の話って何?

これは特定のメソッドにおいて、元々 after コールバック内で実行させた時に不具合が起きがちであり、動作も現在とは異なっていたことが起因しています。

例: changed? メソッド

changed? メソッドを例に出すと、after 内とそれ以後というタイミングによって、以下のように異なる bool 値を返します。

Model.after_save { puts changed? }
model.save # true が出力
puts model.changed? # false が出力

仕様を正しく理解できていないと、直感的には同じ値を返す?と誤解してしまいますね。

そこでRails5.1でこの挙動を変更するために、使用時に以下のような警告が表示されるようになり、

[2] pry(#<User>)> changed?
DEPRECATION WARNING: The behavior of `changed?` inside of after callbacks will be changing in the next version of Rails. The new return value will reflect the behavior of calling the method after `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use `saved_changes?` instead.

Rails5.2で実際の挙動が以下の通り変更されました。

Model.after_save { puts changed? }
model.save # **false** が出力
puts model.changed? # false が出力

つまり changed? 自身の挙動としては、DBからの取得結果から変更された属性があるかという挙動に変更となりました。

またこれに伴い、has_changes_to_save?saved_changes? が導入されました。

has_changes_to_save?changed? のエイリアスであり、挙動は同じです。
changed? は後方互換性のために使えるままですが、よりメソッド名で詳細に意味を語るという意味では has_changes_to_save? を使った方がよいかと思います。

saved_changes? は、DBから取得した後に更新されたかを取得できるメソッドとなります。
つまり、以下のケースではいずれも更新後に実行されるため、trueが出力されます。

Model.after_save { puts saved_changes? }
model.save # true が出力
puts model.saved_changes? # true が出力

その他のメソッド

上記の changed? のような改善が、以下のメソッドでも加えられました。

元のメソッド保存前使用時のメソッド保存後使用時のメソッド
attribute_changed?will_save_change_to_attribute?saved_change_to_attribute?
attribute_changeattribute_change_to_be_savedsaved_change_to_attribute
attribute_wasattribute_in_databaseattribute_before_last_save
changed?has_changes_to_save?saved_changes?
changeschanges_to_savesaved_changes
changedchanged_attribute_names_to_savesaved_changes.keys
changed_attributesattributes_in_databasesaved_changes.transform_values(&:first)

挙動としてはいずれも元のメソッドは「保存前使用時のメソッド」のエイリアスとなります。

なお、Rails5.2で挙動が変更となってからは、5.1で出力されるようになった非推奨の警告は表示されなくなってます。
ただメソッド名から意味を汲み取りやすいという点で、「保存前使用時のメソッド」を使った方が好ましいかと思いました。

参考

Deprecate the behavior of AR::Dirty inside of after_(create|update|save) callbacks by sgrif · Pull Request #25337 · rails/rails
Cleaning Up: ActiveRecord::Dirty 5.2 API Changes – FastRuby.io | Rails Upgrade Service

タイトルとURLをコピーしました