20
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【Rails】知ってた?name_wasやname_changed?は非推奨じゃないし、今でもふつうに使えるって!

20
Last updated at Posted at 2025-12-07

TL; DR(長いので最初に結論)

  • name_wasname_changed? のようなメソッドは最新のRailsでも引き続き使える(Rails 5.2で仕様が変わっただけ)
  • ただし、 before_validatebefore_save など、DB保存前のコールバックで使うぶんには問題ないが、after_saveafter_update のようなDB保存後のコールバックでは挙動がややこしいので使わない方がよい
  • 何にせよ、正しく使えているかどうかはプログラマの理解度に依存するので、 name_wasname_changed? よりも、 name_in_databasename_before_last_save のようなRails 5.1から導入された新メソッドを使った方が安心・安全(私見)

はじめに

これは「Ruby/Rails Advent Calendar 2025」8日目の記事です。

昨日は @t_mimura さんの「その404本当に「Not Found」ですか?」という記事でした。

ところで、昔からRailsを使っている人は、Rails 5.1で次のような警告を見たことがあるかもしれません。

DEPRECATION WARNING: The behavior of attribute_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_change_to_attribute? instead.

これは次のようなコードを書いていると発生します(正確には発生していました)。

class User < ApplicationRecord
  after_update :some_callback

  def some_callback
    if name_changed? # ←ここで警告が発生!
      # ...
    end
  end
end

この警告を消すためにはname_changed?saved_change_to_name?に変更します。

 class User < ApplicationRecord
   after_save :some_callback

   def some_callback
-    if name_changed?
+    if saved_change_to_name?
       # ...
     end
   end
 end

このほかにも、変更前の値を取得するname_wasのようなメソッドも警告が発生していたので、name_before_last_saveに変更する必要がありました。

僕の勘違い=Rails 5.2以降で使えなくなるんだと思ってた!!

Rails 5.1がリリースされたのは2017年の4月です。
この警告は僕も当時何度か見かけました。
そのときは以下の記事で紹介されているような変換表を見ながら、既存のコードをsaved_change_to_name?name_before_last_saveに変更したのを覚えています。

after_create/after_update より前の場合

修正前 修正後
attribute_changed? will_save_change_to_attribute?
attribute_change attribute_change_to_be_saved
attribute_was attribute_in_database
changes changes_to_save
changed? has_changes_to_save?
changed changed_attribute_names_to_save
changed_attributes attributes_in_database

after_create/after_update より後の場合

修正前 修正後
attribute_changed? saved_change_to_attribute?
attribute_change saved_change_to_attribute
attribute_was attribute_before_last_save
changes saved_changes
changed? saved_changes?
changed saved_changes.keys
changed_attributes saved_changes.transform_values(&:first)

Rails 5.1 以降で attribute_was, attribute_change, attribute_changed?, changed?, changed 等が DEPRECATION WARNING #Rails - Qiita

こういう修正作業が発生するのは、たいていRails 5.0から5.1にアップデートしている最中です。
そういうときって、「こっちの警告は出なくなったぞ。じゃあ、次!」みたいな感じで、どんどん警告やエラーを潰していくので、そこまで詳しく警告文とかを読んでないんですよね。。

しかも、これまでの経験上、非推奨警告ってその大半が「このメソッドは将来使えなくなりますよー」というものなので、僕はてっきり「非推奨である = name_wasname_changed?はRails 5.2以降で廃止されて使えなくなる」と思っていました。

ですが、違いました!!

name_wasname_changed?はRails 5.2以降でも引き続き使えます。
しかも、Rails 5.2以降だと警告が出なくなります。
つまり、何の問題もなく使えます。

というのをあれから8年以上経った2025年12月に知りました!!
(いやあ、お恥ずかしい……)

警告文をちゃんと読んでみる

あらためて冒頭に紹介した警告文を読んでみましょう。

DEPRECATION WARNING: The behavior of attribute_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_change_to_attribute? instead.

