0
0

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コールバックのchanged?って?

Last updated at Posted at 2023-11-19

きっかけ

「order_idが変わったらorder_statusの値を変更する」という実装でコールバックを使用した際に、
コールバックの挙動を理解できておらず躓いてしまったのでアウトプットとしてまとめてみました。

ケース1

コールバック発火しない

order.rb
  enum status: {
    unchecked: 0,
    checked:   1,
    error:     3
  }

  after_update :order_status_reset, if: -> { order_id_changed? }

  def order_status_reset
    self.unchecked!
  end

原因

after_updateではorder_id_changed?に該当しない。

after_saveも同様だが、changed?は
現在の値と変更しようとしている値を検証するもの。

そのため、updateやsaveした後は
すでに変更しようとした値は保存されており、
changed?は発火しない。

ケース2

afrter_updateでは発火しないのでbefore_updateに変更したところ
updateが無限に呼ばれ以下のエラーが発生

SystemStackError: stack level too deep
order.rb
  enum status: {
    unchecked: 0,
    checked:   1,
    error:     3
  }

  before_update :order_status_reset, if: -> { order_id_changed? }

  def order_status_reset
    self.unchecked!
  end

原因

before_updateでorder_status_resetメソッドが実行されるものの、
メソッド内でupdateが行われており、再びbefore_updateが発火(以降繰り返し)。

解決策

コールバックで呼び出したメソッドの中ではupdateやsaveは行わず代入で対応。

order.rb
  enum status: {
    unchecked: 0,
    checked:   1,
    error:     3
  }

  before_update :order_status_reset, if: -> { order_id_changed? }

  def order_status_reset
    self.order_status = :unchecked
  end

まとめ

・before_update系の中でupdateしてはいけない
・after_update系でchanged?は効かない

コールバックの中でも限られた部分ですが、参考になれば幸いです。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?