(ChatGPT翻訳)
非推奨警告:
after コールバック内で attribute_changed? を使った場合の挙動は、次の Rails バージョンで変更されます。
新しい挙動では、「save が返った後にこのメソッドを呼び出した場合」と同じ結果(=現在とは逆の結果)を返すようになります。
現在の挙動を維持したい場合は、代わりに saved_change_to_attribute? を使用してください。

よく読むと、「このメソッドは廃止されます」ではなく「挙動が変更されます」と書いてありますね。
どういうことなのか、具体例を出しながら説明していきます。

Rails 5.1と5.2の挙動の違いを確かめる

たとえば以下のようなコードを実行したとします。

user = User.create(name: 'Alice')
user.update(name: 'Bob')

このとき、Userクラスに以下のようなコールバックを設定していると、Rails 5.1と5.2で結果が異なります。

class User < ApplicationRecord
  after_update :some_callback

  def some_callback
    # Rails 5.1なら"Alice" (⚠️警告あり)
    # Rails 5.2なら"Bob" (警告なし)
    puts name_was
    
    # Rails 5.1ならtrue (⚠️警告あり)
    # Rails 5.2ならfalse (警告なし)
    puts name_changed?
  end
end

ポイントは(before_validationbefore_saveではなく)、after_updatename_wasname_changed?を呼びだしている点です。
Rails 5.1と5.2では次のように挙動が変わっています。

  • Rails 5.1
    • 保存前の状態に基づいてname_wasname_changed?の値を返す
    • name_wasname_changed?を使うと警告が出る
  • Rails 5.2
    • 保存後の状態に基づいてname_wasname_changed?の値を返す(つまり、保存直後であれば、何も変わっていないことになる)
    • name_wasname_changed?を使っても警告は出ない

このため、Rails 5.2以降でRails 5.1以前と同じ挙動を維持したいなら、Rais 5.1で新たに導入された name_before_last_savesaved_change_to_name? を使う必要があるわけです。

class User < ApplicationRecord
  after_update :some_callback

  def some_callback
    # Rails 5.1なら"Alice" (⚠️警告あり)
    # Rails 5.2なら"Bob" (警告なし)
    puts name_was

    # 5.1でも5.2でも"Alice"
    puts name_before_last_save
    
    # Rails 5.1ならtrue (⚠️警告あり)
    # Rails 5.2ならfalse (警告なし)
    puts name_changed?

    # 5.1でも5.2でもtrue
    puts saved_change_to_name?
  end
end

押さえておきたいポイント

個人的には以下のようなポイントを押さえておく必要があると思いました。

警告が出るのはRails 5.1だけ

前述の通り、 name_wasname_changed? を使ったときに警告が出るのはRails 5.1だけです。
しかも、これらのメソッドは挙動が変わるだけで、Rails 5.2以降もふつうに使えます。

そのため、「Rails 5.0から(5.1を経由せずに)6.0に一気にアップデート!」みたいな雑なRailsアップデートをやっていたり、5.1を経由してもテストのカバレッジが甘くてコールバックが実行されるテストがなかったりすると、警告を目にする機会がないため、隠れた不具合を生み出している可能性があります。

また、最近rails newしたような歴史の浅いプロジェクトでも、ついうっかり name_wasname_changed?after_update で不適切に使用したりするケースがあるかもしれません。
その場合もやはり、Railsはプログラマの書いたコードを信じるだけ(Railsの仕様通りに動くだけ)なので、警告は出ません(そしてテストが甘いとそのまま不具合が組み込まれる)。

警告が出るのはDBへの保存が完了したあとだけ

ActiveRecordには以下のようなコールバックがあります(参考)。

  • before_validation
  • after_validation
  • before_save
  • around_save
  • before_create
  • around_create
  • after_create
  • after_save
  • after_commit / after_rollback

このうちRails 5.1で警告が出る(=Rails 5.2の仕様変更の影響を受ける)のは、 DBへの保存が完了したあとに、コールバックの内部でname_wasname_changed?のような属性の変化を参照するようなメソッドを呼び出した場合だけ です。

  • 警告なし
    • before_validation
    • after_validation
    • before_save
    • before_create
  • yield後なら警告あり
    • around_save
    • around_create
  • 警告あり
    • after_create
    • after_save
    • after_commit / after_rollback

これは言い換えると、before_validationbefore_saveの内部でname_wasname_changed?のようなメソッドを使っても警告は出ないし、Rails 5.1と5.2で挙動が変わることもない、ということです。

name_in_databasewill_save_change_to_name?への変更は任意?

前述の変換表では「after_create/after_update より前の場合」に使える新メソッドとして、 name_in_databasewill_save_change_to_name? のようなメソッドが紹介されています。
しかし、これらのメソッドは before_validationbefore_savebefore_update の内部で使う上では name_wasname_changed? と同じ挙動になります(たぶん)。

class User < ApplicationRecord
  # 保存前に実行されるコールバックを定義
  before_update :some_callback

  def some_callback
    # 5.1でも5.2でも"Alice" (警告なし)
    puts name_was

    # 5.1でも5.2でも"Alice"
    puts name_in_database
    
    # 5.1でも5.2でもtrue (警告なし)
    puts name_changed?

    # 5.1でも5.2でもtrue
    puts will_save_change_to_name?
  end
end

ということは、Rails 5.1以降でも無理に name_in_databasewill_save_change_to_name? へ変更せず、 name_wasname_changed? を使い続けるという選択肢もありかもしれません。

ただし、Rails 5.1のコードをデバッガで追いかけると name_changed?will_save_change_to_name? はそれぞれ異なるコードで実装されていたので、もしかすると微妙な挙動の違いがあるかもしれません(詳しい人がいたら教えてください🙏)。

name_wasname_changed?を今後も使い続けるべきか否か?

最初の話に戻ってしまいますが、name_wasname_changed?はRails 5.1かつ特定の条件下で警告が出るだけで、Rails 5.2以降でも引き続き使えます。

ゆえに、Rails 5.2以降のメソッド仕様をきっちり押さえて いれば (強調)、「確信をもってname_wasname_changed?を使い続ける」という選択肢を採ることも可能です。

ただ、個人的な気持ちとしては「警告を出してそれに変わるメソッドをいろいろ用意したのであれば、Rails 5.2で name_wasname_changed? を廃止してしまえば良かったのに」と思います。

というのも、name_wasname_changed? を使っているコードを見つけたときに、「これははたして適切な使い方なのかどうか」を一瞬立ち止まって考えないといけないからです。
また、このややこしい仕様をちゃんと理解していない人だったら、うっかり不具合が起きる使い方をしてしまうかもしれません。

なので、僕としてはいろいろ混乱の原因になるので、コールバックの種類によらず、 name_wasname_changed? は一律使わない方がいい(=Rails 5.1から導入された name_in_databasename_before_last_save といった新メソッドを適材適所で使い分けた方がいい)と考えます。

ただ、これは精神論やプログラマの努力で強制するのはなかなか大変です。
こういうものは、RuboCopみたいなツールで機械的に name_wasname_changed? の使用を検知できたらベストなんですが、そういうRuboCopのルールとかはあったりするんですかねえ?(知ってる人がいたら教えてください :pray:

まとめ

というわけで、この記事ではRailsの name_wasname_changed? は非推奨じゃないし、今でもふつうに使えるんですよ、という話を書いてみました。

「警告をちゃんと読まなかったお前が悪い」とか「私はちゃんと理解してましたけど?」と言われそうですが、中には僕と同じように「えっ、そうだったの!?」と驚いている人もいるかもしれません。

もし初耳だったら、一度自分のプロジェクトで name_wasname_changed? みたいなメソッドを適切に使っているかどうかをチェックしてみてください!

参考文献

20
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?